-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Trigger database syncing with Background Sync #820
Conversation
@gnowoel thanks for your work on this PR. I think we need to hold off on further work on this until this PR for PouchDB is completed: pouchdb/pouchdb#5903. I believe this maybe why you are having trouble doing the initial sync. Basically, PouchDB wasn't testing fetch properly in their test suite, so it appears there may be some issues with using fetch with PouchDB, which is needed to use PouchDB within a Service Worker. Also, in looking at your code, listening for changes in the Service Worker will not work because the service worker can be killed at any time by the browser, so we can't rely on the changes listener to be long running. I haven't had time to test the concept in full but I think the solution for HospitalRun should be as follows in browsers that support background sync and service workers
|
@jkleinsc My motivation of the PR was to fix the upfront bug, while laying ground work for possible future update. I totally respect your decision to hold it off, but I really think we could perhaps improve this PR first. Here're the reasons just for your consideration.
Actually, we listen for database changes in the main thread (in the
We created a PouchDB instance in the UI thread. This is for doing the initial sync and watching database changes. Considering the initial sync in fact only happens once (because the data would be persisted in IndexedDB) and watching database changes is a relatively lightweight operation, the performance impact should be acceptable.
I didn't do intensive testing, but this seems to be working already. If there're any bugs, I'd be happy to fix them.
It's a great idea for sure. However, as I briefly mentioned above, while it's not ideal, watching database changes in the UI thread should be acceptable. So, it's more like an enhancement than a necessity for me. Could we leave the watching code in the UI thread for now, and worry about how to integrate Push API (which requires more work because it needs service-side cooperation) for later?
If the bug is fixed, maybe we can move the initial syncing code into the Service Worker. But it also sounds more like an enhancement to me. The initial sync only happens initially, when we start the app in the browser for the very first time. Once the databases has been fully synced for once, all subsequent calling to the Suggestion While I totally respect your decision to hold it off, I would suggest that we fix the bug first. If we stop here, we get something that works already. If we want to improve it later by introducing advanced techniques like Pushing API, the current implementation shouldn't get in the way. Please correct me if I'm completely wrong. |
@gnowoel I misread your code. I mistakenly thought the changes listeners were in the service worker. Let me take a closer look at your code and what you are proposing. Can you check on one thing on your end? It looks like Travis CI is failing and I suspect if you run The error I am seeing is: |
@jkleinsc I missed the test part. Sorry about that. The failure in the CI is due to undefined databases. We created databases when initializing the I updated the code to just skip the initial sync unless database are created. By the way, do you think it's a good idea to implement poor man's Periodic Sync with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gnowoel thanks for the updates. I finally had some time to sit down and take a closer look and I have a decent amount of comments. Basically the concepts are sound, but there are some improvements that can be made using Ember conventions around service injections as well as tapping into functions that ember-pouch provides us. Let me know if this all makes sense:
}); | ||
} | ||
|
||
if (localMainDB && remoteDB) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of running this code when this code is parsed, it should be run when the DB is set on the adapter. This adapter extends the ember-pouch adapter, source code for that is here:
https://github.com/nolanlawson/ember-pouch/blob/master/addon/adapters/pouch.js.
There is a function in the adapter, changeDb
that we should be calling to set the database from the database service, but right now we are instead using a computed property to define the db with the following line:
db: Ember.computed.reads('database.mainDB'),
What we should do instead is in app/services/database do the following:
- Inject the store service into the database service:
store: Ember.inject.service('store')
- In the setup function after this line:
this.set('mainDB', db);
, add the following code:
let adapter = this.get('store').adapterFor('application');
adapter.changeDb(db);
Once this is setup, we can then extend onChange function in app/adapters/application.js to run our code. For example:
onChange(db) {
foregroundSync().then(() => {
watchLocalDB();
this._super(db); //Super will watch remote db
});
}
|
||
function backgroundSync() { | ||
console.log('registering background sync'); | ||
return navigator.serviceWorker.ready |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code should verify navigator.serviceWorker is available, eg if (navigator.serviceWorker) { ...
console.log('registering background sync'); | ||
return navigator.serviceWorker.ready | ||
.then(function(reg) { | ||
return reg.sync.register('sync'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same thing here, check to see if reg.sync is available to call.
@@ -9,6 +9,62 @@ const { | |||
} | |||
} = Ember; | |||
|
|||
const { localMainDB, remoteDB } = window; | |||
|
|||
function foregroundSync() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My only concern here is this will be slow for large databases and I would prefer to offload that to a separate thread. Small dbs would work ok with this though.
}); | ||
} | ||
|
||
function watchRemoteDB() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This adapter is already watching the remoteDB, so we just need to extend the onChange function. For example:
onChange(change) {
backgroundSync();
this._super(change);
}
@@ -162,6 +165,7 @@ export default Ember.Service.extend(PouchAdapterUtils, { | |||
_createRemoteDB(remoteUrl, pouchOptions) { | |||
return new Ember.RSVP.Promise(function(resolve, reject) { | |||
let remoteDB = new PouchDB(remoteUrl, pouchOptions); | |||
window.remoteDB = remoteDB; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a global is unneeded. The application adapter has access to the database service, so it can use that to get the remoteDb via:
this.get('database').get('mainDB')
@@ -9,6 +9,62 @@ const { | |||
} | |||
} = Ember; | |||
|
|||
const { localMainDB, remoteDB } = window; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These values should be pulled from the database service. For example:
let remoteDB = this.get('database').get('mainDB');
@@ -176,6 +180,7 @@ export default Ember.Service.extend(PouchAdapterUtils, { | |||
_createLocalDB(localDBName, pouchOptions) { | |||
return new Ember.RSVP.Promise(function(resolve, reject) { | |||
let localDB = new PouchDB(localDBName, pouchOptions); | |||
window.localMainDB = localDB; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like remoteDB, Using a global is unneeded, but we should set the localDB as a property on the database service so that the application adapter has access to it:
this.set('localDb', localDb);
Then in the application adapter you can access via:
let localDB = this.get('database').get('localDB');
@gnowoel as far as |
@tangollama Sorry for the long delay. I didn't forget it, but I was trapped by my own life's struggle. Your reminder reminds me that there's no better time than now. And yes, I'm willing to finish what I started, at least give it another try before it's too late. |
@gnowoel My apologies here. Sorry to hear about your own life's struggle - I hope everything is going ok. @tangollama and I weren't in sync on this. I have been doing some work on this whole issue and given that we are trying to get a 1.0 release out this month, I need to take primary responsibility for this issue, so please feel released from taking care of this. I am going to close this PR, but please feel free to contribute in the future! |
@jkleinsc Thanks for your kind words. It means a lot to me! I was trying to update PR today, based on your very inspiring comment above. It's only partially completed, but along the way, I've found a few things that might be useful.
I ended up defining the sync-related methods in a service, and then injecting it in the module that handles authentication. That way, I can setup syncing just by adding a authenticate(credentials).then(() => this.get('sync').setup()); I'm not sure if it's a good idea. Perhaps Ember initializers are the way to go. I don't know. But I'm sure your solution will be much better. Thanks again! (I'd like to attach my latest update below. It's just a prototype. So, please ignore it if it's useless.) |
Oops. The code is not showing. Guess it's because the issue is closed. Never mind :) In case you want to take a look, please see here. |
Fixes #731.
Do initial sync in the main thread. Subsequent syncing is triggered with a Background Sync event and only happens in the Service Worker.
cc @HospitalRun/core-maintainers