Bug 1462803: Remove obsolete telemetry measurements from XPIProvider. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Fri, 18 May 2018 16:36:04 -0700
changeset 797407 9edb251d64b5813bfec517d0aa5c468ae1718240
parent 797406 23c3b4137f92d77f96f606345d63e658e9fb52a5
child 797419 6d668c2a06bd9803d101c71d8ce3676594e44216
push id110469
push usermaglione.k@gmail.com
push dateFri, 18 May 2018 23:37:23 +0000
reviewersaswan
bugs1462803
milestone62.0a1
Bug 1462803: Remove obsolete telemetry measurements from XPIProvider. r?aswan MozReview-Commit-ID: 9YqtsaGYkNe
toolkit/components/telemetry/docs/data/main-ping.rst
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/internal/XPIDatabase.jsm
toolkit/mozapps/extensions/internal/XPIProvider.jsm
--- a/toolkit/components/telemetry/docs/data/main-ping.rst
+++ b/toolkit/components/telemetry/docs/data/main-ping.rst
@@ -617,22 +617,19 @@ This section contains per add-on telemet
 
 Structure:
 
 .. code-block:: js
 
     "addonDetails": {
       "XPI": {
         "adbhelper@mozilla.org": {
-          "scan_items": 24,
-          "scan_MS": 3,
           "location": "app-profile",
           "name": "ADB Helper",
           "creator": "Mozilla & Android Open Source Project",
-          "startup_MS": 30
         },
         ...
       },
       ...
     }
 
 slowSQL
 -------
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -2960,20 +2960,20 @@ var AddonManagerPrivate = {
   // timer.done() is called
   simpleTimer(aName) {
     let startTime = Cu.now();
     return {
       done: () => this.recordSimpleMeasure(aName, Math.round(Cu.now() - startTime))
     };
   },
 
-  recordTiming(name, task) {
+  async recordTiming(name, task) {
     let timer = this.simpleTimer(name);
     try {
-      return task();
+      return await task();
     } finally {
       timer.done();
     }
   },
 
   /**
    * Helper to call update listeners when no update is available.
    *
--- a/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIDatabase.jsm
@@ -70,19 +70,16 @@ const ZipReader = Components.Constructor
 
 // Create a new logger for use by the Addons XPI Provider Utils
 // (Requires AddonManager.jsm)
 var logger = Log.repository.getLogger(LOGGER_ID);
 
 const KEY_PROFILEDIR                  = "ProfD";
 const FILE_JSON_DB                    = "extensions.json";
 
-// The last version of DB_SCHEMA implemented in SQLITE
-const LAST_SQLITE_DB_SCHEMA           = 14;
-
 const PREF_DB_SCHEMA                  = "extensions.databaseSchema";
 const PREF_EM_AUTO_DISABLED_SCOPES    = "extensions.autoDisableScopes";
 const PREF_EM_EXTENSION_FORMAT        = "extensions.";
 const PREF_PENDING_OPERATIONS         = "extensions.pendingOperations";
 const PREF_XPI_PERMISSIONS_BRANCH     = "xpinstall.";
 const PREF_XPI_SIGNATURES_DEV_ROOT    = "xpinstall.signatures.dev-root";
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
@@ -1420,55 +1417,55 @@ this.XPIDatabase = {
    * until it finishes.
    *
    * @param {boolean} aRebuildOnError
    *        A boolean indicating whether add-on information should be loaded
    *        from the install locations if the database needs to be rebuilt.
    *        (if false, caller is XPIProvider.checkForChanges() which will rebuild)
    */
   syncLoadDB(aRebuildOnError) {
-    logger.warn(new Error("Synchronously loading the add-ons database"));
+    let err = new Error("Synchronously loading the add-ons database");
+    logger.debug(err);
+    AddonManagerPrivate.recordSimpleMeasure("XPIDB_sync_stack", Log.stackTrace(err));
     try {
       this.syncLoadingDB = true;
       XPIInternal.awaitPromise(this.asyncLoadDB(aRebuildOnError));
     } finally {
       this.syncLoadingDB = false;
     }
   },
 
+  _recordStartupError(reason) {
+    AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", reason);
+  },
+
   /**
    * Parse loaded data, reconstructing the database if the loaded data is not valid
    *
    * @param {string} aData
    *        The stringified add-on JSON to parse.
    * @param {boolean} aRebuildOnError
    *        If true, synchronously reconstruct the database from installed add-ons
    */
   async parseDB(aData, aRebuildOnError) {
-    let parseTimer = AddonManagerPrivate.simpleTimer("XPIDB_parseDB_MS");
     try {
+      let parseTimer = AddonManagerPrivate.simpleTimer("XPIDB_parseDB_MS");
       let inputAddons = JSON.parse(aData);
 
       if (!("schemaVersion" in inputAddons) || !("addons" in inputAddons)) {
-        parseTimer.done();
-
-        // Content of JSON file is bad, need to rebuild from scratch
-        logger.error("bad JSON file contents");
-        AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "badJSON");
-
-        this.timeRebuildDatabase("XPIDB_rebuildBadJSON_MS", aRebuildOnError);
-        return;
+        let error = new Error("Bad JSON file contents");
+        error.rebuildReason = "XPIDB_rebuildBadJSON_MS";
+        throw error;
       }
 
       if (inputAddons.schemaVersion != DB_SCHEMA) {
         // For now, we assume compatibility for JSON data with a
         // mismatched schema version, though we throw away any fields we
         // don't know about (bug 902956)
-        AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError",
-                                                `schemaMismatch-${inputAddons.schemaVersion}`);
+        this._recordStartupError(`schemaMismatch-${inputAddons.schemaVersion}`);
         logger.debug(`JSON schema mismatch: expected ${DB_SCHEMA}, actual ${inputAddons.schemaVersion}`);
       }
 
       let forEach = this.syncLoadingDB ? arrayForEach : idleForEach;
 
       // If we got here, we probably have good data
       // Make AddonInternal instances from the loaded data and save them
       let addonDB = new Map();
@@ -1490,73 +1487,29 @@ this.XPIDatabase = {
         }
       });
 
       parseTimer.done();
       this.addonDB = addonDB;
       logger.debug("Successfully read XPI database");
       this.initialized = true;
     } catch (e) {
-      // If we catch and log a SyntaxError from the JSON
-      // parser, the xpcshell test harness fails the test for us: bug 870828
-      parseTimer.done();
-
       if (e.name == "SyntaxError") {
         logger.error("Syntax error parsing saved XPI JSON data");
-        AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "syntax");
+        this._recordStartupError("syntax");
       } else {
         logger.error("Failed to load XPI JSON data from profile", e);
-        AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "other");
+        this._recordStartupError("other");
       }
 
-      this.timeRebuildDatabase("XPIDB_rebuildReadFailed_MS", aRebuildOnError);
+      this.timeRebuildDatabase(e.rebuildReason || "XPIDB_rebuildReadFailed_MS",
+                               aRebuildOnError);
     }
   },
 
-  timeRebuildDatabase(timerName, rebuildOnError) {
-    AddonManagerPrivate.recordTiming(timerName, () => {
-      this.rebuildDatabase(rebuildOnError);
-    });
-  },
-
-  /**
-   * Upgrade database from earlier (sqlite or RDF) version if available
-   *
-   * @param {boolean} aRebuildOnError
-   *        If true, synchronously reconstruct the database from installed add-ons
-   */
-  upgradeDB(aRebuildOnError) {
-    let schemaVersion = Services.prefs.getIntPref(PREF_DB_SCHEMA, 0);
-    if (schemaVersion > LAST_SQLITE_DB_SCHEMA) {
-      // we've upgraded before but the JSON file is gone, fall through
-      // and rebuild from scratch
-      AddonManagerPrivate.recordSimpleMeasure("XPIDB_startupError", "dbMissing");
-    }
-
-    this.timeRebuildDatabase("XPIDB_upgradeDB_MS", aRebuildOnError);
-  },
-
-  /**
-   * Reconstruct when the DB file exists but is unreadable
-   * (for example because read permission is denied)
-   *
-   * @param {Error} aError
-   *        The error that triggered the rebuild.
-   * @param {boolean} aRebuildOnError
-   *        If true, synchronously reconstruct the database from installed add-ons
-   */
-  rebuildUnreadableDB(aError, aRebuildOnError) {
-    logger.warn(`Extensions database ${this.jsonFile.path} exists but is not readable; rebuilding`, aError);
-    // Remember the error message until we try and write at least once, so
-    // we know at shutdown time that there was a problem
-    this._loadError = aError;
-
-    this.timeRebuildDatabase("XPIDB_rebuildUnreadableDB_MS", aRebuildOnError);
-  },
-
   async maybeIdleDispatch() {
     if (!this.syncLoadingDB) {
       await promiseIdleSlice();
     }
   },
 
   /**
    * Open and read the XPI database asynchronously, upgrading if
@@ -1572,55 +1525,55 @@ this.XPIDatabase = {
    *        this.addonDB; never rejects.
    */
   asyncLoadDB(aRebuildOnError = true) {
     // Already started (and possibly finished) loading
     if (this._dbPromise) {
       return this._dbPromise;
     }
 
-    logger.debug("Starting async load of XPI database " + this.jsonFile.path);
-    AddonManagerPrivate.recordSimpleMeasure("XPIDB_async_load", XPIProvider.runPhase);
-    let readOptions = {
-      outExecutionDuration: 0
-    };
+    logger.debug(`Starting async load of XPI database ${this.jsonFile.path}`);
     this._dbPromise = (async () => {
       try {
-        let byteArray = await OS.File.read(this.jsonFile.path, null, readOptions);
-
-        logger.debug(`Async JSON file read took ${readOptions.outExecutionDuration} MS`);
-        AddonManagerPrivate.recordSimpleMeasure("XPIDB_asyncRead_MS",
-                                                readOptions.outExecutionDuration);
+        let byteArray = await OS.File.read(this.jsonFile.path, null);
 
         logger.debug("Finished async read of XPI database, parsing...");
         await this.maybeIdleDispatch();
-        let text = AddonManagerPrivate.recordTiming(
-          "XPIDB_decode_MS",
-          () => new TextDecoder().decode(byteArray));
+        let text = new TextDecoder().decode(byteArray);
 
         await this.maybeIdleDispatch();
         await this.parseDB(text, true);
       } catch (error) {
         if (error.becauseNoSuchFile) {
-          this.upgradeDB(aRebuildOnError);
+          if (Services.prefs.getIntPref(PREF_DB_SCHEMA, 0)) {
+            this._recordStartupError("dbMissing");
+          }
         } else {
-          // it's there but unreadable
-          this.rebuildUnreadableDB(error, aRebuildOnError);
+          logger.warn(`Extensions database ${this.jsonFile.path} exists but is not readable; rebuilding`,
+                      error);
+          this._loadError = error;
         }
+        this.timeRebuildDatabase("XPIDB_rebuildUnreadableDB_MS", aRebuildOnError);
       }
       return this.addonDB;
     })();
 
     this._dbPromise.then(() => {
-      Services.obs.notifyObservers(this.addonDB, "xpi-database-loaded");
+      Services.obs.notifyObservers(null, "xpi-database-loaded");
     });
 
     return this._dbPromise;
   },
 
+  timeRebuildDatabase(timerName, rebuildOnError) {
+    AddonManagerPrivate.recordTiming(timerName, () => {
+      return this.rebuildDatabase(rebuildOnError);
+    });
+  },
+
   /**
    * Rebuild the database from addon install directories.
    *
    * @param {boolean} aRebuildOnError
    *        A boolean indicating whether add-on information should be loaded
    *        from the install locations if the database needs to be rebuilt.
    *        (if false, caller is XPIProvider.checkForChanges() which will rebuild)
    */
@@ -1891,17 +1844,16 @@ this.XPIDatabase = {
   getAddonsByType(...aTypes) {
     if (!this.addonDB) {
       // jank-tastic! Must synchronously load DB if the theme switches from
       // an XPI theme to a lightweight theme before the DB has loaded,
       // because we're called from sync XPIProvider.addonChanged
       logger.warn(`Synchronous load of XPI database due to ` +
                   `getAddonsByType([${aTypes.join(", ")}]) ` +
                   `Stack: ${Error().stack}`);
-      AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_byType", XPIProvider.runPhase);
       this.syncLoadDB(true);
     }
 
     return _filterDB(this.addonDB, aAddon => aTypes.includes(aAddon.type));
   },
 
   /**
    * Asynchronously gets all add-ons with pending operations.
@@ -2104,22 +2056,16 @@ this.XPIDatabase = {
    * @param {AddonInternal} aAddon
    *        AddonInternal to add
    * @param {string} aPath
    *        The file path of the add-on
    * @returns {AddonInternal}
    *        the AddonInternal that was added to the database
    */
   addToDatabase(aAddon, aPath) {
-    if (!this.addonDB) {
-      AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_addMetadata",
-          XPIProvider.runPhase);
-      this.syncLoadDB(false);
-    }
-
     aAddon.addedToDatabase();
     aAddon.path = aPath;
     this.addonDB.set(aAddon._key, aAddon);
     if (aAddon.visible) {
       this.makeAddonVisible(aAddon);
     }
 
     this.saveChanges();
@@ -2281,24 +2227,16 @@ this.XPIDatabase = {
     aAddon.active = aActive;
     this.saveChanges();
   },
 
   /**
    * Synchronously calculates and updates all the active flags in the database.
    */
   updateActiveAddons() {
-    if (!this.addonDB) {
-      logger.warn("updateActiveAddons called when DB isn't loaded");
-      // force the DB to load
-      AddonManagerPrivate.recordSimpleMeasure("XPIDB_lateOpen_updateActive",
-          XPIProvider.runPhase);
-      this.syncLoadDB(true);
-    }
-
     logger.debug("Updating add-on states");
     for (let [, addon] of this.addonDB) {
       let newActive = (addon.visible && !addon.disabled && !addon.pendingUninstall);
       if (newActive != addon.active) {
         addon.active = newActive;
         this.saveChanges();
       }
     }
@@ -2445,32 +2383,25 @@ this.XPIDatabase = {
           logger.debug("updateAddonRepositoryData got info for " + addon.id);
           addon._repositoryAddon = aRepoAddon;
           this.updateAddonDisabledState(addon);
         }
       })));
   },
 
   /**
-   * Record a bit of per-addon telemetry.
-   *
-   * Yes, this description is extremely helpful. How dare you question its
-   * utility?
+   * Adds the add-on's name and creator to the telemetry payload.
    *
    * @param {AddonInternal} aAddon
    *        The addon to record
    */
   recordAddonTelemetry(aAddon) {
     let locale = aAddon.defaultLocale;
-    if (locale) {
-      if (locale.name)
-        XPIProvider.setTelemetry(aAddon.id, "name", locale.name);
-      if (locale.creator)
-        XPIProvider.setTelemetry(aAddon.id, "creator", locale.creator);
-    }
+    XPIProvider.addTelemetry(aAddon.id, {name: locale.name,
+                                         creator: locale.creator});
   },
 };
 
 this.XPIDatabaseReconcile = {
   /**
    * Returns a map of ID -> add-on. When the same add-on ID exists in multiple
    * install locations the highest priority location is chosen.
    *
@@ -2811,23 +2742,16 @@ this.XPIDatabaseReconcile = {
    *        The updated AddonInternal object for the add-on, if one
    *        could be created.
    */
   updateExistingAddon(oldAddon, xpiState, newAddon, aUpdateCompatibility, aSchemaChange) {
     XPIDatabase.recordAddonTelemetry(oldAddon);
 
     let installLocation = oldAddon.location;
 
-    if (xpiState.mtime < oldAddon.updateDate) {
-      XPIProvider.setTelemetry(oldAddon.id, "olderFile", {
-        mtime: xpiState.mtime,
-        oldtime: oldAddon.updateDate
-      });
-    }
-
     // Update the add-on's database metadata from on-disk metadata if:
     //
     //  a) The add-on was staged for install in the last session,
     //  b) The add-on has been modified since the last session, or,
     //  c) The app has been updated since the last session, and the
     //     add-on is part of the application bundle (and has therefore
     //     likely been replaced in the update process).
     if (newAddon ||
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -134,24 +134,16 @@ const BOOTSTRAP_REASONS = {
 
 const ALL_EXTERNAL_TYPES = new Set([
   "dictionary",
   "extension",
   "locale",
   "theme",
 ]);
 
-// Keep track of where we are in startup for telemetry
-// event happened during XPIDatabase.startup()
-const XPI_STARTING = "XPIStarting";
-// event happened after startup() but before the final-ui-startup event
-const XPI_BEFORE_UI_STARTUP = "BeforeFinalUIStartup";
-// event happened after final-ui-startup
-const XPI_AFTER_UI_STARTUP = "AfterFinalUIStartup";
-
 var gGlobalScope = this;
 
 /**
  * Valid IDs fit this pattern.
  */
 var gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]*\@[a-z0-9-\._]+)$/i;
 
 ChromeUtils.import("resource://gre/modules/Log.jsm");
@@ -673,17 +665,17 @@ class XPIStateLocation extends Map {
    *        The DBAddon to add.
    */
   addAddon(addon) {
     logger.debug("XPIStates adding add-on ${id} in ${location}: ${path}", addon);
 
     let xpiState = this._addState(addon.id, {file: addon._sourceBundle});
     xpiState.syncWithDB(addon, true);
 
-    XPIProvider.setTelemetry(addon.id, "location", this.name);
+    XPIProvider.addTelemetry(addon.id, {location: this.name});
   }
 
   /**
    * Remove the XPIState for an add-on and save the new state.
    *
    * @param {string} aId
    *        The ID of the add-on.
    */
@@ -1273,17 +1265,17 @@ var XPIStates = {
 
           if (addonChanged) {
             changed = true;
             logger.debug("Changed add-on ${id} in ${loc}", {id, loc: loc.name});
           } else {
             logger.debug("Existing add-on ${id} in ${loc}", {id, loc: loc.name});
           }
         }
-        XPIProvider.setTelemetry(id, "location", loc.name);
+        XPIProvider.addTelemetry(id, {location: loc.name});
       }
 
       // Anything left behind in oldState was removed from the file system.
       for (let id of knownIds) {
         loc.delete(id);
         changed = true;
       }
     }
@@ -1509,17 +1501,16 @@ class BootstrapScope {
    * @returns {any}
    *        The return value of the bootstrap method.
    */
   async callBootstrapMethod(aMethod, aReason, aExtraParams = {}) {
     let {addon, runInSafeMode} = this;
     if (Services.appinfo.inSafeMode && !runInSafeMode)
       return null;
 
-    let timeStart = new Date();
     if (addon.type == "extension" && aMethod == "startup") {
       logger.debug(`Registering manifest for ${this.file.path}`);
       Components.manager.addBootstrappedManifestLocation(this.file);
     }
 
     try {
       if (!this.scope) {
         this.loadBootstrapScope(aReason);
@@ -1610,17 +1601,16 @@ class BootstrapScope {
         }
       }
 
       if (addon.type == "extension" && aMethod == "shutdown" &&
           aReason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
         logger.debug(`Removing manifest for ${this.file.path}`);
         Components.manager.removeBootstrappedManifestLocation(this.file);
       }
-      XPIProvider.setTelemetry(addon.id, `${aMethod}_MS`, new Date() - timeStart);
     }
   }
 
   /**
    * Loads a bootstrapped add-on's bootstrap.js into a sandbox and the reason
    * values as constants in the scope.
    *
    * @param {integer?} [aReason]
@@ -1859,18 +1849,16 @@ var XPIProvider = {
   },
 
   BOOTSTRAP_REASONS: Object.freeze(BOOTSTRAP_REASONS),
 
   // A Map of active addons to their bootstrapScope by ID
   activeAddons: new Map(),
   // True if the platform could have activated extensions
   extensionsActive: false,
-  // Keep track of startup phases for telemetry
-  runPhase: XPI_STARTING,
   // Per-addon telemetry information
   _telemetryDetails: {},
   // Have we started shutting down bootstrap add-ons?
   _closing: false,
 
   // Check if the XPIDatabase has been loaded (without actually
   // triggering unwanted imports or I/O)
   get isDBLoaded() {
@@ -1937,22 +1925,22 @@ var XPIProvider = {
     };
 
     Object.values(addons).forEach(add);
 
     return Array.from(res, id => addons[id]);
   },
 
   /*
-   * Set a value in the telemetry hash for a given ID
+   * Adds metadata to the telemetry payload for the given add-on.
    */
-  setTelemetry(aId, aName, aValue) {
+  addTelemetry(aId, aPayload) {
     if (!this._telemetryDetails[aId])
       this._telemetryDetails[aId] = {};
-    this._telemetryDetails[aId][aName] = aValue;
+    Object.assign(this._telemetryDetails[aId], aPayload);
   },
 
   setupInstallLocations(aAppChanged) {
     function DirectoryLoc(aName, aScope, aKey, aPaths, aLocked) {
       try {
         var dir = FileUtils.getDir(aKey, aPaths);
       } catch (e) {
         return null;
@@ -2052,17 +2040,16 @@ var XPIProvider = {
    *        The version of the platform last run with this profile or null
    *        if it is a new profile or the version is unknown
    */
   startup(aAppChanged, aOldAppVersion, aOldPlatformVersion) {
     try {
       AddonManagerPrivate.recordTimestamp("XPI_startup_begin");
 
       logger.debug("startup");
-      this.runPhase = XPI_STARTING;
 
       // Clear this at startup for xpcshell test restarts
       this._telemetryDetails = {};
       // Register our details structure with AddonManager
       AddonManagerPrivate.setTelemetryDetails("XPI", this._telemetryDetails);
 
       this.setupInstallLocations(aAppChanged);
 
@@ -2153,17 +2140,16 @@ var XPIProvider = {
             AsyncShutdown.profileChangeTeardown.addBlocker(
               `Extension shutdown: ${addon.id}`, promise);
           }
         });
 
       // Detect final-ui-startup for telemetry reporting
       Services.obs.addObserver(function observer() {
         AddonManagerPrivate.recordTimestamp("XPI_finalUIStartup");
-        XPIProvider.runPhase = XPI_AFTER_UI_STARTUP;
         Services.obs.removeObserver(observer, "final-ui-startup");
       }, "final-ui-startup");
 
       // If we haven't yet loaded the XPI database, schedule loading it
       // to occur once other important startup work is finished.  We want
       // this to happen relatively quickly after startup so the telemetry
       // environment has complete addon information.
       //
@@ -2192,17 +2178,16 @@ var XPIProvider = {
         for (let event of EVENTS) {
           Services.obs.addObserver(observer, event);
         }
       }
 
       AddonManagerPrivate.recordTimestamp("XPI_startup_end");
 
       this.extensionsActive = true;
-      this.runPhase = XPI_BEFORE_UI_STARTUP;
 
       timerManager.registerTimer("xpi-signature-verification", () => {
         XPIDatabase.verifySignatures();
       }, XPI_SIGNATURE_CHECK_PERIOD);
     } catch (e) {
       logger.error("startup failed", e);
       AddonManagerPrivate.recordException("XPI", "startup failed", e);
     }
@@ -2227,17 +2212,16 @@ var XPIProvider = {
       if (install.onShutdown()) {
         install.onShutdown();
       }
     }
 
     // If there are pending operations then we must update the list of active
     // add-ons
     if (Services.prefs.getBoolPref(PREF_PENDING_OPERATIONS, false)) {
-      AddonManagerPrivate.recordSimpleMeasure("XPIDB_pending_ops", 1);
       XPIDatabase.updateActiveAddons();
       Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false);
     }
 
     // Ugh, if we reach this point without loading the xpi database,
     // we need to load it know, otherwise the telemetry shutdown blocker
     // will never resolve.
     if (!XPIDatabase.initialized) {