Bug 1349363 - Run an experiment in beta for e10s-multi. r=Felipe
authorBlake Kaplan <mrbkap@gmail.com>
Mon, 17 Apr 2017 15:45:47 -0700
changeset 353690 483168efae35648010c909809c427d190f010b9c
parent 353689 c0c849826a143ade9b6eb1c81bfd97d93194e872
child 353691 bf54a57111845a3eae965d79c22ac53475cd73b1
push id31675
push usercbook@mozilla.com
push dateWed, 19 Apr 2017 08:28:05 +0000
treeherdermozilla-central@245811243903 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersFelipe
bugs1349363
milestone55.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 1349363 - Run an experiment in beta for e10s-multi. r=Felipe MozReview-Commit-ID: 8P0pbWZJ8uz
browser/extensions/e10srollout/bootstrap.js
--- a/browser/extensions/e10srollout/bootstrap.js
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -8,17 +8,17 @@ const {classes: Cc, interfaces: Ci, util
 
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/UpdateUtils.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
 
  // The amount of people to be part of e10s
 const TEST_THRESHOLD = {
-  "beta": 0.5,  // 50%
+  "beta": 0.9,  // 90%
   "release": 1.0,  // 100%
   "esr": 1.0,  // 100%
 };
 
 const ADDON_ROLLOUT_POLICY = {
   "beta": "50allmpc",
   "release": "50allmpc",
   "esr": "esrA", // WebExtensions and Addons with mpc=true
@@ -46,16 +46,20 @@ const PREF_COHORT_SAMPLE       = "e10s.r
 const PREF_COHORT_NAME         = "e10s.rollout.cohort";
 const PREF_E10S_OPTED_IN       = "browser.tabs.remote.autostart";
 const PREF_E10S_FORCE_ENABLED  = "browser.tabs.remote.force-enable";
 const PREF_E10S_FORCE_DISABLED = "browser.tabs.remote.force-disable";
 const PREF_TOGGLE_E10S         = "browser.tabs.remote.autostart.2";
 const PREF_E10S_ADDON_POLICY   = "extensions.e10s.rollout.policy";
 const PREF_E10S_ADDON_BLOCKLIST = "extensions.e10s.rollout.blocklist";
 const PREF_E10S_HAS_NONEXEMPT_ADDON = "extensions.e10s.rollout.hasAddon";
+const PREF_E10S_MULTI_OPTOUT   = "dom.ipc.multiOptOut";
+const PREF_E10S_PROCESSCOUNT   = "dom.ipc.processCount";
+const PREF_E10S_MULTI_ADDON_BLOCKS = "extensions.e10sMultiBlocksEnabling";
+const PREF_E10S_MULTI_BLOCKED_BY_ADDONS = "extensions.e10sMultiBlockedByAddons";
 
 function startup() {
   // In theory we only need to run this once (on install()), but
   // it's better to also run it on every startup. If the user has
   // made manual changes to the prefs, this will keep the data
   // reported more accurate.
   // It's also fine (and preferred) to just do it here on startup
   // (instead of observing prefs), because e10s takes a restart
@@ -99,106 +103,153 @@ function defineCohort() {
   } else {
     Preferences.reset(PREF_E10S_ADDON_POLICY);
   }
 
   let userOptedOut = optedOut();
   let userOptedIn = optedIn();
   let disqualified = (Services.appinfo.multiprocessBlockPolicy != 0);
   let testThreshold = TEST_THRESHOLD[updateChannel];
-  let testGroup = (getUserSample() < testThreshold);
+  let testGroup = (getUserSample(false) < testThreshold);
   let hasNonExemptAddon = Preferences.get(PREF_E10S_HAS_NONEXEMPT_ADDON, false);
   let temporaryDisqualification = getTemporaryDisqualification();
   let temporaryQualification = getTemporaryQualification();
 
   let cohortPrefix = "";
   if (disqualified) {
     cohortPrefix = "disqualified-";
   } else if (hasNonExemptAddon) {
     cohortPrefix = `addons-set${addonPolicy}-`;
   }
 
-  if (userOptedOut) {
+  let inMultiExperiment = false;
+  if (userOptedOut.e10s || userOptedOut.multi) {
+    // If we detected that the user opted out either for multi or e10s, then
+    // the proper prefs must already be set.
     setCohort("optedOut");
-  } else if (userOptedIn) {
+  } else if (userOptedIn.e10s) {
     setCohort("optedIn");
   } else if (temporaryDisqualification != "") {
     // Users who are disqualified by the backend (from multiprocessBlockPolicy)
     // can be put into either the test or control groups, because e10s will
     // still be denied by the backend, which is useful so that the E10S_STATUS
     // telemetry probe can be correctly set.
 
     // For these volatile disqualification reasons, however, we must not try
     // to activate e10s because the backend doesn't know about it. E10S_STATUS
     // here will be accumulated as "2 - Disabled", which is fine too.
     setCohort(`temp-disqualified-${temporaryDisqualification}`);
     Preferences.reset(PREF_TOGGLE_E10S);
+    Preferences.reset(PREF_E10S_PROCESSCOUNT + ".web");
   } else if (!disqualified && testThreshold < 1.0 &&
              temporaryQualification != "") {
     // Users who are qualified for e10s and on channels where some population
     // would not receive e10s can be pushed into e10s anyway via a temporary
     // qualification which overrides the user sample value when non-empty.
     setCohort(`temp-qualified-${temporaryQualification}`);
     Preferences.set(PREF_TOGGLE_E10S, true);
+    inMultiExperiment = true;
   } else if (testGroup) {
     setCohort(`${cohortPrefix}test`);
     Preferences.set(PREF_TOGGLE_E10S, true);
+    inMultiExperiment = true;
   } else {
     setCohort(`${cohortPrefix}control`);
     Preferences.reset(PREF_TOGGLE_E10S);
+    Preferences.reset(PREF_E10S_PROCESSCOUNT + ".web");
+  }
+
+  // Now determine if this user should be in the e10s-multi experiment.
+  // - We only run the experiment on the beta channel.
+  // - We decided above whether this user qualifies for the experiment.
+  // - If the user already opted into multi, then their prefs are already set
+  //   correctly, we're done.
+  // - If the user has addons that disqualify them for multi, leave them with
+  //   the default number of content processes (1 on beta) but still in the
+  //   test cohort.
+  if (updateChannel !== "beta" ||
+      !inMultiExperiment ||
+      userOptedIn.multi ||
+      getAddonsDisqualifyForMulti()) {
+    Preferences.reset(PREF_E10S_PROCESSCOUNT + ".web");
+    return;
+  }
+
+  // The user is in the multi experiment!
+  // Decide how many content processes to use for this user.
+  let BUCKETS = {
+    1: .25,
+    2: .5,
+    4: .75,
+    8: 1
+  };
+
+  let multiUserSample = getUserSample(true);
+  for (let sampleName of Object.getOwnPropertyNames(BUCKETS)) {
+    if (multiUserSample < BUCKETS[sampleName]) {
+      setCohort(`${cohortPrefix}multiBucket${sampleName}`);
+      Preferences.set(PREF_E10S_PROCESSCOUNT + ".web", sampleName);
+      break;
+    }
   }
 }
 
 function shutdown(data, reason) {
 }
 
 function uninstall() {
 }
 
-function getUserSample() {
-  let prefValue = Preferences.get(PREF_COHORT_SAMPLE, undefined);
+function getUserSample(multi) {
+  let pref = multi ? (PREF_COHORT_SAMPLE + ".multi") : PREF_COHORT_SAMPLE;
+  let prefValue = Preferences.get(pref, undefined);
   let value = 0.0;
 
   if (typeof(prefValue) == "string") {
     value = parseFloat(prefValue, 10);
     return value;
   }
 
   if (typeof(prefValue) == "number") {
     // convert old integer value
     value = prefValue / 100;
   } else {
     value = Math.random();
   }
 
-  Preferences.set(PREF_COHORT_SAMPLE, value.toString().substr(0, 8));
+  Preferences.set(pref, value.toString().substr(0, 8));
   return value;
 }
 
 function setCohort(cohortName) {
   Preferences.set(PREF_COHORT_NAME, cohortName);
   try {
     if (Ci.nsICrashReporter) {
       Services.appinfo.QueryInterface(Ci.nsICrashReporter).annotateCrashReport("E10SCohort", cohortName);
     }
   } catch (e) {}
 }
 
 function optedIn() {
-  return Preferences.get(PREF_E10S_OPTED_IN, false) ||
-         Preferences.get(PREF_E10S_FORCE_ENABLED, false);
+  let e10s = Preferences.get(PREF_E10S_OPTED_IN, false) ||
+             Preferences.get(PREF_E10S_FORCE_ENABLED, false);
+  let multi = Preferences.isSet(PREF_E10S_PROCESSCOUNT);
+  return { e10s, multi };
 }
 
 function optedOut() {
   // Users can also opt-out by toggling back the pref to false.
   // If they reset the pref instead they might be re-enabled if
   // they are still part of the threshold.
-  return Preferences.get(PREF_E10S_FORCE_DISABLED, false) ||
-         (Preferences.isSet(PREF_TOGGLE_E10S) &&
-          Preferences.get(PREF_TOGGLE_E10S) == false);
+  let e10s = Preferences.get(PREF_E10S_FORCE_DISABLED, false) ||
+               (Preferences.isSet(PREF_TOGGLE_E10S) &&
+                Preferences.get(PREF_TOGGLE_E10S) == false);
+  let multi = Preferences.get(PREF_E10S_MULTI_OPTOUT, 0) >=
+              Services.appinfo.E10S_MULTI_EXPERIMENT;
+  return { e10s, multi };
 }
 
 /* If this function returns a non-empty string, it
  * means that this particular user should be temporarily
  * disqualified due to some particular reason.
  * If a user shouldn't be disqualified, then an empty
  * string must be returned.
  */
@@ -220,8 +271,13 @@ function getTemporaryQualification() {
   const PREF_OPENED_DEVTOOLS = "devtools.telemetry.tools.opened.version";
   let hasOpenedDevTools = Preferences.isSet(PREF_OPENED_DEVTOOLS);
   if (hasOpenedDevTools) {
     return "devtools";
   }
 
   return "";
 }
+
+function getAddonsDisqualifyForMulti() {
+  return Preferences.get("extensions.e10sMultiBlocksEnabling", false) &&
+         Preferences.get("extensions.e10sMultiBlockedByAddons", false);
+}