Bug 1416320 - Do a quick sync before going to sleep. r?markh
MozReview-Commit-ID: I36uvEFlEz5
--- a/services/sync/modules/policies.js
+++ b/services/sync/modules/policies.js
@@ -172,16 +172,17 @@ SyncScheduler.prototype = {
Svc.Obs.add("weave:engine:sync:applied", this);
Svc.Obs.add("weave:service:setup-complete", this);
Svc.Obs.add("weave:service:start-over", this);
Svc.Obs.add("FxA:hawk:backoff:interval", this);
if (Status.checkSetup() == STATUS_OK) {
Svc.Obs.add("wake_notification", this);
Svc.Obs.add("captive-portal-login-success", this);
+ Svc.Obs.add("sleep_notification", this);
IdleService.addIdleObserver(this, Svc.Prefs.get("scheduler.idleTime"));
}
},
// eslint-disable-next-line complexity
observe: function observe(subject, topic, data) {
this._log.trace("Handling " + topic);
switch (topic) {
@@ -323,16 +324,17 @@ SyncScheduler.prototype = {
this._log.error(`Engine ${data} found ${subject.newFailed} new records that failed to apply`);
}
break;
case "weave:service:setup-complete":
Services.prefs.savePrefFile(null);
IdleService.addIdleObserver(this, Svc.Prefs.get("scheduler.idleTime"));
Svc.Obs.add("wake_notification", this);
Svc.Obs.add("captive-portal-login-success", this);
+ Svc.Obs.add("sleep_notification", this);
break;
case "weave:service:start-over":
this.setDefaults();
try {
IdleService.removeIdleObserver(this, Svc.Prefs.get("scheduler.idleTime"));
} catch (ex) {
if (ex.result != Cr.NS_ERROR_FAILURE) {
throw ex;
@@ -384,16 +386,19 @@ SyncScheduler.prototype = {
});
break;
case "captive-portal-login-success":
this.shouldSyncWhenLinkComesUp = false;
this._log.debug("Captive portal login success. Scheduling a sync.");
CommonUtils.nextTick(() => {
this.scheduleNextSync(3000);
});
+ case "sleep_notification":
+ this._log.debug("Going to sleep, doing a quick sync.");
+ this.scheduleNextSync(0, ["tabs"], "sleep");
break;
}
},
adjustSyncInterval: function adjustSyncInterval() {
if (Status.eol) {
this._log.debug("Server status is EOL; using eolInterval.");
this.syncInterval = this.eolInterval;
@@ -485,39 +490,41 @@ SyncScheduler.prototype = {
this.scheduleNextSync(wait);
},
/**
* Call sync() if Master Password is not locked.
*
* Otherwise, reschedule a sync for later.
*/
- syncIfMPUnlocked: function syncIfMPUnlocked() {
+ syncIfMPUnlocked(engines, why) {
// No point if we got kicked out by the master password dialog.
if (Status.login == MASTER_PASSWORD_LOCKED &&
Utils.mpLocked()) {
this._log.debug("Not initiating sync: Login status is " + Status.login);
// If we're not syncing now, we need to schedule the next one.
this._log.trace("Scheduling a sync at MASTER_PASSWORD_LOCKED_RETRY_INTERVAL");
this.scheduleAtInterval(MASTER_PASSWORD_LOCKED_RETRY_INTERVAL);
return;
}
if (!Async.isAppReady()) {
this._log.debug("Not initiating sync: app is shutting down");
return;
}
- CommonUtils.nextTick(this.service.sync, this.service);
+ Services.tm.dispatchToMainThread(() => {
+ this.service.sync({engines, why});
+ });
},
/**
* Set a timer for the next sync
*/
- scheduleNextSync: function scheduleNextSync(interval) {
+ scheduleNextSync(interval, engines = null, why = null) {
// If no interval was specified, use the current sync interval.
if (interval == null) {
interval = this.syncInterval;
}
// Ensure the interval is set to no less than the backoff.
if (Status.backoffInterval && interval < Status.backoffInterval) {
this._log.trace("Requested interval " + interval +
@@ -538,22 +545,23 @@ SyncScheduler.prototype = {
interval + " ms.");
return;
}
}
// Start the sync right away if we're already late.
if (interval <= 0) {
this._log.trace("Requested sync should happen right away.");
- this.syncIfMPUnlocked();
+ this.syncIfMPUnlocked(engines, why);
return;
}
this._log.debug("Next sync in " + interval + " ms.");
- CommonUtils.namedTimer(this.syncIfMPUnlocked, interval, this, "syncTimer");
+ CommonUtils.namedTimer(() => { this.syncIfMPUnlocked(engines, why); },
+ interval, this, "syncTimer");
// Save the next sync time in-case sync is disabled (logout/offline/etc.)
this.nextSync = Date.now() + interval;
},
/**
* Incorporates the backoff/retry logic used in error handling and elective
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -903,18 +903,18 @@ Sync11Service.prototype = {
this.serverConfiguration = configResponse.obj;
}
this._log.trace("info/configuration for this server", this.serverConfiguration);
return true;
},
// Stuff we need to do after login, before we can really do
// anything (e.g. key setup).
- async _remoteSetup(infoResponse) {
- if (!(await this._fetchServerConfiguration())) {
+ async _remoteSetup(infoResponse, fetchConfig = true) {
+ if (fetchConfig && !(await this._fetchServerConfiguration())) {
return false;
}
this._log.debug("Fetching global metadata record");
let meta = await this.recordManager.get(this.metaURL);
// Checking modified time of the meta record.
if (infoResponse &&
@@ -1107,17 +1107,17 @@ Sync11Service.prototype = {
async _lockedSync(engineNamesToSync, why) {
return this._lock("service.js: sync",
this._notify("sync", JSON.stringify({why}), async function onNotify() {
let histogram = Services.telemetry.getHistogramById("WEAVE_START_COUNT");
histogram.add(1);
let synchronizer = new EngineSynchronizer(this);
- await synchronizer.sync(engineNamesToSync); // Might throw!
+ await synchronizer.sync(engineNamesToSync, why); // Might throw!
histogram = Services.telemetry.getHistogramById("WEAVE_COMPLETE_SUCCESS_COUNT");
histogram.add(1);
// We successfully synchronized.
// Check if the identity wants to pre-fetch a migration sentinel from
// the server.
// If we have no clusterURL, we are probably doing a node reassignment
--- a/services/sync/modules/stages/enginesync.js
+++ b/services/sync/modules/stages/enginesync.js
@@ -26,17 +26,18 @@ XPCOMUtils.defineLazyModuleGetter(this,
this.EngineSynchronizer = function EngineSynchronizer(service) {
this._log = Log.repository.getLogger("Sync.Synchronizer");
this._log.level = Log.Level[Svc.Prefs.get("log.logger.synchronizer")];
this.service = service;
};
EngineSynchronizer.prototype = {
- async sync(engineNamesToSync) {
+ async sync(engineNamesToSync, why) {
+ let fastSync = why && why == "sleep";
let startTime = Date.now();
this.service.status.resetSync();
// Make sure we should sync or record why we shouldn't.
let reason = this.service._checkSync();
if (reason) {
if (reason == kSyncNetworkOffline) {
@@ -70,27 +71,29 @@ EngineSynchronizer.prototype = {
// Figure out what the last modified time is for each collection
let info = await this.service._fetchInfo(infoURL);
// Convert the response to an object and read out the modified times
for (let engine of [this.service.clientsEngine].concat(engineManager.getAll())) {
engine.lastModified = info.obj[engine.name] || 0;
}
- if (!(await this.service._remoteSetup(info))) {
+ if (!(await this.service._remoteSetup(info, !fastSync))) {
throw new Error("Aborting sync, remote setup failed");
}
- // Make sure we have an up-to-date list of clients before sending commands
- this._log.debug("Refreshing client list.");
- if (!(await this._syncEngine(this.service.clientsEngine))) {
- // Clients is an engine like any other; it can fail with a 401,
- // and we can elect to abort the sync.
- this._log.warn("Client engine sync failed. Aborting.");
- return;
+ if (!fastSync) {
+ // Make sure we have an up-to-date list of clients before sending commands
+ this._log.debug("Refreshing client list.");
+ if (!(await this._syncEngine(this.service.clientsEngine))) {
+ // Clients is an engine like any other; it can fail with a 401,
+ // and we can elect to abort the sync.
+ this._log.warn("Client engine sync failed. Aborting.");
+ return;
+ }
}
// We only honor the "hint" of what engines to Sync if this isn't
// a first sync.
let allowEnginesHint = false;
// Wipe data in the desired direction if necessary
switch (Svc.Prefs.get("firstSync")) {
case "resetClient":
@@ -102,17 +105,17 @@ EngineSynchronizer.prototype = {
case "wipeRemote":
await this.service.wipeRemote(engineManager.enabledEngineNames);
break;
default:
allowEnginesHint = true;
break;
}
- if (this.service.clientsEngine.localCommands) {
+ if (!fastSync && this.service.clientsEngine.localCommands) {
try {
if (!(await this.service.clientsEngine.processIncomingCommands())) {
this.service.status.sync = ABORT_SYNC_COMMAND;
throw new Error("Processed command aborted sync.");
}
// Repeat remoteSetup in-case the commands forced us to reset
if (!(await this.service._remoteSetup(info))) {
@@ -174,17 +177,19 @@ EngineSynchronizer.prototype = {
await this.service.uploadMetaGlobal(meta);
delete meta.isNew;
delete meta.changed;
} catch (error) {
this._log.error("Unable to upload meta/global. Leaving marked as new.");
}
}
- await Doctor.consult(enginesToValidate);
+ if (!fastSync) {
+ await Doctor.consult(enginesToValidate);
+ }
// If there were no sync engine failures
if (this.service.status.service != SYNC_FAILED_PARTIAL) {
Svc.Prefs.set("lastSync", new Date().toString());
this.service.status.sync = SYNC_SUCCEEDED;
}
} finally {
Svc.Prefs.reset("firstSync");