Bug 1349363 - Run an experiment in beta for e10s-multi. r=Felipe draft
authorBlake Kaplan <mrbkap@gmail.com>
Mon, 17 Apr 2017 15:45:47 -0700
changeset 564566 d74358832040b3862d8e96ed5d311326a2b99825
parent 564565 e60ef55ff64e3e74ee23b0bd77ef0e0bcc0c6c3a
child 564567 611e65fe6391b69832eb033bfbbc89ad09052e1a
child 564568 c0b2204894fe32e83b806737e91eb47d0d4a9a34
push id54642
push userbmo:mrbkap@mozilla.com
push dateTue, 18 Apr 2017 19:22:13 +0000
reviewersFelipe
bugs1349363
milestone55.0a1
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);
+}