Exploring ServiceWorkers for Discourse

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 containing If-Modified-Since with 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.

16 Likes

Very excited about this! I’m hoping it’d make sense to bundle “Add to Homescreen” into this effort.

2 Likes

I like this, but it needs to be tightly scoped.

Our first priority is getting the push notifications going for Android, cause that unlocks brand new functionality.

Our second priority is a PR to message bus that enables centralizing of message bus polling without turning message_bus.js into a “soup” of completely hard to understand and debug code that falls back super cleanly.

5 Likes

And before we do any of this, there are critical items on the 1.4 release list that are not done…

3 Likes

Praises be for push notifications! :smiley:
I bet Safari doesn’t support them :frowning:

1 Like

I did a quick spike and got it to work. Do we have a :+1: to continue?

7 Likes

Sure go ahead :thumbsup:

1 Like

Finally got it to work properly :smile:

https://github.com/discourse/discourse/pull/3815

8 Likes

Oh my :smile:

Can’t wait to try this out!
(and 10 seconds later have iOS users complain that they’re not supported. Come on Apple!)

1 Like

Here’s the docs for Firefox push notifications :wink: Push API - Web APIs | MDN

2 Likes

Hmmm the Push API isn’t available until FireFox 42, I’ll wait till it is released first before integrating push notifications for FireFox. In the mean time, I’ll update my PR to account for different browsers.

1 Like

Seems like Firefox 44 has Push Notifications:

https://blog.mozilla.org/blog/2016/01/25/firefox-can-now-get-push-notifications-from-your-favorite-sites/

5 Likes

This is not (or at least no longer) true, see the clients.claim() API: https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#clients-claim

3 Likes

Good news from Microsoft land: service workers and the push api moved to High Priority in the roadmap for Edge.


4 Likes

Big thumbs up for push notifications. We’re currently trialling Discourse to replace PhpBB for CAMRA (170,000 members) and I’ve been a fan of Discourse - having used StackExchange for years.

The single biggest requirement for the replacement system is so smartphone and tablet friendly but Discourse isn’t sadly ticking all the boxes. It’s better than PhpBB but maybe not enough for us to recommend :frowning:

2 Likes

I think that is a bit unfair, I use Discourse quite extensively over mobile as do many others, we have spent enormous amounts of engineering effort making sure the mobile story is great. To add to that you can do everything over email (reply, like, open topics etc) which also captures a bunch of use cases.

We plan to add support for push notification in a shell mobile application. However, the current experience is far superior to many other mobile sites I have seen and use. You achieve much of the notification over email anyway.

4 Likes

I’m not sure why it’s “unfair”. I like Discourse but to say it’s as mobile friendly as it could be would be ignoring the truth. The stakeholders in our project are not IT specialists and like the rest of the world, have come to rely on their mobile phone. So when they say “why doesn’t it work like this?”, we’re immediately on the back foot defending the fact that Discourse is a web application and not a native one. I’ve asked whether I can start a separate thread on, for us, will be a key decision point.

There are multiple threads on this, feel free to weigh in there

https://meta.discourse.org/search?q=mobile%20discourse

2 Likes

I’d keep an eye on https://github.com/ember-cli/rfcs/issues/45 and https://github.com/runspired/skyrocket as well.

2 Likes

Massive support signal from Google last week: the 2016 Founders’ Letter mentions Progressive Web Apps as one of Google’s two investments in the mobile web:

We also continue to invest in the mobile web […] Over this past year, Google has worked closely with publishers, developers, and others in the ecosystem to help make the mobile web a smoother, faster experience for users. A good example is […] Progressive Web Apps (PWA), which combine the best of the web and the best of apps—allowing companies to build mobile sites that load quickly, send push notifications, have home screen icons, and much more.

Service Worker support is the cornerstone of PWA support.

As a Developer Advocate at Google helping with PWA and AMP efforts, please let me know if there’s anything we can do to help flesh out SW support - which I do see running at https://meta.discourse.org when I use a mobile UA.

13 Likes