Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | .. _experiments_manifests: |
michael@0 | 2 | |
michael@0 | 3 | ===================== |
michael@0 | 4 | Experiments Manifests |
michael@0 | 5 | ===================== |
michael@0 | 6 | |
michael@0 | 7 | *Experiments Manifests* are documents that describe the set of active |
michael@0 | 8 | experiments a client may run. |
michael@0 | 9 | |
michael@0 | 10 | *Experiments Manifests* are fetched periodically by clients. When |
michael@0 | 11 | fetched, clients look at the experiments within the manifest and |
michael@0 | 12 | determine which experiments are applicable. If an experiment is |
michael@0 | 13 | applicable, the client may download and start the experiment. |
michael@0 | 14 | |
michael@0 | 15 | Manifest Format |
michael@0 | 16 | =============== |
michael@0 | 17 | |
michael@0 | 18 | Manifests are JSON documents where the main element is an object. |
michael@0 | 19 | |
michael@0 | 20 | The *schema* of the object is versioned and defined by the presence |
michael@0 | 21 | of a top-level ``version`` property, whose integer value is the |
michael@0 | 22 | schema version used by that manifest. Each version is documented |
michael@0 | 23 | in the sections below. |
michael@0 | 24 | |
michael@0 | 25 | Version 1 |
michael@0 | 26 | --------- |
michael@0 | 27 | |
michael@0 | 28 | Version 1 is the original manifest format. |
michael@0 | 29 | |
michael@0 | 30 | The following properties may exist in the root object: |
michael@0 | 31 | |
michael@0 | 32 | experiments |
michael@0 | 33 | An array of objects describing candidate experiments. The format of |
michael@0 | 34 | these objects is documented below. |
michael@0 | 35 | |
michael@0 | 36 | An array is used to create an explicit priority of experiments. |
michael@0 | 37 | Experiments listed at the beginning of the array take priority over |
michael@0 | 38 | experiments that follow. |
michael@0 | 39 | |
michael@0 | 40 | Experiments Objects |
michael@0 | 41 | ^^^^^^^^^^^^^^^^^^^ |
michael@0 | 42 | |
michael@0 | 43 | Each object in the ``experiments`` array may contain the following |
michael@0 | 44 | properties: |
michael@0 | 45 | |
michael@0 | 46 | id |
michael@0 | 47 | (required) String identifier of this experiment. The identifier should |
michael@0 | 48 | be treated as opaque by clients. It is used to uniquely identify an |
michael@0 | 49 | experiment for all of time. |
michael@0 | 50 | |
michael@0 | 51 | xpiURL |
michael@0 | 52 | (required) String URL of the XPI that implements this experiment. |
michael@0 | 53 | |
michael@0 | 54 | If the experiment is activated, the client will download and install this |
michael@0 | 55 | XPI. |
michael@0 | 56 | |
michael@0 | 57 | xpiHash |
michael@0 | 58 | (required) String hash of the XPI that implements this experiment. |
michael@0 | 59 | |
michael@0 | 60 | The value is composed of a hash identifier followed by a colon |
michael@0 | 61 | followed by the hash value. e.g. |
michael@0 | 62 | `sha1:f677428b9172e22e9911039aef03f3736e7f78a7`. `sha1` and `sha256` |
michael@0 | 63 | are the two supported hashing mechanisms. The hash value is the hex |
michael@0 | 64 | encoding of the binary hash. |
michael@0 | 65 | |
michael@0 | 66 | When the client downloads the XPI for the experiment, it should compare |
michael@0 | 67 | the hash of that XPI against this value. If the hashes don't match, |
michael@0 | 68 | the client should not install the XPI. |
michael@0 | 69 | |
michael@0 | 70 | Clients may also use this hash as a means of determining when an |
michael@0 | 71 | experiment's XPI has changed and should be refreshed. |
michael@0 | 72 | |
michael@0 | 73 | startTime |
michael@0 | 74 | Integer seconds since UNIX epoch that this experiment should |
michael@0 | 75 | start. Clients should not start an experiment if *now()* is less than |
michael@0 | 76 | this value. |
michael@0 | 77 | |
michael@0 | 78 | maxStartTime |
michael@0 | 79 | (optional) Integer seconds since UNIX epoch after which this experiment |
michael@0 | 80 | should no longer start. |
michael@0 | 81 | |
michael@0 | 82 | Some experiments may wish to impose hard deadlines after which no new |
michael@0 | 83 | clients should activate the experiment. This property may be used to |
michael@0 | 84 | facilitate that. |
michael@0 | 85 | |
michael@0 | 86 | endTime |
michael@0 | 87 | Integer seconds since UNIX epoch after which this experiment |
michael@0 | 88 | should no longer run. Clients should cease an experiment when the current |
michael@0 | 89 | time is beyond this value. |
michael@0 | 90 | |
michael@0 | 91 | maxActiveSeconds |
michael@0 | 92 | Integer seconds defining the max wall time this experiment should be |
michael@0 | 93 | active for. |
michael@0 | 94 | |
michael@0 | 95 | The client should deactivate the experiment this many seconds after |
michael@0 | 96 | initial activation. |
michael@0 | 97 | |
michael@0 | 98 | This value only involves wall time, not browser activity or session time. |
michael@0 | 99 | |
michael@0 | 100 | appName |
michael@0 | 101 | Array of application names this experiment should run on. |
michael@0 | 102 | |
michael@0 | 103 | An application name comes from ``nsIXULAppInfo.name``. It is a value |
michael@0 | 104 | like ``Firefox``, ``Fennec``, or `B2G`. |
michael@0 | 105 | |
michael@0 | 106 | The client should compare its application name against the members of |
michael@0 | 107 | this array. If a match is found, the experiment is applicable. |
michael@0 | 108 | |
michael@0 | 109 | minVersion |
michael@0 | 110 | (optional) String version number of the minimum application version this |
michael@0 | 111 | experiment should run on. |
michael@0 | 112 | |
michael@0 | 113 | A version number is something like ``27.0.0`` or ``28``. |
michael@0 | 114 | |
michael@0 | 115 | The client should compare its version number to this value. If the client's |
michael@0 | 116 | version is greater or equal to this version (using a version-aware comparison |
michael@0 | 117 | function), the experiment is applicable. |
michael@0 | 118 | |
michael@0 | 119 | If this is not specified, there is no lower bound to versions this |
michael@0 | 120 | experiment should run on. |
michael@0 | 121 | |
michael@0 | 122 | maxVersion |
michael@0 | 123 | (optional) String version number of the maximum application version this |
michael@0 | 124 | experiment should run on. |
michael@0 | 125 | |
michael@0 | 126 | This is similar to ``minVersion`` except it sets the upper bound for |
michael@0 | 127 | application versions. |
michael@0 | 128 | |
michael@0 | 129 | If the client's version is less than or equal to this version, the |
michael@0 | 130 | experiment is applicable. |
michael@0 | 131 | |
michael@0 | 132 | If this is not specified, there is no upper bound to versions this |
michael@0 | 133 | experiment should run on. |
michael@0 | 134 | |
michael@0 | 135 | version |
michael@0 | 136 | (optional) Array of application versions this experiment should run on. |
michael@0 | 137 | |
michael@0 | 138 | This is similar to ``minVersion`` and ``maxVersion`` except only a |
michael@0 | 139 | whitelisted set of specific versions are allowed. |
michael@0 | 140 | |
michael@0 | 141 | The client should compare its version to members of this array. If a match |
michael@0 | 142 | is found, the experiment is applicable. |
michael@0 | 143 | |
michael@0 | 144 | minBuildID |
michael@0 | 145 | (optional) String minimum Build ID this experiment should run on. |
michael@0 | 146 | |
michael@0 | 147 | Build IDs are values like ``201402261424``. |
michael@0 | 148 | |
michael@0 | 149 | The client should perform a string comparison of its Build ID against this |
michael@0 | 150 | value. If its value is greater than or equal to this value, the experiment |
michael@0 | 151 | is applicable. |
michael@0 | 152 | |
michael@0 | 153 | maxBuildID |
michael@0 | 154 | (optional) String maximum Build ID this experiment should run on. |
michael@0 | 155 | |
michael@0 | 156 | This is similar to ``minBuildID`` except it sets the upper bound |
michael@0 | 157 | for Build IDs. |
michael@0 | 158 | |
michael@0 | 159 | The client should perform a string comparison of its Build ID against |
michael@0 | 160 | this value. If its value is less than or equal to this value, the |
michael@0 | 161 | experiment is applicable. |
michael@0 | 162 | |
michael@0 | 163 | buildIDs |
michael@0 | 164 | (optional) Array of Build IDs this experiment should run on. |
michael@0 | 165 | |
michael@0 | 166 | This is similar to ``minBuildID`` and ``maxBuildID`` except only a |
michael@0 | 167 | whitelisted set of Build IDs are considered. |
michael@0 | 168 | |
michael@0 | 169 | The client should compare its Build ID to members of this array. If a |
michael@0 | 170 | match is found, the experiment is applicable. |
michael@0 | 171 | |
michael@0 | 172 | os |
michael@0 | 173 | (optional) Array of operating system identifiers this experiment should |
michael@0 | 174 | run on. |
michael@0 | 175 | |
michael@0 | 176 | Values for this array come from ``nsIXULRuntime.OS``. |
michael@0 | 177 | |
michael@0 | 178 | The client will compare its operating system identifier to members |
michael@0 | 179 | of this array. If a match is found, the experiment is applicable to the |
michael@0 | 180 | client. |
michael@0 | 181 | |
michael@0 | 182 | channel |
michael@0 | 183 | (optional) Array of release channel identifiers this experiment should run |
michael@0 | 184 | on. |
michael@0 | 185 | |
michael@0 | 186 | The client will compare its channel to members of this array. If a match |
michael@0 | 187 | is found, the experiment is applicable. |
michael@0 | 188 | |
michael@0 | 189 | If this property is not defined, the client should assume the experiment |
michael@0 | 190 | is to run on all channels. |
michael@0 | 191 | |
michael@0 | 192 | locale |
michael@0 | 193 | (optional) Array of locale identifiers this experiment should run on. |
michael@0 | 194 | |
michael@0 | 195 | A locale identifier is a string like ``en-US`` or ``zh-CN`` and is |
michael@0 | 196 | obtained by looking at |
michael@0 | 197 | ``nsIXULChromeRegistry.getSelectedLocale("global")``. |
michael@0 | 198 | |
michael@0 | 199 | The client should compare its locale identifier to members of this array. |
michael@0 | 200 | If a match is found, the experiment is applicable. |
michael@0 | 201 | |
michael@0 | 202 | If this property is not defined, the client should assume the experiment |
michael@0 | 203 | is to run on all locales. |
michael@0 | 204 | |
michael@0 | 205 | sample |
michael@0 | 206 | (optional) Decimal number indicating the sampling rate for this experiment. |
michael@0 | 207 | |
michael@0 | 208 | This will contain a value between ``0.0`` and ``1.0``. The client should |
michael@0 | 209 | generate a random decimal between ``0.0`` and ``1.0``. If the randomly |
michael@0 | 210 | generated number is less than or equal to the value of this field, the |
michael@0 | 211 | experiment is applicable. |
michael@0 | 212 | |
michael@0 | 213 | disabled |
michael@0 | 214 | (optional) Boolean value indicating whether an experiment is disabled. |
michael@0 | 215 | |
michael@0 | 216 | Normally, experiments are deactivated after a certain time has passed or |
michael@0 | 217 | after the experiment itself determines it no longer needs to run (perhaps |
michael@0 | 218 | it collected sufficient data already). |
michael@0 | 219 | |
michael@0 | 220 | This property serves as a backup mechanism to remotely disable an |
michael@0 | 221 | experiment before it was scheduled to be disabled. It can be used to |
michael@0 | 222 | kill experiments that are found to be doing wrong or bad things or that |
michael@0 | 223 | aren't useful. |
michael@0 | 224 | |
michael@0 | 225 | If this property is not defined or is false, the client should assume |
michael@0 | 226 | the experiment is active and a candidate for activation. |
michael@0 | 227 | |
michael@0 | 228 | frozen |
michael@0 | 229 | (optional) Boolean value indicating this experiment is frozen and no |
michael@0 | 230 | longer accepting new enrollments. |
michael@0 | 231 | |
michael@0 | 232 | If a client sees a true value in this field, it should not attempt to |
michael@0 | 233 | activate an experiment. |
michael@0 | 234 | |
michael@0 | 235 | jsfilter |
michael@0 | 236 | (optional) JavaScript code that will be evaluated to determine experiment |
michael@0 | 237 | applicability. |
michael@0 | 238 | |
michael@0 | 239 | This property contains the string representation of JavaScript code that |
michael@0 | 240 | will be evaluated in a sandboxed environment using JavaScript's |
michael@0 | 241 | ``eval()``. |
michael@0 | 242 | |
michael@0 | 243 | The string is expected to contain the definition of a JavaScript function |
michael@0 | 244 | ``filter(context)``. This function receives as its argument an object |
michael@0 | 245 | holding application state. See the section below for the definition of |
michael@0 | 246 | this object. |
michael@0 | 247 | |
michael@0 | 248 | The purpose of this property is to allow experiments to define complex |
michael@0 | 249 | rules and logic for evaluating experiment applicability in a manner |
michael@0 | 250 | that is privacy conscious and doesn't require the transmission of |
michael@0 | 251 | excessive data. |
michael@0 | 252 | |
michael@0 | 253 | The return value of this filter indicates whether the experiment is |
michael@0 | 254 | applicable. Functions should return true if the experiment is |
michael@0 | 255 | applicable. |
michael@0 | 256 | |
michael@0 | 257 | If an experiment is not applicable, they should throw an Error whose |
michael@0 | 258 | message contains the reason the experiment is not applicable. This |
michael@0 | 259 | message may be logged and sent to remote servers, so it should not |
michael@0 | 260 | contain private or otherwise sensitive data that wouldn't normally |
michael@0 | 261 | be submitted. |
michael@0 | 262 | |
michael@0 | 263 | If a falsey (or undefined) value is returned, the client should |
michael@0 | 264 | assume the experiment is not applicable. |
michael@0 | 265 | |
michael@0 | 266 | If this property is not defined, the client does not consider a custom |
michael@0 | 267 | JavaScript filter function when determining whether an experiment is |
michael@0 | 268 | applicable. |
michael@0 | 269 | |
michael@0 | 270 | JavaScript Filter Context Objects |
michael@0 | 271 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
michael@0 | 272 | |
michael@0 | 273 | The object passed to a ``jsfilter`` ``filter()`` function contains the |
michael@0 | 274 | following properties: |
michael@0 | 275 | |
michael@0 | 276 | healthReportSubmissionEnabled |
michael@0 | 277 | This property contains a boolean indicating whether Firefox Health |
michael@0 | 278 | Report has its data submission flag enabled (whether Firefox Health |
michael@0 | 279 | Report is sending data to remote servers). |
michael@0 | 280 | |
michael@0 | 281 | healthReportPayload |
michael@0 | 282 | This property contains the current Firefox Health Report payload. |
michael@0 | 283 | |
michael@0 | 284 | The payload format is documented at :ref:`healthreport_dataformat`. |
michael@0 | 285 | |
michael@0 | 286 | telemetryPayload |
michael@0 | 287 | This property contains the current Telemetry payload. |
michael@0 | 288 | |
michael@0 | 289 | The evaluation sandbox for the JavaScript filters may be destroyed |
michael@0 | 290 | immediately after ``filter()`` returns. This function should not assume |
michael@0 | 291 | async code will finish. |
michael@0 | 292 | |
michael@0 | 293 | Experiment Applicability and Client Behavior |
michael@0 | 294 | ============================================ |
michael@0 | 295 | |
michael@0 | 296 | The point of an experiment manifest is to define which experiments are |
michael@0 | 297 | available and where and how to run them. This section explains those |
michael@0 | 298 | rules in more detail. |
michael@0 | 299 | |
michael@0 | 300 | Many of the properties in *Experiment Objects* are related to determining |
michael@0 | 301 | whether an experiment should run on a given client. This evaluation is |
michael@0 | 302 | performed client side. |
michael@0 | 303 | |
michael@0 | 304 | 1. Multiple conditions in an experiment |
michael@0 | 305 | --------------------------------------- |
michael@0 | 306 | |
michael@0 | 307 | If multiple conditions are defined for an experiment, the client should |
michael@0 | 308 | combine each condition with a logical *AND*: all conditions must be |
michael@0 | 309 | satisfied for an experiment to run. If one condition fails, the experiment |
michael@0 | 310 | is not applicable. |
michael@0 | 311 | |
michael@0 | 312 | 2. Active experiment disappears from manifest |
michael@0 | 313 | --------------------------------------------- |
michael@0 | 314 | |
michael@0 | 315 | If a specific experiment disappears from the manifest, the client should |
michael@0 | 316 | continue conducting an already-active experiment. Furthermore, the |
michael@0 | 317 | client should remember what the expiration events were for an experiment |
michael@0 | 318 | and honor them. |
michael@0 | 319 | |
michael@0 | 320 | The rationale here is that we want to prevent an accidental deletion |
michael@0 | 321 | or temporary failure on the server to inadvertantly deactivate |
michael@0 | 322 | supposed-to-be-active experiments. We also don't want premature deletion |
michael@0 | 323 | of an experiment from the manifest to result in indefinite activation |
michael@0 | 324 | periods. |
michael@0 | 325 | |
michael@0 | 326 | 3. Inactive experiment disappears from manifest |
michael@0 | 327 | ----------------------------------------------- |
michael@0 | 328 | |
michael@0 | 329 | If an inactive but scheduled-to-be-active experiment disappears from the |
michael@0 | 330 | manifest, the client should not activate the experiment. |
michael@0 | 331 | |
michael@0 | 332 | If that experiment reappears in the manifest, the client should not |
michael@0 | 333 | treat that experiment any differently than any other new experiment. Put |
michael@0 | 334 | another way, the fact an inactive experiment disappears and then |
michael@0 | 335 | reappears should not be significant. |
michael@0 | 336 | |
michael@0 | 337 | The rationale here is that server operators should have complete |
michael@0 | 338 | control of an inactive experiment up to it's go-live date. |
michael@0 | 339 | |
michael@0 | 340 | 4. Re-evaluating applicability on manifest refresh |
michael@0 | 341 | -------------------------------------------------- |
michael@0 | 342 | |
michael@0 | 343 | When an experiment manifest is refreshed or updated, the client should |
michael@0 | 344 | re-evaluate the applicability of each experiment therein. |
michael@0 | 345 | |
michael@0 | 346 | The rationale here is that the server may change the parameters of an |
michael@0 | 347 | experiment and want clients to pick those up. |
michael@0 | 348 | |
michael@0 | 349 | 5. Activating a previously non-applicable experiment |
michael@0 | 350 | ---------------------------------------------------- |
michael@0 | 351 | |
michael@0 | 352 | If the conditions of an experiment change or the state of the client |
michael@0 | 353 | changes to allow an experiment to transition from previously |
michael@0 | 354 | non-applicable to applicable, the experiment should be activated. |
michael@0 | 355 | |
michael@0 | 356 | For example, if a client is running version 28 and the experiment |
michael@0 | 357 | initially requires version 29 or above, the client will not mark the |
michael@0 | 358 | experiment as applicable. But if the client upgrades to version 29 or if |
michael@0 | 359 | the manifest is updated to require 28 or above, the experiment will |
michael@0 | 360 | become applicable. |
michael@0 | 361 | |
michael@0 | 362 | 6. Deactivating a previously active experiment |
michael@0 | 363 | ---------------------------------------------- |
michael@0 | 364 | |
michael@0 | 365 | If the conditions of an experiment change or the state of the client |
michael@0 | 366 | changes and an active experiment is no longer applicable, that |
michael@0 | 367 | experiment should be deactivated. |
michael@0 | 368 | |
michael@0 | 369 | 7. Calculation of sampling-based applicability |
michael@0 | 370 | ---------------------------------------------- |
michael@0 | 371 | |
michael@0 | 372 | For calculating sampling-based applicability, the client will associate |
michael@0 | 373 | a random value between ``0.0`` and ``1.0`` for each observed experiment |
michael@0 | 374 | ID. This random value will be generated the first time sampling |
michael@0 | 375 | applicability is evaluated. This random value will be persisted and used |
michael@0 | 376 | in future applicability evaluations for this experiment. |
michael@0 | 377 | |
michael@0 | 378 | By saving and re-using the value, the client is able to reliably and |
michael@0 | 379 | consistently evaluate applicability, even if the sampling threshold |
michael@0 | 380 | in the manifest changes. |
michael@0 | 381 | |
michael@0 | 382 | Clients should retain the randomly-generated sampling value for |
michael@0 | 383 | experiments that no longer appear in a manifest for a period of at least |
michael@0 | 384 | 30 days. The rationale is that if an experiment disappears and reappears |
michael@0 | 385 | from a manifest, the client will not have multiple opportunities to |
michael@0 | 386 | generate a random value that satisfies the sampling criteria. |
michael@0 | 387 | |
michael@0 | 388 | 8. Incompatible version numbers |
michael@0 | 389 | ------------------------------- |
michael@0 | 390 | |
michael@0 | 391 | If a client receives a manifest with a version number that it doesn't |
michael@0 | 392 | recognize, it should ignore the manifest. |
michael@0 | 393 | |
michael@0 | 394 | 9. Usage of old manifests |
michael@0 | 395 | ------------------------- |
michael@0 | 396 | |
michael@0 | 397 | If a client experiences an error fetching a manifest (server not |
michael@0 | 398 | available) or if the manifest is corrupt, not readable, or compatible, |
michael@0 | 399 | the client may use a previously-fetched (cached) manifest. |
michael@0 | 400 | |
michael@0 | 401 | 10. Updating XPIs |
michael@0 | 402 | ----------------- |
michael@0 | 403 | |
michael@0 | 404 | If the URL or hash of an active experiment's XPI changes, the client |
michael@0 | 405 | should fetch the new XPI, uninstall the old XPI, and install the new |
michael@0 | 406 | XPI. |
michael@0 | 407 | |
michael@0 | 408 | Examples |
michael@0 | 409 | ======== |
michael@0 | 410 | |
michael@0 | 411 | Here is an example manifest:: |
michael@0 | 412 | |
michael@0 | 413 | { |
michael@0 | 414 | "version": 1, |
michael@0 | 415 | "experiments": [ |
michael@0 | 416 | { |
michael@0 | 417 | "id": "da9d7f4f-f3f9-4f81-bacd-6f0626ffa360", |
michael@0 | 418 | "xpiURL": "https://experiments.mozilla.org/foo.xpi", |
michael@0 | 419 | "xpiHash": "sha1:cb1eb32b89d86d78b7326f416cf404548c5e0099", |
michael@0 | 420 | "startTime": 1393000000, |
michael@0 | 421 | "endTime": 1394000000, |
michael@0 | 422 | "appName": ["Firefox", "Fennec"], |
michael@0 | 423 | "minVersion": "28", |
michael@0 | 424 | "maxVersion": "30", |
michael@0 | 425 | "os": ["windows", "linux", "osx"], |
michael@0 | 426 | "jsfilter": "function filter(context) { return context.healthReportEnabled; }" |
michael@0 | 427 | } |
michael@0 | 428 | ] |
michael@0 | 429 | } |