Bug 1416320 - Do a quick sync before going to sleep. r=markh
authorEdouard Oger <eoger@fastmail.com>
Fri, 10 Nov 2017 14:09:44 -0500
changeset 392820 8b6a35a99b40aa3ae0ed8d1f92badb5437adf623
parent 392819 6faac9d0daff124559c45c3820c79d8f1b412450
child 392821 d65aba303d138b832145027f03904c70624b967b
push id55719
push usereoger@mozilla.com
push dateTue, 21 Nov 2017 05:40:37 +0000
treeherderautoland@8b6a35a99b40 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarkh
bugs1416320
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1416320 - Do a quick sync before going to sleep. r=markh MozReview-Commit-ID: I36uvEFlEz5
services/sync/modules/policies.js
services/sync/modules/service.js
services/sync/modules/stages/enginesync.js
--- 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");