To push, or not to push?! - The future of HTTP/2 server push - Patrick Hamann
Assets And you weren't allowed to send any more assets down to the browser what, would they be for. Your website that you're building today can. You do that for exercise, do. We think that it's the logo, is it. The font file I mean think about why, did someone come to read there, to homepage they, probably came to read the news what is that core, user experience, what is that the core thing that the user came here to do today optimize. The delivery of those assets, and nothing, else so, for the FT it's probably the branding, you need to tell them who you are so the logo the, hero image which. Is about, that main article, that I'm interested, in reading and the fonts to be able to read it and then. We also need to think about interactivity, as well there's, no running good to just. Sending. Down all of those critical assets and then I try and click on your hamburger icon and I can't actually do anything, and. So we, talked a lot about when. We're talking about resource, loading about. The critical, path and, critical. Request is what I've just said I've just made you think about what are your critical requests so my, friend an industry, colleague Ben Schwartz of calibre, describes, a critical quest as one that contains an asset that is essential. To, the content, within the users viewport, and I would extend that and say that is essential, to the users core experience, of the thing that they came to your website to do it's highly likely the user only came to perform one action, so, you need to think about that in terms of resources what, are the resources that that user needs, in their, browser to, be able to do the thing that they came here for is it, that, lazy, loaded, content, at the bottom of the screen on this that. Is it the adverts, is it the comment section below the fold unfortunately, the fold does, exist, it is a real thing and so you need to optimize for that core user experience, at the beginning so. The question then is what are your critical resources I've, mentioned, fonts before it's, probably the critical, CSS required, just to render that above the VOAD viewport.
Loads Of preload elements, to preload the images so that when the carousel does open they've actually already loaded. So, I think it's really really powerful primitives, here and this is how simple it is so. What, let's look at what the impact that actually has that, com, home page again this is the network waterfall if we're to load it normally so, notes how low down in the waterfall the font files are without, preload, and this is because it's a hidden sub resource it's but could they're not initiated, until the render tree is constructed, and that's. Terrible right the user needed, to read so that means the user can't read anything from the screen until after, these network requests. To finish so just by applying some preload, headers to tell the browser to, download the fonts upfront, this. Is the impact that it can have on your network waterfall we've prioritized the delivery. Of our critical resources and therefore, we're going to actually dramatically. Decrease. The time to text paint for the ectoderm homepage. Fussy. Customer Shopify, switched, to pre loading fonts and they saw a 50%, improvement. In, their time to text paint there's, a whole, 1.2. Seconds. On their. Free ji loading. Experience, right I've been working in the performance industry for a very long time now and I've never had, a single technique, that can shave 1.2. Seconds, on your render experience, just, by adding one, HTTP, header, to my experience so, it's that easy. But. The question I want to pose is, are, indicating. Our resource hints like this to the browser via. The HTML, response, ie we have to wait for that HTML. Response to be generated, by the server in fact, actually too late, in the connection lifecycle, and, this. Is what HB - server push was, designed to solve so, let's have, a look at that now, this. Is the traditional, request, flow that the. Browser and the server makes, to, load a web page first we make, a get request for that home page you may have a CDN, or a proxy a load balance or a cache in the middle that goes to your application, data we then have to our application, has to do some thinking it has to might have to go and make some database, requests, do some templating, merge lots of stuff together until it renders, your HTML, and returns, that 200, response the, browser gets that it parses, constricts, the the, render to the kind of CSS object model it has to then go and make the request for the CSS file but.
Response. Data down the wire we can also send, other data such as a push promise, so. The question is you now you're probably wondering how, you can push and this. Is exactly how we've, in the industry we've converged, upon using our friend the link preload, header as a way of you indicating, from your application to your hb2, enabled server that, I want to push this resource now there's a very big debate including. Myself of whether or not this is the good semantics to use for this but, that's another discussion for another day you can come and talk to me in the break and the. Caveat here is you have to have a h2 enabled server and your h2 enabled server also needs to have push enabled. But the majority of the main proxy. Service that you probably use such as Apache nginx. Is, AWS. GCP Heroku, that all of them now supports, h2, and. You don't want to push and you only want the preload semantics then you can put this and no push. Attribute. At the end of them so. Let's take a look at the typical water before using server push and then after using server fish note. That, in the top before we use server push we have that idle time once the browser has made the request for the CSS file and we're waiting for that term to 5 bytes for the server to respond if, I pushed the, CSS file the, browser no longer has that idle, waiting time because, it never needs to make the request because the server said I'm going to push you this resource here, it is and it starts flushing the bytes down the wire so, we have a whole one round-trip saving, and depending, on how, long the distance between your client and the server that round-trip, could actually be a very very big. Saving so this is great if our, RTT time has. High latency especially, in developing countries where we have highly latent connections, on mobile. But. Note that we still got this idle time right in the beginning of the. Exit of the TCP connection whilst we're waiting for, the server to respond, of its HTML and, this, makes me really sad. Let's. Have a look why right, I thought hey push, was meant to solve that problem but. Because push. Is indicated. Via that link header on the response, of our. Index dot HTML we, had to still wait for the server Fink time before. The server could push the resource right, and, that's, that's a bad thing because, we've got this idle, time on the connection. So. To recap server push gives us the benefits of a one round-trip time saving because, we don't have to the browser no longer has to request that resource it's, useful, if we've got long server sync times and long Artie T's but. Again that question of is indicating. Links. Preloads. And, pushes via the response of your HTML in fact too late. So. How. Can we achieve that holy grail that pushed actually, set out to achieve, and told us that we actually deliver, our critical resources using, that ID or TCP connection time and to do this we need to be actually decouple our push logic from, our HTML, HTTP, responses, and this is what a farsi would call a sync push, a more. Common architecture as you've seen in my diagram so far is that you've probably got a load balancer or, a CDN, or a proxy of some kind that sits in front of your application, server like, nginx, or Apache and so why if we had programmatic, access to the underlying network connection we, can dispatch, that push as, soon as the request is received and, then send the rest or let the application, do its thing and do it server think time and then, we'll be utilizing that. Idle connection time and that makes me really happy so. Let's have a look at that this is just using node standard, hb2, standard. Library but you could do this of you any kind of middleware Express. It's happy etc, you, have a request coming, in and if you have programmatic access to the underlying connection here, I'm creating a stream.
For The critical CSS file and I'm pushing that down the wire down my connection, way, before I dispatch, my database. Calls done my user lookups, did some templating, etc. And this is how you can do async push and not relying on link preload headers and. Your hate speech server to do those things now. If we did async push now, we've got a push example, at the top where, yes we had the one Transit's time-saving but with async we've managed to achieve that, holy grail of pushing, during the idle time right at the beginning of the connection now the browser has all of the information it needs before. It's even started to, construct the document object model and so we can have really, fast instant painting. And. That makes me really really happy. So. Whilst. Push is really useful enough on the first view like this I'm. Hoping some of you started to question okay well what happens on the repeat view if I had that same logic am. I just going to push again and what. If the the client has already got that asset cached right hopefully, the critical resources that we identified, at the beginning of the talk your, secrets, ago CSS your fonts and your application, data they, should be highly, cacheable resources, you should have long, max-age, headers on them or you're using immutable, URLs of hashes. In them and but, what, would happen with push we're. Actually gonna push it again and. The problem here is that the server has no knowledge of what, the client has brought in its cache so, all we're going to do is actually push, that resource every time the, user requests, our HTML, especially, if we're using the, preload link headers to push we're, just going to keep on doing this and the fact what ends up happening is we're going to over push and this is much more detrimental than pushing at all because, you're actually going to create contention, on the network and, slow, down the, building, of the DOM and the rendering to the screen and both. Myself. At far-seeing our colleagues and our colleagues at Google have been noticing that over, pushing is actually become more of a detriment than pushing, at all has. But. If you want to overcome that problem one, solution, is to use the purple pattern, if you're in more interested in especially you've got a progressive. Web app and, you're using a serviceworker the, pattern is that you push beyond, the initial load you actually push all of the resources required, for. The entire application you. Then they get filled and populated, in the serviceworker cache and, then on the repeat view everything. It's coming from the serviceworker cache and will never hit the network so you'll never have the problem, of over pushing if you're interested in that I. Urge. You to check out Adi and Eva's talk from Google i/o this year and they detail the pattern a lot more so. The, servers got no knowledge of the client cache tape and this. Is the problem here with push but. In all of this theory is right and everything I've told you it still sounds like an awesome technique, and I can dramatically, improve. My. My. Render time if I pushed my critical resources, but. Adoptions it has been extremely low and it has to be can look quite tricky to implement and so what, is the problem here, in. Fact first I'd like to ask you a question how many people are using hb2 in production. Okay. Not that many and how many people keep your hands up if you're using push. Right. Wow that's like no one okay. So. My, point thankfully. Stands. Here is that what is the problem why aren't we using it if it's such a great technique so. Let's, have a look at some of the problems after, we sent that push promise and we flushed the bytes down the wire I actually lied to you early on the, client does have a way of resetting. That in the repeat view of saying no I've got that in my cache and don't, send it to me and this is in the form of a reset stream frame that the client can send to the server saying reset, the stream for that HTC. Ttp response, I don't, need it I've got in my cash but the problem here is that by, the time the browser sent to that it's probably far too late because the server is already flushed all of the bikes especially of your critical resources are small things like a critical, CSS file, it's, far too late or it's flushed them into the TCP kernel on the server and then that's in kernel space and not user space and there's no way of telling, the kernel to do that and um, and, and by, that and they're probably all on the network link anyway, and so actually it's far too late when the server receives this, the. New quic protocol, this, is, going to solve this the, superseded. To TCP because, they're moving a lot of this. Type of work into user space and out of kernel space but it's going to be years until, I come back to the stage to talk to you about quick, and.
The Ver common areas how the, the, browser actually push it caches, the pushed, resource, that you pushed to it when. A network request leaves, the page it, actually goes on this little dance through. All of the browsers caches before it actually goes to the network first, it looks at the memory cache so, that's all the resources that have been loaded for this session of that host name and for. That lifecycle, of this page then. It goes into a serviceworker, cache checks, if the resource is there then, it goes to finally into the HTTP, cache the one that we all know about with our cache control semantics, and it is a resource passed and finally. The last cache it will look in is the push cache so, even if you've pushed that resource but actually it already existed, in one of the first three what, you've done is you've over pushed again and actually you've sent far too many bytes down the wire and you've created contention, on your network the, worst thing is well is that push caches are separate, for each at. H2. Connection, that you have to the same host name so, if you have a resource that's credentialed, like a so. Non credentialed, like a font file that's, actually gonna be on a separate request then, your credential, main host. Connection. For your main host name so you'll never actually claim it at all. So. I've detailed some of these issues that surrounding, here that the connection has to be Authority for the resource so you again, that's the connector at the credential, problem it only lives for the lifetime, of the connection so if you push it for. That connection that gets closed and then a user opens up a new tab to your same web site it's, not the, than what the resource that you push initially can't be claimed they, can only be claimed once so, if you have two tabs open, to, the same website. You push a resource tab, to can't, also. Claim the resource, that was pushed in tab 1 which is extremely, inefficient so, you have to send it again and it's, that last cache so, it's highly likely that it may be in one of the other caches before, you pushed but, the worst thing I think is that it's not Specht at all and so that, leads to browser inconsistencies. If you want to push and there, many people that have done some extensive, research into history push and it's inconsistency, so I urge you to check our jaycara, Blues blog post on that, but. The problem is that it really results. In is that we can only really use push reliably, in Chrome, and Firefox Edge has got the one connection, per tab it does it doesn't support um the, fetch API to, use, the. Push semantics, and Safari is completely, non-deterministic sometimes. You'll, get the pushed items sometimes you won't so, this kind of leaves us to resorting, to having to use user, agent sniffing on the server to, determine whether or not we actually want to push a resource at all and, hopefully like, me you know where that's going to end up if you know it's starting to do you a sniffing, and it will everything you've just created a whole nother problem for yourself. But. Lastly the rate of adoption of push is extremely, low so we've, been doing some research or fasting we observed that only eight hundred out of a million requests that we see are push initiated. And, if it was meant to be this amazing, technique, that solves, all of our performance, problems surely, more people would be using this and just. To give you some comparison, we do, around six million to seven million requests a second and so, that, number is extremely, extremely small, to be us observing, that on our network, the. Rate of adoption is so low that it's even leading to many implementers. To completely. Potentially. Abandoned, the idea so this is research from Akamai and Google with the latest ITF, meeting two, months ago Google. Have run an experiment where they've actually disabled. Push in Chrome, and to, see what, impact it, had on the.
Loading Experience, in mobiles on, mobile connections the, evidence what it was they had no. Packed and so just by they're considering, completely, ripping it out to the chrome chrome codebase, Akamai. Tried. Some other tests, they're really interested in push they, did see some benefits, but most of the blue lines is that they actually most of the tests that couldn't even reach statistically, significance, to determine, whether or not ie it was so close that we don't even know so the proof is that actually in the wild, even it's not doing what we were hoping it would do so should. You really push only. If you've got long round trip times but again you should probably be solving, that problem before, can. You only if you can use the async push pattern instead. Of the preload pattern and, if. You've got a client rendered app shell and you could use purple pattern so if you've got a progressive, web app I would actually advise. You to look into this pattern because it could speed things up even more and if, you can control, control, the client cache like, an electron, app or you, have a serviceworker. So. The question really is is that one round-trip time saving, actually, worth the complexity. That it brings and I personally, would argue no and. Are there other solutions out there there must be solutions. And. I wouldn't be sitting on this stage if I couldn't give you some solutions to these problems so most, of the techniques I presented so far have. All got trade-offs with them however. I personally am really excited about the future and some of the new techniques and specifications. Are about to land in browsers that are going to solve some of these problems for us so, the first question is can we just fix push right. There are a couple of inconsistencies, maybe we can just make that better and the biggest problem was, about the server not knowing the knowledge of what is inside the clients, cache tape and this is where the cache digest specification, comes in what. If the browser could send an end indication. To the server every time it opens a TCP connection and it says here, were the items that are in my cache for. The hostname that you're afraid to for that's very important for security reasons we don't want to tell other host names things, that we've got cache for other host names and he says and so this is where custard just comes in it's a data frame that, uses a cuckoo filter which is a probabilistic, data structure, that the, server then you can say okay do they have main.css and look inside that bloom filter if it does or it doesn't okay, I'm going to push now so, the server can use that knowledge to determine, what, it wants to push and I Percy I'm really excited about this spec not because of push, because, of the possibilities, that opens for doing some really intelligent optimization. For our build systems, what, if webpack, could, know that the, browser the client has already got module, a D, and Z, inside. Its client cache I could then create a bundle. That was optimized, for not just the things missing, them and so we can actually become really, really intelligent if we, had client cache tape on the server side and. So. That solves our repeat view problem with push that, on the repeat view we could see the client the cache digest ago they've already got it in their cache I'm not, going to push it again and, there's a big win for me and Patrick is very happy. The. Specification, is being actively worked on and it's, been accepted by the ITF it was proposed by my colleague Kazuo at fastly Akamai, advice, is our. Friend is also working on it with us fast. These open source hates to be too server if you're interested, in is one of the first working implementations. Of this Chrome, and Firefox are, extremely interested, and discussing. Implementing, but. That still seems a bit, through complicated right having, this client. And server, state, that, we're having to manage right HTTP. One of the beautiful things about about, the whole protocol, is it that is stateless, and this is creating, state and knowledge between them which which I don't really like and.
And. If you take away one photo, or anything, from this talk I've, summarized, it here for you now, identify your critical resources, preload. Those hidden sub resources like it's fonts your application routes your. Application. Data pre. Connect to any third critical third parties. Avoid. Using push. With preload if you, want to push use async push but. In the future priority, hints early hints and cache digests, hopefully, are going to solve a lot of these problems for me for, us rather, that's. Yes thank you very much Jim, Pat scrunch it to there and my. Slides will be online there. There. Were a couple of questions. One. Of them was, about, your. Use of system font was that just an example or is that the kind of thing that people would in fact be considering, a critical resource for. The. What sorry are, you have you had is one of your examples using, a font is yes I know the fonts definitely. Of all. The things that eyelid I listed as hidden sub resources fonts. Are the most important, because of the way that I described, the fonts. I the. Network requests for them are initiated, so late in the life cycle we really, really need to tell browsers. About them and I gave, the Shopify example, of saving, about 1.2, seconds unloading that's, like in there, already quite fast website we've seen other. People. Save up to 3 to 5 seconds, just by adding that single, preload. Header to a load their font file fantastic. My other advice though is would, be just to reduce the amount of custom fonts that you're delivering, but, I know that we all like, to have aesthetically, pleasing and, nice-looking, websites so, one, of the great things I saw is that there's another question which was hey you know what if I'm hosting on firebase what if I'm hosting static assets. With s3 and we already got some people starting a conversation which is fantastic. This is a peer-to-peer conference. But, if, we could just get your quick opinion, what about people who aren't running their own elaborate, server server, set, up you have a comment about yes. So like it specifically, the preload API is friendly. For you because of this so even, though I said that the, header.
Version, Of it is my preferred method you. Can still use, the Dom version, of it just have a link element in your Dom I'm assuming if you're you, still have access to that your actual underlying application, code even though it's being hosted by a third party so, you can still press preload. And priety, hints in your HTML so, just decorate your HTML then and not have to worry about the networking side of it and, then also send, an email to your hosting support company to get interested. In in, preload, and push well. Fantastic folks please continue the conversation, in our forums thank you so much thank, you.