Bug 1308337 - Part 4: Instrument FetchMetaGlobal stage r=nalexander draft
authorGrigory Kruglov <gkruglov@mozilla.com>
Mon, 29 May 2017 21:48:05 -0400
changeset 588427 c4a10afafe83bbb404ad47a56b7c5c389c716dd6
parent 588426 4f9efb83da8f31b2e0470df6538c67533872f23a
child 588428 801c9c27730c217c6cf2d6e2bf086ef78f36f3a4
push id62031
push userbmo:gkruglov@mozilla.com
push dateFri, 02 Jun 2017 19:52:26 +0000
reviewersnalexander
bugs1308337
milestone55.0a1
Bug 1308337 - Part 4: Instrument FetchMetaGlobal stage r=nalexander MozReview-Commit-ID: 6zXqgsAIajN
mobile/android/services/src/main/java/org/mozilla/gecko/sync/GlobalSession.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/FetchMetaGlobalStage.java
mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/GlobalSyncStage.java
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/GlobalSession.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/GlobalSession.java
@@ -290,16 +290,20 @@ public class GlobalSession implements Ht
       this.abort(e, "No such stage " + next);
       return;
     }
     this.currentState = next;
     Logger.info(LOG_TAG, "Running next stage " + next + " (" + nextStage + ")...");
 
     // For named stages, use the repository name.
     String collectorName = currentState.getRepositoryName();
+    // For unnamed, non-repository stages use name of the stage itself.
+    if (collectorName == null) {
+      collectorName = currentState.name();
+    }
     final TelemetryStageCollector stageCollector = telemetryCollector.collectorFor(collectorName);
     // Stage is responsible for setting the 'finished' timestamp when appropriate.
     stageCollector.started = SystemClock.elapsedRealtime();
 
     try {
       nextStage.execute(this, stageCollector);
     } catch (Exception ex) {
       Logger.warn(LOG_TAG, "Caught exception " + ex + " running stage " + next);
@@ -654,22 +658,23 @@ public class GlobalSession implements Ht
     }
 
     request.put(keysRecord);
   }
 
   /*
    * meta/global callbacks.
    */
-  public void processMetaGlobal(MetaGlobal global) {
+  public void processMetaGlobal(MetaGlobal global, TelemetryStageCollector stageCollector) {
     config.metaGlobal = global;
 
     Long storageVersion = global.getStorageVersion();
     if (storageVersion == null) {
       Logger.warn(LOG_TAG, "Malformed remote meta/global: could not retrieve remote storage version.");
+      stageCollector.error = new TelemetryCollector.StageErrorBuilder("metaglobal", "noversion").build();
       freshStart();
       return;
     }
     if (storageVersion < STORAGE_VERSION) {
       Logger.warn(LOG_TAG, "Outdated server: reported " +
           "remote storage version " + storageVersion + " < " +
           "local storage version " + STORAGE_VERSION);
       freshStart();
@@ -680,16 +685,17 @@ public class GlobalSession implements Ht
           "remote storage version " + storageVersion + " > " +
           "local storage version " + STORAGE_VERSION);
       requiresUpgrade();
       return;
     }
     String remoteSyncID = global.getSyncID();
     if (remoteSyncID == null) {
       Logger.warn(LOG_TAG, "Malformed remote meta/global: could not retrieve remote syncID.");
+      stageCollector.error = new TelemetryCollector.StageErrorBuilder("metaglobal", "nosyncid").build();
       freshStart();
       return;
     }
     String localSyncID = config.syncID;
     if (!remoteSyncID.equals(localSyncID)) {
       Logger.warn(LOG_TAG, "Remote syncID different from local syncID: resetting client and assuming remote syncID.");
       resetAllStages();
       config.purgeCryptoKeys();
@@ -1126,17 +1132,17 @@ public class GlobalSession implements Ht
 
   /**
    * Suggest that your Sync client needs to be upgraded to work
    * with this server.
    */
   public void requiresUpgrade() {
     Logger.info(LOG_TAG, "Client outdated storage version; requires update.");
     // TODO: notify UI.
-    this.abort(null, "Requires upgrade");
+    this.abort(null, "Requires upgrade from " + STORAGE_VERSION);
   }
 
   /**
    * If meta/global is missing or malformed, throws a MetaGlobalException.
    * Otherwise, returns true if there is an entry for this engine in the
    * meta/global "engines" object.
    * <p>
    * This is a global/permanent setting, not a local/temporary setting. For the
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/FetchMetaGlobalStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/FetchMetaGlobalStage.java
@@ -1,76 +1,102 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.sync.stage;
 
+import android.os.SystemClock;
+
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.sync.GlobalSession;
+import org.mozilla.gecko.sync.HTTPFailureException;
 import org.mozilla.gecko.sync.InfoCollections;
 import org.mozilla.gecko.sync.MetaGlobal;
 import org.mozilla.gecko.sync.PersistedMetaGlobal;
 import org.mozilla.gecko.sync.delegates.MetaGlobalDelegate;
 import org.mozilla.gecko.sync.net.SyncStorageResponse;
+import org.mozilla.gecko.sync.telemetry.TelemetryCollector;
 
 public class FetchMetaGlobalStage extends AbstractNonRepositorySyncStage {
   private static final String LOG_TAG = "FetchMetaGlobalStage";
   private static final String META_COLLECTION = "meta";
 
+  private static final String TELEMETRY_ERROR_NAME = "metaglobal";
+  private static final String TELEMETRY_ERROR_MISSING = "missing";
+  private static final String TELEMETRY_ERROR_NO_INFO_COLLECTIONS = "noic";
+
   public class StageMetaGlobalDelegate implements MetaGlobalDelegate {
 
     private final GlobalSession session;
     public StageMetaGlobalDelegate(GlobalSession session) {
       this.session = session;
     }
 
     @Override
     public void handleSuccess(MetaGlobal global, SyncStorageResponse response) {
       Logger.trace(LOG_TAG, "Persisting fetched meta/global and last modified.");
       PersistedMetaGlobal pmg = session.config.persistedMetaGlobal();
       pmg.persistMetaGlobal(global);
       // Take the timestamp from the response since it is later than the timestamp from info/collections.
       pmg.persistLastModified(response.normalizedWeaveTimestamp());
 
-      session.processMetaGlobal(global);
+      telemetryStageCollector.finished = SystemClock.elapsedRealtime();
+      session.processMetaGlobal(global, telemetryStageCollector);
     }
 
     @Override
     public void handleFailure(SyncStorageResponse response) {
+      telemetryStageCollector.error = new TelemetryCollector.StageErrorBuilder()
+              .setLastException(new HTTPFailureException(response))
+              .build();
+      telemetryStageCollector.finished = SystemClock.elapsedRealtime();
       session.handleHTTPError(response, "Failure fetching meta/global.");
     }
 
     @Override
     public void handleError(Exception e) {
+      telemetryStageCollector.error = new TelemetryCollector.StageErrorBuilder()
+              .setLastException(e)
+              .build();
+      telemetryStageCollector.finished = SystemClock.elapsedRealtime();
       session.abort(e, "Failure fetching meta/global.");
     }
 
     @Override
     public void handleMissing(MetaGlobal global, SyncStorageResponse response) {
+      // While not strictly an error, it's good to keep track of this.
+      telemetryStageCollector.error = new TelemetryCollector
+              .StageErrorBuilder(TELEMETRY_ERROR_NAME, TELEMETRY_ERROR_MISSING)
+              .build();
       session.processMissingMetaGlobal(global);
     }
   }
 
   @Override
   public void execute() throws NoSuchStageException {
     InfoCollections infoCollections = session.config.infoCollections;
     if (infoCollections == null) {
+      telemetryStageCollector.finished = SystemClock.elapsedRealtime();
+      telemetryStageCollector.error = new TelemetryCollector
+              .StageErrorBuilder(TELEMETRY_ERROR_NAME, TELEMETRY_ERROR_NO_INFO_COLLECTIONS)
+              .build();
       session.abort(null, "No info/collections set in FetchMetaGlobalStage.");
       return;
     }
 
     final long lastModified = session.config.persistedMetaGlobal().lastModified();
     if (!infoCollections.updateNeeded(META_COLLECTION, lastModified)) {
       // Try to use our local collection keys for this session.
       Logger.info(LOG_TAG, "Trying to use persisted meta/global for this session.");
       MetaGlobal global = session.config.persistedMetaGlobal().metaGlobal(session.config.metaURL(), session.getAuthHeaderProvider());
       if (global != null) {
         Logger.info(LOG_TAG, "Using persisted meta/global for this session.");
-        session.processMetaGlobal(global); // Calls session.advance().
+        telemetryStageCollector.finished = SystemClock.elapsedRealtime();
+        session.processMetaGlobal(global, telemetryStageCollector); // Calls session.advance().
         return;
       }
       Logger.info(LOG_TAG, "Failed to use persisted meta/global for this session.");
     }
 
     // We need an update: fetch or upload meta/global as necessary.
     // We assert when we believe meta/global was last modified via X-I-U-S.
     Logger.info(LOG_TAG, "Fetching fresh meta/global for this session.");
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/GlobalSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/GlobalSyncStage.java
@@ -1,14 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.sync.stage;
 
+import android.support.annotation.Nullable;
+
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.telemetry.TelemetryStageCollector;
@@ -63,17 +65,17 @@ public interface GlobalSyncStage {
      * @return an immutable collection of Stages.
      */
     public static Collection<Stage> getNamedStages() {
       return Collections.unmodifiableCollection(named.values());
     }
 
     // Each Stage tracks its repositoryName.
     private final String repositoryName;
-    public String getRepositoryName() {
+    @Nullable public String getRepositoryName() {
       return repositoryName;
     }
 
     private Stage() {
       this.repositoryName = null;
     }
 
     private Stage(final String name) {