Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the downsides to using skipWaiting and clientsClaim with Workbox?

By default skipWaiting is set to false in Workbox. Assuming you're only using the Workbox-created service worker for caching, are there any downsides to setting this to true? Without doing so, the next build of your app will ship updated resource urls (from webpack). These urls will be updated in the service worker's precache manifest, but without skipWaiting, the updated service worker will not be activated to take advantage of them, until the user closes their browser and re-opens.

These updated resources will be loaded correctly (webpack will load the resources with the new hash values in them), but they'll never be cached until, again, the user closes all open browsers running the service worker, and re-opens. Under these circumstances, is there any reason to not just set skipWaiting to true?

As a related issue, what exactly does clientsClaim control? The above problem is fixed by merely switching skipWaiting on; so what does clientsClaim do?

like image 395
Adam Rackis Avatar asked Aug 06 '18 20:08

Adam Rackis


1 Answers

As background, I'd recommend reading "The Service Worker Lifecycle". It's written from the generic service worker perspective, but the points apply to Workbox-powered service workers as well.

These concepts are also explained in more detail, from the perspective of using create-react-app, in this "Paying Attention while Loading Lazily" talk and microsite.

Workbox's implementation

Workbox uses an install event handler to cache new or updated entries in the precache manifest (appending a __WB_REVISION__ query param to the entries' URLs when needed, to avoid overwriting existing entries with different revisions), and it uses the activate event handler to delete previously cached entries that are no longer listed in the precache manifest.

If skipWaiting is true, then activate handler, which is responsible for cleaning up outdated URLs from the cache, would be executed immediately after an updated service worker is installed.

Under these circumstances, is there any reason to not just set skipWaiting to true?

There are a few risks to using skipWaiting, and to err on the side of safety, we disable it by default in Workbox. (It was enabled by default in older, sw-precache-generated service workers.)

Let's assume a scenario in which your HTML is loaded cache-first (generally a best practice), and then sometime after that load, a service worker update is detected.

Risk 1: Lazy-loading of fingerprinted content

Modern web apps often combine two techniques: asynchronous lazy-loading of resources only when needed (e.g. switching to a new view in a single page app) and adding fingerprints (hashes) to uniquely identify URLs based on the content they contain.

Traditionally, it's either the HTML itself (or some JavaScript that contains route information which is also loaded early on in the page lifecycle) that contains a reference to the current list of URLs that need to be lazy-loaded.

Let's say that the version of your web app which was initially loaded (before the service worker update happened) thinks that the URL /view-one.abcd1234.js needs to be loaded in order to render /view-one. But in the meantime, you've deployed an update to your code, and /view-one.abcd1234.js has been replaced on your server and in your precache manifest by /view-one.7890abcd.js.

If skipWaiting is true, then /view-one.abcd1234.js will be purged from the caches as part of the activate event. Chances are that you've already purged it from your server as part of your deployment as well. So that request will fail.

If skipWaiting is false, then /view-one.abcd1234.js will continue to be available in your caches until all the open clients of the service worker have been closed. That's usually what you'd want.

Note: While using a service worker can make it more likely to run into this class of problem, it's an issue for all web apps that lazy-load versioned URLs. You should always have error handling in place to detect lazy-loading failures and attempt to recover by, e.g., forcibly reloading your page. If you have that recovery logic in place, and you're okay with that UX, you might choose to enable skipWaiting anyway.

Risk 2: Lazy-loading of incompatible logic

This is similar to the first risk, but it applies when you don't have hash fingerprints in your URLs. If you deploy an update to your HTML and a corresponding update to one of your views, existing clients can end lazy-loading a version of /view-one.js that doesn't correspond to the structure of the HTML that was fetched from the cache.

If skipWaiting is false, then this isn't likely to happen, since the /view-one.js that's loaded from the cache should correspond to the HTML structure that was loaded from the same cache. That's why it's a safer default.

Note: Like before, this isn't a risk that's unique to service workers—any web app that dynamically loads unversioned URLs might end up loading incompatible logic following a recent deployment.

so what does clientsClaim do?

clientsClaim ensures that all uncontrolled clients (i.e. pages) that are within scope will be controlled by a service worker immediately after that service worker activates. If you don't enable it, then they won't be controlled until the next navigation.

It's usually safer to enable than skipWaiting, and can be helpful if you want the service worker to start populating runtime caches sooner rather than later.

I'll refer you to the section in the Service Worker Lifecycle doc for more info.

like image 139
Jeff Posnick Avatar answered Oct 11 '22 17:10

Jeff Posnick