Bug 578209 - added ability to deploy to random subset of users. This also lets us do a slow-rollout by gradually increasing the size of the subset, and it lets us deploy two or more mutually exclusive studies by giving them non-overlapping subsets.
authorJono X <jono@mozilla.com>
Thu, 17 Feb 2011 23:39:10 -0800
changeset 547 9f7654da5df865705ad976c8101c5ed420b47bbc
parent 546 e9fce1ea559d4aef4accb09fd47023f25037297f
child 548 46cabbdc9eec8993acc3f849e51ce0c2dbd9b300
push id454
push userjdicarlo@mozilla.com
push dateFri, 18 Feb 2011 07:39:16 +0000
bugs578209
Bug 578209 - added ability to deploy to random subset of users. This also lets us do a slow-rollout by gradually increasing the size of the subset, and it lets us deploy two or more mutually exclusive studies by giving them non-overlapping subsets.
extension/modules/setup.js
--- a/extension/modules/setup.js
+++ b/extension/modules/setup.js
@@ -52,16 +52,17 @@ const RUN_AT_ALL_PREF = "extensions.test
 const POPUP_SHOW_ON_NEW = "extensions.testpilot.popup.showOnNewStudy";
 const POPUP_SHOW_ON_FINISH = "extensions.testpilot.popup.showOnStudyFinished";
 const POPUP_SHOW_ON_RESULTS = "extensions.testpilot.popup.showOnNewResults";
 const POPUP_CHECK_INTERVAL = "extensions.testpilot.popup.delayAfterStartup";
 const POPUP_REMINDER_INTERVAL = "extensions.testpilot.popup.timeBetweenChecks";
 const ALWAYS_SUBMIT_DATA = "extensions.testpilot.alwaysSubmitData";
 const UPDATE_CHANNEL_PREF = "app.update.channel";
 const LOG_FILE_NAME = "TestPilotErrorLog.log";
+const RANDOM_DEPLOY_PREFIX = "extensions.testpilot.deploymentRandomizer";
 
 let TestPilotSetup = {
   didReminderAfterStartup: false,
   startupComplete: false,
   _shortTimer: null,
   _longTimer: null,
   _remoteExperimentLoader: null, // TODO make this a lazy initializer too?
   taskList: [],
@@ -689,31 +690,32 @@ let TestPilotSetup = {
   _experimentRequirementsAreMet: function TPS__requirementsMet(experiment) {
     /* Returns true if we we meet the requirements to run this experiment
      * (e.g. meet the minimum Test Pilot version and Firefox version)
      * false if not.
      * Default is always to run the study - return true UNLESS the study
      * specifies a requirement that we don't meet. */
     let logger = this._logger;
     try {
-      let minTpVer, minFxVer, expName, runOrNotFunc;
+      let minTpVer, minFxVer, expName, runOrNotFunc, randomDeployment;
       /* Could be an experiment, which specifies experimentInfo, or survey,
        * which specifies surveyInfo. */
       let info = experiment.experimentInfo ?
                    experiment.experimentInfo :
                    experiment.surveyInfo;
       if (!info) {
         // If neither one is supplied, study lacks metadata required to run
         logger.warn("Study lacks minimum metadata to run.");
         return false;
       }
       minTpVer = info.minTPVersion;
       minFxVer = info.minFXVersion;
       expName =  info.testName;
       runOrNotFunc = info.runOrNotFunc;
+      randomDeployment = info.randomDeployment;
 
       // Minimum test pilot version:
       if (minTpVer && this._isNewerThanMe(minTpVer)) {
         logger.warn("Not loading " + expName);
         logger.warn("Because it requires Test Pilot version " + minTpVer);
 
         // Let user know there is a newer version of Test Pilot available:
         if (!this._isShowingUpdateNotification()) {
@@ -730,16 +732,35 @@ let TestPilotSetup = {
 
       // Minimum firefox version:
       if (minFxVer && this._isNewerThanFirefox(minFxVer)) {
         logger.warn("Not loading " + expName);
         logger.warn("Because it requires Firefox version " + minFxVer);
         return false;
       }
 
+      // Random deployment (used to give study to random subsample of users)
+      if (randomDeployment) {
+        /* Roll a hundred-sided die. Remember what we roll for later reference.  A study
+         * using random subsample deployment will provide a range (say, 0 ~ 30) which means
+         * only users who roll within that range will run the study. */
+        let prefName = RANDOM_DEPLOY_PREFIX + "." + randomDeployment.rolloutCode;
+        let myRoll = this._prefs.getValue(prefName, null);
+        if (myRoll == null) {
+          myRoll = Math.floor(Math.random()*100);
+          this._prefs.setValue(prefName, myRoll);
+        }
+        if (myRoll < randomDeployment.minRoll) {
+          return false;
+        }
+        if (myRoll > randomDeployment.maxRoll) {
+          return false;
+        }
+      }
+
       /* The all-purpose, arbitrary code "Should this study run?" function - if
        * provided, use its return value. */
       if (runOrNotFunc) {
         return runOrNotFunc();
       }
     } catch (e) {
       logger.warn("Error in requirements check " +  e);
     }