 Hi, everybody. So as they said, my name is Drew Knox. I'm a PM on the storage team. I work on a few other projects, but storage is really what I'm here to talk about today. Before I get started, though, my mom told me right before I came on that my grandma was going to be watching this talk. So please laugh at all of my jokes. Otherwise, we'll be cripplingly embarrassing just throughout. So again, please thank you for that. So before I get started, I want to do a show of hands. I'm the talk right after lunch. So you guys are all probably in food commas, not really paying attention, catching up on email. So a little calisthenics to get you guys going. First, how many of you are still capable of raising your hands? You didn't eat too much. You can, yeah. All right, good. A few people lost, but that's OK. All right, now onto the real question. How many of you have used client-side storage in a meaningful way, not just playing around with Service Worker in a demo app, in one of your sites? All right, now keep your hand up if you viewed that primarily as a critical performance optimization, not offline. All right, it's about right. So when we look at these kind of numbers through Chrome usage metrics, we see that about 2.5% of page traffic uses things like index DB or cache storage. So my goal today is to convince all of you. So hopefully, you'll all have your hands up next time around at CDS, that client storage is the most important performance optimization you can make for load time in all browsers. And most importantly, because we all know that caching and all this is important, I want to convince you that it's available today and that it's kind of low hanging fruit for you to pick up everywhere for all of your users. So why is this important? We've heard this number repeated a lot, which is that you lose half of your users if your site takes more than three seconds to load. I won't belabor the point, but it's pretty scary, right? It's kind of a horror movie. But when you think about it, it's actually a lot worse than that, because on the average 2G network, it takes three seconds just to get the first byte. So we're kind of already hosed, right? We're fighting an uphill battle. And what's worse, 320 milliseconds is how long it takes to load one megabyte off the network. This is really hard. The deck is kind of stacked against us. So we need some tools to help us not just improve our loading performance, but avoid the need to hit the network at all. All right. So we know it's important. We know we've got to do something, but I don't just want to preach a horror movie. I want to give you guys some actionable steps. So in my talk today, I want to walk through how you should reason about spending your time on client storage, where the biggest wins the least amount of work for 80% of the value, some technologies that you can use, along with some libraries that'll make it easier, more ergonomic, how much storage space you have available. And then if you guys are all really good and you laugh at all my jokes, and my grandma is really proud of me at the end, I'll give you guys a view of some of the future things we're looking at that are kind of exciting. Now, before I move on, I was told I should explain the first line, because nobody thought that how you spend your time, that these emojis were conveying that. My girlfriend said it made no sense, but she's also an iOS developer. So what does she know? All right. How are you going to invest your time? Web developers are pulled a thousand different directions. Lots of us are full-stack engineers. Unfortunately, we're working in a place where Flexbox is still one of our most powerful layout primitives. We don't really have infinite resources to do infinite things. So before I get started, I want you, for the purposes of this talk, to think about storage as cash, not offline support. Offline support is really important, and it's something that's been touched on a lot. I just want to focus on cash as a performance optimization here today. All right. As a cash, you kind of have this spectrum of investment that you can make. Browser cash, all the way on the left, is sort of the default. It's relying on the browser to get things right for you, hoping that your responses are cashed and that they aren't cleared before the next time the user visits. And on the other end, you're building a spaceship. This is service worker, cash storage. You're optimizing everything to the nines. You're hitting like three seconds load time. This is sort of what you've been hearing from a lot of folks. So looking at the first one, browser cash, doing nothing, it does have some real benefits, right? You speed up repeat visits for your users, and that's not insignificant. But unfortunately, it only works for network responses. It's unpredictable. You don't know when it's gonna be cleared out. And it's got pretty coarse granularity, right? It's the level at which you served up files. So this is not great. It's kinda sad. Optimized browser cash is probably what a lot of you are doing today, and it's a really good step. This allows you to not only get repeat visits sped up, but you can actually get some proactive page load improvements using things like link rel equals import, or any number of things to try and load things before they come in. But it still only works for network responses. So these are a lot of the optimizations you see people, you've seen people suggesting sort of off-handed as they've been giving talks. It's still unpredictable, because again, you're relying on where the browser is storing things. And there's not much granularity here either, because it's still on the level of network resources that you've served up. Still not great. Content caching is where the first big step function can come in, in terms of improving performance. You get proactive page load improvements like before, but now you can work for all response types. So this is, when I say content caching, I mean things like saving image blobs in cache storage if it's available, or in index DB, and then serving your image tags with a blob URL, all kinds of things like that. You have some predictability, right, because the things that you're storing in cache storage, or in index DB, you have control over. But you're still using network responses for some other things, so it only gets a yellow here. It's not perfect. You have content granularity, and this is really important. Granularity is something where you wanna be able to change something and not have to re-download your whole bundle. So the more you can break things up and have your cache invalidate for only small pieces, the better. So again, you get granularity for content, but not for your network requests, so it only gets a yellow. This is still pretty valuable though. It gets a smiley face from me. Full cache control, the spaceship. This is a lot of work. I'm gonna be honest with you guys. I've never effectively done it, and I work on this team, and I should theoretically be able to say I've done a thousand of these. So it's great when you can nail it, and you've seen a lot of really big production apps that have, but with this comes a lot of work. You get proactive page load improvements, like before. You get all response types, again, that's great. It's fully predictable, because now you're pulling in even the network responses into a cache that you control, and that's really valuable. You can guarantee your user a certain performance level. You also have content granularity for your network requests and your content that you're serving. And as a major bonus, you get offline support, which people have talked about a lot. So I would be crying tears of joy if everyone started building their apps like this today, but I understand that that's pretty hard. Realistically, I think you guys are gonna wanna sit somewhere between the optimized browser cache that most people are doing today, and content caching. This is kind of the sweet spot, right? If you can serve all of your content from IndexedDB or CacheStorage or something like that now, you really have access to storing all of your content, even if you're not building that spaceship with ServiceWorker. So you can get full performance levels for your site, not just the app shell or something like that. All right, I've talked a lot about how this is, maybe not too hard, it's kind of low hanging fruit. It's really important, but let me put my money where my mouth is and dig into some code. So first of all, as I put this together, I really fretted about whether or not I should put my thens on a new line or attached. I was afraid I would get flamed one way or the other. I went with their own line, but please don't hurt me. That's my best go. So here I'm using a Redux app. It could work with any framework, right? You could use your own view binding library. I just create my store, get it set up. Here's the magic part. I'm using an IndexedDB wrapper library to store my state essentially whenever there's a change. This is done asynchronously, so it's not gonna block the main thread. And then later, when I'm re-inflating my state, instead of hitting the network or pulling from Firebase or something like that, I grab it and fire a database-loaded event, which will reinstate my state without having to hit the network. So this kind of pattern, as you can see, was only three to five lines of code, and it can avoid an entire network hop for your whole app, right? You have your entire app state all saved local disk really, really easily. So this is a pattern that I think works really well with Redux-style apps, but it really can work for anything. Maybe it's a little more work if you don't have a single object you're trying to save. If you wanted to tweet this, here's a good slide with all the syntax highlighting. In general, there are a few best practices. I hinted at them, but just to make them clear, when you're managing your cache on the user's device, you wanna make sure you're doing client-side chunking. This means you might pull in an initial bundle and then kick off requests for smaller, more granular pieces so that you can revalidate only small chunks as things change. This pattern is a little more complex, but can really save you network bandwidth. You also wanna preload pages the user might be about to visit. So if you imagine you're on some news site, you might wanna load all of the articles that are shown above the fold or something like that. You also wanna save commonly repeated components. So if you have a hero image, a logo, anything like that, just save as many of them as you can. Get rid of as many network hops as you can. What should you be using to do this, though? You guys might be aware that the web is not really one for having a single answer to a problem. There's lots of different ways to do things, but thankfully, it's pretty simple in terms of what you wanna use on the browser. So if your data is URL-addressable, you should use the cache storage where it's available. It's really simple. It's kinda just like a key value pair. Works really great with service worker. So it's your no-nonsense easy, easy solution. If you've got structured data or if you have a lot of users who don't have access to cache storage, Index TV is where you wanna go. These two combined, they're asynchronous. They're modern. They're getting lots of attention from browser makers. This is sort of your bread and butter. This is where you wanna be doing all your work. Now, in terms of availability of cache storage, I have here, can I use usage-weighted slides? It's available in a lot of places already. So I know some of you are thinking, oh, I don't wanna deal with having to do progressive enhancement or fall back to Index DB, but you can hit a lot of your users with cache storage today and it's only gonna improve in the future. So I have here just a few libraries that we think on the Chrome team are great for helping to improve your interactions with Index DB. They all give promise support. Some of them give database sync. Some of them even try to recreate SQL syntax, but these are all great libraries in terms of ergonomics, but also they've thought a lot about making themselves minified so they don't impact loading performance. So that is a really big win. For cache storage, it's a newer API. There's not quite as much available. We heard from Jeff about ServiceWorker Toolbox, ServiceWorker Precache. Webpack has the offline plugin, but otherwise there aren't quite as many things that are available now for use. All right. If I had to guess, I would say this was probably the area you guys were most skeptical when I started the talk. You are making websites, right? We aren't using device resources. This is the whole point of the web. It's ephemeral. It doesn't stick things around. I'm sure that's been changing as we've been talking about ServiceWorkers and kind of convincing you guys of offline, but it's still a real question. How much space do you get and how reliable is it? So at first I started looking at empirical ways that I could do this, right? Small demo apps to try to fill the cache, fill the storage partition and see what would happen, but then I realized why don't I just email all the different storage teams and the different browsers and ask them how much space is available. And it turns out that worked way faster. So the browser quota limits today are, they kind of fall into two camps. So we have percentage base. Chrome gives you 6% of free disk space per origin. Firefox gives you a little more. It's 10% shared across ETLD plus one. So this is like play.google.com and movies.google.com would share storage. Safari gives you at least 10% of free disk space. An edge is, well, it's a little bit more complicated, but thankfully it's still fairly reasonable, right? Like edge is largely a desktop browser. It's got, so you can rely on being in one of the higher tiers. So you don't have to worry about all four of these all the time. And based on usage statistics and sort of looking into our own telemetry, we found that a simple number, simple rule of thumb is that you have 50 megabytes available on all devices and all browsers today. So this will get higher as you're working on higher end phones, but you can think of this as your minimum budget that you can use to try and improve performance on your site. If you remember my slide before or a ways back, if it takes 320 milliseconds to load a megabyte across the wire and you've got 50 megabytes available to you, that's 16 seconds of load time that you can save averaged across all your users' visits. That's pretty huge, right? 16 seconds could take that 19 second loading app down to three if you were able to condense all of those network hops into something that you could cache. If I could, I would take this off and I'd do a mic drop because I think that's probably one of the most exciting things to me about client side storage. But with great power comes great responsibility, right? We're now using resources on the user's device and this is kind of reinventing what we think is the contract we make with users. So first and foremost, you need to make sure you're measuring and thinking about your app's overall storage footprint. So this is something like figuring out your eviction strategy to make sure you don't just balloon up to 6% after three visits. But the whole point was we wanted to be using the user's device, so we can't just keep arbitrarily lowering our storage footprint, right? That would get us back to where we are today. So the second number that comes in is your read to write ratio, and this is something that Chrome Storage Team thinks about globally as well, which is where we try to make changes to our eviction policies that reduce the storage footprint without lowering this ratio. That means we're trying to clear out data that's never going to be read again. Now, sometimes you'll clear something and maybe it was going to be read in three months, right? And sometimes that's right, but sometimes you actually do want your cache to stick around for three months. So another metric that you can look at is when you store something, check to see if you had cached the resource before. And if you had, look at the time difference between the two, and that'll give you a sense of how long of a horizon was this sitting there for kind of useless on the user's device. So these are the three numbers that Chrome looks at, and Chrome really cares about. And I think it's a really useful way to think about storage, but I would love to hear from other people if they have other metrics that they think are important to track. It's a really interesting space. Your eviction strategy is not the only thing at play though. The other browsers have eviction strategies of their own, or at least some of them do. So for Chrome and Firefox, we evict your storage, or any storage on the device. When Chrome or the disk is full, we evict the most least recently used, most least recently used, that's not great. The least recently used domain from the list. Now it's important to note, this is very rare. Chrome clears a domain storage less than 0.1% of the time. So for the most part, when you store something it sticks around, but it is something to keep in mind. Safari and Edge, however, don't clear index DB. So you can treat that as persistent. Now on Mozilla, or on Firefox and on Chrome, there is something to try and help you work around the eviction policies when it's important. It's called persistent storage. It shipped in Chrome 55, and it's in development with Firefox. So the way this works is you essentially request the persistent storage permission, and then Chrome will exempt you from automatic clearing. Well also, when the user clears browsing data, pop up a prompt for any persistent storage sites that says you're also gonna clear these, is that okay? So this is trying to help make sure that if you've entered a contract with your user that something should be available offline, you can guarantee that. Now unfortunately, when you do user surveys and you ask them about storage, it becomes pretty clear that it's not something they either want or are able to effectively reason about upfront. If you ask a user, hey I'd like to store 100 megabytes of your data, is that okay? They'll either freak out and say no, or they just won't really understand the question. But they are very good at reasoning about your storage is full, which site would you like to clear? So because of this, we try to avoid showing a permission prompt when you request durable storage or persistent storage. Instead, we have a heuristic, which we use to either automatically grant or deny. And the heuristic is essentially, if you're treated like an app, you'll get app like storage persistence. And that means if you've been added to home screen, if you have push notifications, if you've been bookmarked, or if we've seen if Chrome has tracked that the user has engaged with you, with your site a lot. So if any of those hold, you'll get the permission, otherwise it'll be denied. We recommend that you hold off on showing offline UI until you've received that permission so that you know it'll be around. Certainly not a requirement, but it's a little bit of a best practice. And then use the quota estimate API to make sure that you aren't ballooning your storage, or you want to make sure if you have a regression where you get some kind of storage leak, you can clear it up, because now you can't rely on the browser to protect you anymore. You have to kind of take your life into your own hands. That's sort of the end of my practical area of the talk. You guys laughed at a few of my jokes, so I like you. I feel like we've grown close over this time. I want to give you a look into what we're thinking for the future, because I think it's important. I think it's gonna change the way we think about storage and apps and offline and all of these things. So first and most importantly, we want to give you guys more space. We're kind of channeling our inner Elon Musk. You get more people with more space all the time. This is kind of a new paradigm. On Chrome, we're thinking about how can we start giving PWAs, or web apps in general, access to as much device storage as native apps get. And we want to do this because we think, as PWAs become more common, the divide between apps and PWAs and all these things are gonna become less clear. And so we want to give developers all the tools that they need to make great experiences. Some of you might be cringing or freaking out. It is a little bit of a divide. You could go to a website and it could take all of your storage. So this is something we're thinking about very carefully. And again, those three metrics I talked about before, we're tracking those very closely and we're gonna be ratcheting this up slowly to make sure that bad ecosystem changes aren't coming into play. So this is something, though, to be looking for storage increasing over time. We're also thinking about some new functionality and it kind of falls into two stages. We have one set of things that are in development. They're actually pretty far along. IndexedDB observers are a way for you to help synchronize transactions with IndexedDB across tabs. So if you have a kind of UI that uses something like that, it's really effective for making it quite simple. Async cookies are gonna be a big win. They'll also be available in service worker. So that's pretty awesome. Both of these are Wikig or YCG specs that have some degree of implementation. So they're coming soon, they're sooner rather than later. Then we have a couple areas of exploration that we're looking at. So I mentioned a lot of libraries that will give you promise support for IndexedDB. We know it's great. We know it's the way that people wanna work with async code moving forward or at least a large subset of people wanna work with it that way. But it turns out layering on IndexedDB promises to transactions is kind of thorny. It turns out to hit all the edge cases it's hard. So this is an area that in terms of baking into the platform we're still thinking about and figuring out. The last one is kind of exciting and it's personally very, very cool to me. We're calling it writable files but it's the idea that we wanna start giving web apps the ability to get reusable handles to files on the user's device. So I'm sure a lot of you have gone to some website where they ask you to upload a file or they offer to download a file and you downloaded it as a zip or every time you made an edit you had to re-download the file. You had to re-upload the file. It's not a great user flow. So instead what we wanna do is create a way for apps to get a handle to a file such that they can just track changes to it like a normal app would. Again, this is an area of exploration. We're figuring out how to get the privacy and security models right but it is a spec that's available on the Wikig and we would love to get people commenting there telling us what use cases they might have for it, what concerns or mitigations they have planned. It's all on GitHub. We would love to get collaboration from everyone. All right. I hope that I've made an okay, hopefully great case for the idea that caching with client-side storage is a huge win that you have available to you today. It's not the easiest or the first thing you'll jump at. Performance optimization is kind of in a lot of ways, at least for me, when I'm developing a secondary impulse to making it look right or adding a cool new feature but it can be a huge win and it can really translate to increased bottom lines. So it's very important. And I hope that I've convinced you guys that there's stuff that you can do via link rel preload, loading blob URLs for images, storing things in index DB that work across all browsers and that you can do today and that it's not too much work to get it working. A few concrete takeaways is that storage isn't just about offline. Think about it as a performance optimization just as much, if not more. Offline is amazing but until your page is loading quickly, there's not gonna be anything available offline anyways. 50 megabytes is available to you on all browsers, on all devices. And this is gonna go up as time goes on and it will go up when you have users who are using higher end devices but this is sort of your bare minimum budget you have for improving performance. Index DBs for structured data, cache storage where it's available is for URL addressable data. And that's it. Thank you guys, I really appreciate you taking the time.