Bug 1331629 - Reuse the same kinto client instance accross syncs (r=mgoodwin)
authorMathieu Leplatre <mathieu@mozilla.com>
Mon, 06 Feb 2017 10:35:26 +0100
changeset 340996 dd195a6fabaf558c0089488af184265041ffc34b
parent 340995 ed583f4e5054748738c92e466fd8d2650c8d0888
child 340997 f0f5fdf96c80ed8ef55c7c8152976cd8c41a072a
push id86615
push userkwierso@gmail.com
push dateTue, 07 Feb 2017 01:52:08 +0000
treeherdermozilla-inbound@f0453084d86e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmgoodwin
bugs1331629
milestone54.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 1331629 - Reuse the same kinto client instance accross syncs (r=mgoodwin) MozReview-Commit-ID: F8Fhy9TzPTG
services/common/blocklist-clients.js
--- a/services/common/blocklist-clients.js
+++ b/services/common/blocklist-clients.js
@@ -63,64 +63,53 @@ function mergeChanges(collection, localR
   return Object.values(records)
     // Filter out deleted records.
     .filter((record) => record.deleted != true)
     // Sort list by record id.
     .sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
 }
 
 
-function fetchCollectionMetadata(collection) {
-  const client = new KintoHttpClient(collection.api.remote);
+function fetchCollectionMetadata(remote, collection) {
+  const client = new KintoHttpClient(remote);
   return client.bucket(collection.bucket).collection(collection.name).getData()
     .then(result => {
       return result.signature;
     });
 }
 
-function fetchRemoteCollection(collection) {
-  const client = new KintoHttpClient(collection.api.remote);
+function fetchRemoteCollection(remote, collection) {
+  const client = new KintoHttpClient(remote);
   return client.bucket(collection.bucket)
            .collection(collection.name)
            .listRecords({sort: "id"});
 }
 
-/**
- * Helper to instantiate a Kinto client based on preferences for remote server
- * URL and bucket name. It uses the `FirefoxAdapter` which relies on SQLite to
- * persist the local DB.
- */
-function kintoClient(connection, bucket) {
-  const remote = Services.prefs.getCharPref(PREF_SETTINGS_SERVER);
-
-  const config = {
-    remote,
-    bucket,
-    adapter: FirefoxAdapter,
-    adapterOptions: {sqliteHandle: connection},
-  };
-
-  return new Kinto(config);
-}
-
 
 class BlocklistClient {
 
   constructor(collectionName, lastCheckTimePref, processCallback, bucketName, signerName) {
     this.collectionName = collectionName;
     this.lastCheckTimePref = lastCheckTimePref;
     this.processCallback = processCallback;
     this.bucketName = bucketName;
     this.signerName = signerName;
+
+    this._kinto = new Kinto({
+      bucket: bucketName,
+      adapter: FirefoxAdapter,
+    });
   }
 
-  validateCollectionSignature(payload, collection, ignoreLocal) {
+  validateCollectionSignature(remote, payload, collection, options = {}) {
+    const {ignoreLocal} = options;
+
     return Task.spawn((function* () {
       // this is a content-signature field from an autograph response.
-      const {x5u, signature} = yield fetchCollectionMetadata(collection);
+      const {x5u, signature} = yield fetchCollectionMetadata(remote, collection);
       const certChain = yield fetch(x5u).then((res) => res.text());
 
       const verifier = Cc["@mozilla.org/security/contentsignatureverifier;1"]
                          .createInstance(Ci.nsIContentSignatureVerifier);
 
       let toSerialize;
       if (ignoreLocal) {
         toSerialize = {
@@ -152,57 +141,63 @@ class BlocklistClient {
    * Synchronize from Kinto server, if necessary.
    *
    * @param {int}  lastModified the lastModified date (on the server) for
                                 the remote collection.
    * @param {Date} serverTime   the current date return by the server.
    * @return {Promise}          which rejects on sync or process failure.
    */
   maybeSync(lastModified, serverTime) {
-    const opts = {};
-    const enforceCollectionSigning =
+    const remote = Services.prefs.getCharPref(PREF_SETTINGS_SERVER);
+    let enforceCollectionSigning =
       Services.prefs.getBoolPref(PREF_BLOCKLIST_ENFORCE_SIGNING);
 
     // if there is a signerName and collection signing is enforced, add a
     // hook for incoming changes that validates the signature
+    let hooks;
     if (this.signerName && enforceCollectionSigning) {
-      opts.hooks = {
-        "incoming-changes": [this.validateCollectionSignature.bind(this)]
+      hooks = {
+        "incoming-changes": [(payload, collection) => {
+          return this.validateCollectionSignature(remote, payload, collection);
+        }]
       }
     }
 
-
     return Task.spawn((function* syncCollection() {
-      let connection;
+      let sqliteHandle;
       try {
-        connection = yield FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
-        const db = kintoClient(connection, this.bucketName);
-        const collection = db.collection(this.collectionName, opts);
+        // Synchronize remote data into a local Sqlite DB.
+        sqliteHandle = yield FirefoxAdapter.openConnection({path: KINTO_STORAGE_PATH});
+        const options = {
+          hooks,
+          adapterOptions: {sqliteHandle},
+        };
+        const collection = this._kinto.collection(this.collectionName, options);
 
         const collectionLastModified = yield collection.db.getLastModified();
         // If the data is up to date, there's no need to sync. We still need
         // to record the fact that a check happened.
         if (lastModified <= collectionLastModified) {
           this.updateLastCheck(serverTime);
           return;
         }
         // Fetch changes from server.
         try {
-          const {ok} = yield collection.sync();
+          const {ok} = yield collection.sync({remote});
           if (!ok) {
             throw new Error("Sync failed");
           }
         } catch (e) {
           if (e.message == INVALID_SIGNATURE) {
             // if sync fails with a signature error, it's likely that our
             // local data has been modified in some way.
             // We will attempt to fix this by retrieving the whole
             // remote collection.
-            const payload = yield fetchRemoteCollection(collection);
-            yield this.validateCollectionSignature(payload, collection, true);
+            const payload = yield fetchRemoteCollection(remote, collection);
+            yield this.validateCollectionSignature(remote, payload, collection, {ignoreLocal: true});
             // if the signature is good (we haven't thrown), and the remote
             // last_modified is newer than the local last_modified, replace the
             // local data
             const localLastModified = yield collection.db.getLastModified();
             if (payload.last_modified >= localLastModified) {
               yield collection.clear();
               yield collection.loadDump(payload.data);
             }
@@ -213,17 +208,17 @@ class BlocklistClient {
         // Read local collection of records.
         const {data} = yield collection.list();
 
         yield this.processCallback(data);
 
         // Track last update.
         this.updateLastCheck(serverTime);
       } finally {
-        yield connection.close();
+        yield sqliteHandle.close();
       }
     }).bind(this));
   }
 
   /**
    * Save last time server was checked in users prefs.
    *
    * @param {Date} serverTime   the current date return by server.