Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about offline sync when browser closes #140

Closed
BorntraegerMarc opened this issue May 21, 2017 · 6 comments
Closed

Question about offline sync when browser closes #140

BorntraegerMarc opened this issue May 21, 2017 · 6 comments

Comments

@BorntraegerMarc
Copy link

BorntraegerMarc commented May 21, 2017

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:

  • My web app uses a chat, where users can write messages to each other
  • Performance is a very high prio. That is why we use Socket.IO to send/receive messages
  • So I wanted to implement BackgroundSync in case the user is offline but sends messages anyway. One requirement was that (like from @jakearchibald tutorial) that the browser could be closed and the messages would sync anyway.
  • The PROBLEM: 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.
  • The SOLUTION: Listen to the window.onbeforeunload event and manage the sync there for all unsent messages

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
Copy link
Collaborator

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
Copy link
Author

@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
Copy link
Collaborator

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
Copy link
Author

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
Copy link
Collaborator

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?

whatwg/websockets#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
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants