WICG / background-sync

A design and spec for ServiceWorker-based background synchronization
https://wicg.github.io/background-sync/spec/
Apache License 2.0
640 stars 85 forks source link

Question about offline sync when browser closes #140

Closed BorntraegerMarc closed 7 years ago

BorntraegerMarc commented 7 years ago

Hi everybody So I started of at this tutorial https://developers.google.com/web/updates/2015/12/background-sync which is quite neat when it comes to using the simple standard use case for BackgroundSync.

However I ran into problems when I had this scenario:

However now comes the real problem: window.onbeforeunload only executes once in a synchronous way. So every navigator.serviceWorker.ready or registration.sync.register would never get executed.

So basically my question comes down to this: How would one activate the synchronization after the user closes the browser?

jakearchibald commented 7 years ago

Because of performance reasons I do not want to send every messages to the Service Worker to let it do a POST call to my backend

What's the shape of this performance problem? What's slowing things down?

BorntraegerMarc commented 7 years ago

@jakearchibald Thanks for the reply. At the moment I am using web sockets to communicate between clients. So making a REST call for every single message takes a lot more resources. In terms of bandwidth usage... For normal applications this could be sufficient. However we are building a app for a medical communication. So speed is a top priority for us.

Plus we never know when the sync event is fired from BackgroundSync. So this makes it kinda unstable to rely on this service if it is not 100% guaranteed, that the code will be executed immediately.

I do not think it is possible to use socket.io in a service worker, correct? That is the reason why we need to determine if the app is active/inactive. So we do not waste resources

Additionally we switched from window.onbeforeunload to Page Visibility API (Because nearly no mobile browsers implement the window.onbeforeunload API) . Here again: I do not know if it is possible to call async methods. So the same question is still standing...

It would be generally interesting to know, how BackgroundSync deals with these performance critical scenarios...

jakearchibald commented 7 years ago

So making a REST call for every single message takes a lot more resources.

I didn't think this was particularly true with HTTP/2. Websockets are kinda redundant there.

If you're already invested in websockets it may be an issue though. Websockets aren't available in a service worker.

I wonder if you could:

  1. Add the message to an outbox in indexeddb
  2. Register a background sync
  3. Send the message via websockets & remove from the outbox if successful

In the sync event:

  1. Wait a second
  2. If the item is no longer in the outbox, all done! Otherwise…
  3. Attempt to send the message via a regular HTTP request & remove from outbox if needed.

With this system there's a potential race condition where the message will be sent by both the page (via the web socket) and service worker (via HTTP). The messages should have an ID so the server can detect any duplicates.

BorntraegerMarc commented 7 years ago

I didn't think this was particularly true with HTTP/2. Websockets are kinda redundant there.

@jakearchibald What do you mean with this? I know that HTTP2 keeps the connection open so resources can be pulled/push more efficiently from the server. But is it as fast as web sockets?

To your other suggestion with indexedDB: Yes this would be absolutely possible if not for the potential race condition. In our current architecture it is not possible to manage this ID in the frontend (so that the backend can determine between already received messages). So we can not really count on that solution...

So most real-time apps need to use the Page Visibility API or something similar to manage the "outbox" (similar as you did in the tutorial; only difference is we sync when the user exits the app). And then the question arises how to manage this outbox in an scenario where it needs to be populated when the user exits the web app? It is probably not realistic that the BackgroundSync event gets params for devs to pass data on event creation?

I understand that this question driftet away from a simple BackgroundSync issue and I'm sorry for that. But maybe we could sum it up to make the answer also interesting for other BackgroundSync users: How should the BackgroundSync event exchange data with a soon-to-be terminated app (in closing state)? Without periodic sync actions (like managing the outbox on every action)...

jakearchibald commented 7 years ago

I know that HTTP2 keeps the connection open so resources can be pulled/push more efficiently from the server. But is it as fast as web sockets?

https://github.com/whatwg/websockets/issues/49 suggests so, but I haven't tested it personally. It'd be faster to start, as it wouldn't need an additional connection set up.

So most real-time apps need to use the Page Visibility API or something similar to manage the "outbox"

Perhaps, although if the user closes a tab there may not be time to schedule the background sync.

It is probably not realistic that the BackgroundSync event gets params for devs to pass data on event creation?

This is something we could add, although I'm a little concerned about adding a new storage system for every new API. However, even with this it's still possible the app will close before the background sync is registered.

How should the BackgroundSync event exchange data with a soon-to-be terminated app

The best idea is to avoid letting this happen. If you run all your important posts through background sync you don't need to worry about trying to set up background sync as the app is closing. I realise web sockets make this difficult for you though 😢

BorntraegerMarc commented 7 years ago

Alright, thanks for the answers @jakearchibald keep up the good work :)