Hack Time: Service Workers, Background Sync, and PouchDB

John Kleinschmidt
Offline Camp
Published in
4 min readNov 28, 2016

One of the best parts of Offline Camp California 2016 was the conversations that happened in between sessions. During one of those conversations I was talking with Nolan Lawson about challenges I was having trying to sync a local PouchDB to a remote CouchDB database within a Service Worker for HospitalRun. We were talking about using Background Sync with a Service Worker to accomplish PouchDB syncing and Nolan suggested we have a session to hack on the problem further.

During our session we made some important discoveries and bumped up against some key questions:

  • What does Background Sync really do?
  • PouchDB continuous syncing doesn’t work well in a Service Worker
  • How can we best sync between PouchDB and CouchDB within a Service Worker?

What does Background Sync really do?

As we started looking into Background Sync, it became clear that we were confused as to what Background Sync was and how it worked. Our first impression was that Background Sync somehow involved a server side push that initiated a sync with the browser. However after reading more about Background Sync and playing with a demo, we realized our assumptions were wrong.

So what does Background Sync actually do? The basic concept is that you can queue events within the browser that will fire in your Service Worker when the browser has network connectivity. One question that then came up was how is this better than using navigator.onLine and the online window event? The difference with Background Sync is that the event listeners will fire even if your web app isn’t open in the browser because the sync fires in a Service Worker.

One of the other learnings with Background Sync is that if the same event is queued multiple times while offline, it will only fire once when online. This means that if you have distinct events that you want to individually fire, you would have to name them as distinct events. Background Sync also has a specification for periodic synchronization that would allow you to fire a sync event on a predetermined interval, but as of this writing, that feature is not available in any browsers. Speaking of browsers, right now Background Sync is only supported in Chrome, but work is in process to bring Background Sync to Firefox.

Dean Hume’s background sync demo in action

PouchDB continuous syncing doesn’t work well in a Service Worker

During our hacking session, Nolan was testing PouchDB continuous synchronization in a Service Worker and discovered it doesn’t seem to work. It turns out that the issue has to do with the fact that you cannot rely on global state in a service worker:

“Once successfully registered, a service worker can and will be terminated when idle to conserve memory and processor power.” https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope

This means that we cannot expect any intermittent process such as a PouchDB continuous sync to “stay alive” within a Service Worker. This was a particularly thorny issue because PouchDB seemed to silently fail when the service worker was killed, making it really hard to diagnose.

Syncing between PouchDB and CouchDB within a Service Worker

At this point maybe you are wondering why we are going to the effort of syncing between PouchDB and CouchDB within a Service Worker. Why not just run the sync in the UI thread? If you are using Edge, Internet Explorer, or Safari, this should be fine, but if you are using Firefox or Chrome you will run into performance issues because IndexedDB, which is used by PouchDB, blocks the DOM. So for performance reasons in those browsers it is important to be able to sync within a Service Worker which uses a separate thread; otherwise your app might just feel like it is a woodpecker trying to fly with a weasel on its back.

Source: https://twitter.com/Jayward7/status/572495249756577792

So what are our options? Instead of a continuous sync, we can be smarter about syncing by firing one time syncs when there is a change. We have several ways of doing this:

  1. Server-to-client changes: Most examples of the the Push API show notifications as the primary use case for the Push API, but you can also use the Push API from your server to push an event to the Service Worker that lets the Service Worker know there are server side database changes.
  2. Client-to-server changes: We can register Background Sync events every time we write to the local PouchDB. These events can be used to notify the Service Worker that there are local changes that need to synced to the server.
  3. In browsers that don’t support Background Sync, we can run a changes listener in the UI thread that then sends a message to the Service Worker via postMessage to sync the local and remote databases.

Where do we go from here?

We were hoping to come away from our hack session with a working demo of PouchDB/CouchDB synchronization from a Service Worker, but unfortunately ran out of time to do so. Ideally we would like to create a demo that coherently demonstrates all of the techniques described above. Additionally, HospitalRun is looking to implement these solutions in order to properly handle offline synchronization, so stay tuned for further updates.

Editor’s Note: This article recaps discussions we had at Offline Camp, a unique tech retreat that brings together the Offline First community. Join us at our next event or sign up for updates and cast your vote on where we should host future editions of Offline Camp.

--

--

John Kleinschmidt
Offline Camp

Full Stack Engineer @electronjs and Lead Developer for @HospitalRun