Bug 1486980 - Fetch tippytop collection in one bulk with getAll() r=asuth,nanj
authorMathieu Leplatre <mathieu@mozilla.com>
Thu, 18 Oct 2018 17:02:24 +0000
changeset 500454 9a05c09af3770321c9d3acd89204ac62f3fb1878
parent 500453 5441249fe31416707df434bc6f886d923543de46
child 500455 7b439431eb95b33e5619ac6c4cd07de948afe7b6
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth, nanj
bugs1486980
milestone64.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 1486980 - Fetch tippytop collection in one bulk with getAll() r=asuth,nanj Iterating an IDB cursor generates a lot of overhead. In this patch, we upgrade the kinto-offline to its latest version that uses `getAll()` when no filter is specified. We leverage this change in tippytop, by omitting the filter and filtering the whole list there instead. This allowed us to go from ~1sec on a 1000 entries to ~70ms. Differential Revision: https://phabricator.services.mozilla.com/D9076
services/common/kinto-offline-client.js
--- a/services/common/kinto-offline-client.js
+++ b/services/common/kinto-offline-client.js
@@ -28,17 +28,17 @@
 //
 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1394556#c3 for
 // more details.
 const global = this;
 
 var EXPORTED_SYMBOLS = ["Kinto"];
 
 /*
- * Version 12.1.1 - 6950498
+ * Version 12.2.0 - 266e100
  */
 
 (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Kinto = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
 /*
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -481,47 +481,64 @@ const cursorHandlers = {
  * @param  {String}           cid        The collection id (ie. `{bid}/{cid}`)
  * @param  {IDBStore}         store      The IDB store.
  * @param  {Object}           filters    Filter the records by field.
  * @param  {Function}         done       The operation completion handler.
  * @return {IDBRequest}
  */
 
 function createListRequest(cid, store, filters, done) {
+  const filterFields = Object.keys(filters); // If no filters, get all results in one bulk.
+
+  if (filterFields.length == 0) {
+    const request = store.index("cid").getAll(IDBKeyRange.only(cid));
+    request.onsuccess = event => done(event.target.result);
+    return request;
+  }
   // Introspect filters and check if they leverage an indexed field.
-  const indexField = Object.keys(filters).find(field => {
+  const indexField = filterFields.find(field => {
     return INDEXED_FIELDS.includes(field);
   });
 
   if (!indexField) {
-    // Get all records for this collection (ie. cid)
+    // Iterate on all records for this collection (ie. cid)
     const request = store.index("cid").openCursor(IDBKeyRange.only(cid));
     request.onsuccess = cursorHandlers.all(filters, done);
     return request;
   } // If `indexField` was used already, don't filter again.
 
 
   const remainingFilters = (0, _utils.omitKeys)(filters, indexField); // value specified in the filter (eg. `filters: { _status: ["created", "updated"] }`)
 
-  const value = filters[indexField]; // WHERE IN equivalent clause
-
+  const value = filters[indexField];
+  // For the "id" field, use the primary key.
+  const indexStore = indexField == "id" ? store : store.index(indexField);
+
+  // WHERE IN equivalent clause
   if (Array.isArray(value)) {
     if (value.length === 0) {
       return done([]);
     }
 
     const values = value.map(i => [cid, i]).sort();
     const range = IDBKeyRange.bound(values[0], values[values.length - 1]);
-    const request = store.index(indexField).openCursor(range);
+    const request = indexStore.openCursor(range);
     request.onsuccess = cursorHandlers.in(values, remainingFilters, done);
     return request;
+  }
+
+  // If no filters on custom attribute, get all results in one bulk.
+  if (remainingFilters.length == 0) {
+    const request = indexStore.getAll(IDBKeyRange.only([cid, value]));
+    request.onsuccess = event => done(event.target.result);
+    return request;
   } // WHERE field = value clause
 
 
-  const request = store.index(indexField).openCursor(IDBKeyRange.only([cid, value]));
+  const request = indexStore.openCursor(IDBKeyRange.only([cid, value]));
   request.onsuccess = cursorHandlers.all(remainingFilters, done);
   return request;
 }
 /**
  * IndexedDB adapter.
  *
  * This adapter doesn't support any options.
  */
@@ -572,19 +589,17 @@ class IDB extends _base.default {
       onupgradeneeded: event => {
         const db = event.target.result; // Records store
 
         const recordsStore = db.createObjectStore("records", {
           keyPath: ["_cid", "id"]
         }); // An index to obtain all the records in a collection.
 
         recordsStore.createIndex("cid", "_cid"); // Here we create indices for every known field in records by collection.
-        // Record id (generated by IdSchema, UUID by default)
-
-        recordsStore.createIndex("id", ["_cid", "id"]); // Local record status ("synced", "created", "updated", "deleted")
+        // Local record status ("synced", "created", "updated", "deleted")
 
         recordsStore.createIndex("_status", ["_cid", "_status"]); // Last modified field
 
         recordsStore.createIndex("last_modified", ["_cid", "last_modified"]); // Timestamps store
 
         db.createObjectStore("timestamps", {
           keyPath: "cid"
         });