ServiceWorkers are a new “web platform” feature that seem to be maturing about now. Here’s two good overviews of what they can do: Using ServiceWorker in Chrome today - JakeArchibald.com Using Service Workers - Web APIs | MDN
Note also that because ServiceWorkers can modify network requests, they are only available on HTTPS sites. In other words: ServiceWorkers can only ever be a value-add for us, never a requirement. The app must always work without them.
What can ServiceWorkers do for Discourse?
-
(Best start) Merge message-bus requests across multiple open tabs on the same Discourse forum.
The message bus is what Discourse uses for all of its live updates. However, currently, every tab you have open makes a request to the server every 15 seconds. A ServiceWorker can intercept these requests and merge them all into 1 request every 15 seconds.
-
(Questionable value) Force caching of vendor.js and application.js
This probably isn’t worth the effort. The JS files have the SHA hash of their contents in their name and have aggressive caching headers.
Pretty much the only beneficial option here is to instantly reply to any request containingIf-Modified-Sincewith 304 Not Modified. -
(Great value) Push & Mobile Notifications
Because a ServiceWorker is able to run even when no clients are around, it’s required in order to serve push notifications. Also, because we know there should only ever be one (or less) ServiceWorker at a time for each forum, it’s the ideal place to serve desktop notifications from. This will let us disable the buggy “active tab tracking” code currently in use for desktop notifications.
Chrome push notifications require the forum admin to create an application with GCM (Google Cloud Messaging). Firefox push notifications require server-side storage of magic URLs.
-
(Easily screwed up) Caching of topic & post data
The ServiceWorker could use IndexedDB to store post data previously requested from the server, and reuse known posts in the responses when scrolling through a topic, potentially allowing offline topic browsing. However, this is easy to mess up - currently, messages are not delivered on the bus for every post that is edited or deleted; and if the message bus backlog gets cleared, the cached post data could easily be stale.
The stale cache problems could be alleviated if we use Background Sync, but it’s just at a proposal stage right now, not implemented in any browsers, and the spec is a bit sparse.
This is probably best shelved for a later date.
-
(Best value, hardest) Deliver the entire initial load from local cache
This would be the most ambitious thing to do with ServiceWorkers. The layout of the initial page is largely predictable, and could be stored on the client. If no PreloadStore data is provided, the Ember router will just do a network fetch for the data. Combined with caching topic & post data, this is a recipe for a fully offline initial page load. We would save a lot of network bytes by doing this (check out that
<noscript>section in every initial-load of a topic view).However, this rears the ugly head of one of the hardest problems in computer science: Cache invalidation. When I look at the source of meta.discourse.org, I see tons of site settings that can be changed, the site can update and so the script hashes change, the current customizations can change, data about groups and categories ( see https://meta.discourse.org/site.json ) can change, the custom emoji too.
The good news is that that’s about it for the list, though. We can add in live updates for categories, emoji, groups, and the site settings. We can make customizations refreshable in the background (sometimes). (We’ll have to move script customizations out of
</head>and to a file like the CSS is.) If we get all that, the last thing left is updates to the site. And a full network refresh is probably OK for that.
Implementation issues I’ve thought of already:
- If there’s a delay between getting topic data and getting the message bus current position from the server, we could lose updates. Solution: deliver the current message bus position in the topic response (and other pages).
- The message bus currently screws up big time if the backlog is cleared (
redis-cli FLUSHALL). Solution: The server should deliver a message on __global when a client requests a too-high ID, and the client will reload the page (or the worker will reload all pages and discard its messagebus data). - A new ServiceWorker that gets downloaded and installed will not activate until all clients (browser tabs) using the old version are disposed. This is a problem for Discourse, because tabs are long-lived. The solution is to get all active clients to refresh. We’ll need some design work to figure out how to do this in a non-intrusive way (the current behavior, “silently reload on navigation, or show a popup after 2 hours” is actually user-hostile in the second case.)
Implementation notes:
- use
Clients.matchAll({ includeUncontrolled: true, type: 'window'})to get all the window objects to trigger refreshes, via postMessage() or navigate(). TODO: how to identify new version vs old version? Make sure not to force refreshes on auth popups (client.frameType).
Reply with comments, or if you have any use-cases that I forgot about.





