Bug 1568277 - [ci] Add an experimental SETA optimize strategy and task to run it r=tomprince
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Thu, 15 Aug 2019 19:36:35 +0000
changeset 488356 056d9515483cd593dc1a931b4ade7ba7b143b540
parent 488355 632d943c947b359b68d7d29626ef3c51744572fd
child 488357 2b1ffc8004d5e8160bc5bb1109c9f67a003048ef
push id92705
push userahalberstadt@mozilla.com
push dateThu, 15 Aug 2019 19:44:39 +0000
treeherderautoland@056d9515483c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstomprince
bugs1568277
milestone70.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 1568277 - [ci] Add an experimental SETA optimize strategy and task to run it r=tomprince These "shadow scheduler" tasks will generate artifacts per-push on autoland. Basically, given the scheduling algorithms defined in TASKGRAPH_OPTIMIZE_STRATEGIES, which tasks *would* have been scheduled on this push. This will allow us to download the artifacts and run comparisons against the baseline to see whether things like code coverage or machine learning are making the situation better or worse. Differential Revision: https://phabricator.services.mozilla.com/D40427
taskcluster/ci/config.yml
taskcluster/ci/source-test/kind.yml
taskcluster/ci/source-test/shadow-scheduler.yml
taskcluster/taskgraph/optimize/__init__.py
taskcluster/taskgraph/optimize/seta.py
taskcluster/taskgraph/transforms/source_test.py
--- a/taskcluster/ci/config.yml
+++ b/taskcluster/ci/config.yml
@@ -110,16 +110,17 @@ treeherder:
         'langpack': 'Langpack sigatures and uploads'
         'TPS': 'Sync tests'
         'UV': 'Update verify'
         'pipfu': 'pipfile update'
         'WR': 'WebRender standalone'
         'Gd': 'Geckodriver'
         'clang': 'Clang Tidy & Format'
         'coverity': 'Coverity Static Analysis'
+        'SS': 'Shadow scheduler'
 
 index:
     products:
         - 'firefox'
         - 'fennec'
         - 'mobile'
         - 'static-analysis'
         - 'devedition'
--- a/taskcluster/ci/source-test/kind.yml
+++ b/taskcluster/ci/source-test/kind.yml
@@ -21,16 +21,17 @@ jobs-from:
     - doc.yml
     - file-metadata.yml
     - infer.yml
     - jsshell.yml
     - mozlint.yml
     - mozlint-android.yml
     - node.yml
     - python.yml
+    - shadow-scheduler.yml
     - webidl.yml
     - wpt-metadata.yml
     - wpt-manifest.yml
 
 job-defaults:
     attributes:
         retrigger: true
 
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/source-test/shadow-scheduler.yml
@@ -0,0 +1,31 @@
+# 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/.
+---
+job-defaults:
+    platform: gecko-decision/opt
+    worker-type: t-linux-xlarge
+    worker:
+        docker-image: {in-tree: "lint"}
+        max-run-time: 3600
+        artifacts:
+            - type: file
+              name: public/shadow-scheduler/optimized_tasks.list
+              path: /builds/worker/optimized_tasks.list
+    treeherder:
+        kind: other
+        tier: 3
+    require-decision-task-id: true
+    run-on-projects: ['autoland']
+    run:
+        using: mach
+        mach: taskgraph optimized -p task-id=$DECISION_TASK_ID --output-file /builds/worker/optimized_tasks.list
+        sparse-profile: taskgraph
+
+seta_10_120:
+    description: Runs the seta_10_120 experimental optimization strategy
+    treeherder:
+        symbol: SS(seta_10_120)
+    worker:
+        env:
+            TASKGRAPH_OPTIMIZE_STRATEGIES: taskgraph.optimize:seta_10_120
--- a/taskcluster/taskgraph/optimize/__init__.py
+++ b/taskcluster/taskgraph/optimize/__init__.py
@@ -305,8 +305,16 @@ class Alias(Either):
 # Trigger registration in sibling modules.
 import_sibling_modules()
 
 
 # Register composite strategies.
 register_strategy('test', args=('skip-unless-schedules', 'seta'))(Either)
 register_strategy('test-inclusive', args=('skip-unless-schedules',))(Alias)
 register_strategy('test-try', args=('skip-unless-schedules',))(Alias)
+
+
+# Experimental strategy that replaces the default SETA with a version that runs
+# all tasks every 10th push or 2 hours.
+seta_10_120 = {
+    'seta': Alias('seta_10_120'),
+    'test': Either('skip-unless-schedules', 'seta_10_120'),
+}
--- a/taskcluster/taskgraph/optimize/seta.py
+++ b/taskcluster/taskgraph/optimize/seta.py
@@ -258,20 +258,23 @@ class SETA(object):
         return label in self.low_value_tasks[project]
 
 
 # create a single instance of this class, and expose its `is_low_value_task`
 # bound method as a module-level function
 is_low_value_task = SETA().is_low_value_task
 
 
-@register_strategy('seta')
+@register_strategy('seta', args=(5, 60))
+@register_strategy('seta_10_120', args=(10, 120))
 class SkipLowValue(OptimizationStrategy):
-    push_interval = 5
-    time_interval = 60
+
+    def __init__(self, push_interval, time_interval):
+        self.push_interval = push_interval
+        self.time_interval = time_interval
 
     def should_remove_task(self, task, params, _):
         label = task.label
 
         # we would like to return 'False, None' while it's high_value_task
         # and we wouldn't optimize it. Otherwise, it will return 'True, None'
         if is_low_value_task(label,
                              params.get('project'),
--- a/taskcluster/taskgraph/transforms/source_test.py
+++ b/taskcluster/taskgraph/transforms/source_test.py
@@ -49,16 +49,18 @@ source_test_description_schema = Schema(
         job_description_schema['worker-type'],
         {'by-platform': {basestring: job_description_schema['worker-type']}},
     ),
     Required('worker'): Any(
         job_description_schema['worker'],
         {'by-platform': {basestring: job_description_schema['worker']}},
     ),
     Optional('python-version'): [int],
+    # If true, the DECISION_TASK_ID env will be populated.
+    Optional('require-decision-task-id'): bool,
 })
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def set_defaults(config, jobs):
     for job in jobs:
@@ -208,8 +210,24 @@ def handle_shell(config, jobs):
             yield job
             continue
 
         for field in fields:
             resolve_keyed_by(job, field, item_name=job['name'])
 
         del job['shell']
         yield job
+
+
+@transforms.add
+def add_decision_task_id_to_env(config, jobs):
+    """
+    Creates the `DECISION_TASK_ID` environment variable in tasks that set the
+    `require-decision-task-id` config.
+    """
+    for job in jobs:
+        if not job.pop('require-decision-task-id', False):
+            yield job
+            continue
+
+        env = job['worker'].setdefault('env', {})
+        env['DECISION_TASK_ID'] = os.environ.get('TASK_ID', '')
+        yield job