Files
mongo/buildscripts/evergreen_task_timeout.py
Ryan Egesdahl daa41cae38 SERVER-60221 Merge Windows suggested build variants into required
We've fixed many of the reasons building and testing on Windows was
slow, which was the reason we had disabled some of the more expensive
tests on its "required" build variants. The tests that were only in the
"suggested" build variants have been merged into the "required" build
variants.
2021-10-21 22:20:55 +00:00

164 lines
6.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""Determine the timeout value a task should use in evergreen."""
import argparse
import math
import sys
from datetime import timedelta
from typing import Optional
import yaml
COMMIT_QUEUE_ALIAS = "__commit_queue"
UNITTEST_TASK = "run_unittests"
COMMIT_QUEUE_TIMEOUT = timedelta(minutes=40)
DEFAULT_REQUIRED_BUILD_TIMEOUT = timedelta(hours=1, minutes=20)
DEFAULT_NON_REQUIRED_BUILD_TIMEOUT = timedelta(hours=2)
# 2x the longest "run tests" phase for unittests as of c9bf1dbc9cc46e497b2f12b2d6685ef7348b0726,
# which is 5 mins 47 secs, excluding outliers below
UNITTESTS_TIMEOUT = timedelta(minutes=12)
SPECIFIC_TASK_OVERRIDES = {
"linux-64-debug": {"auth": timedelta(minutes=60)},
"enterprise-windows-all-feature-flags-suggested": {
"replica_sets_jscore_passthrough": timedelta(hours=3),
"replica_sets_update_v1_oplog_jscore_passthrough": timedelta(hours=2, minutes=30),
},
"enterprise-windows-required": {
"replica_sets_jscore_passthrough": timedelta(hours=3),
"replica_sets_update_v1_oplog_jscore_passthrough": timedelta(hours=2, minutes=30),
},
"enterprise-windows-inmem": {"replica_sets_jscore_passthrough": timedelta(hours=3), },
"enterprise-windows": {"replica_sets_jscore_passthrough": timedelta(hours=3), },
"windows-debug-suggested": {
"replica_sets_initsync_jscore_passthrough": timedelta(hours=2, minutes=30),
"replica_sets_jscore_passthrough": timedelta(hours=2, minutes=30),
"replica_sets_update_v1_oplog_jscore_passthrough": timedelta(hours=2, minutes=30),
},
"windows": {
"replica_sets": timedelta(hours=3),
"replica_sets_jscore_passthrough": timedelta(hours=2, minutes=30),
},
"ubuntu1804-debug-suggested": {"replica_sets_jscore_passthrough": timedelta(hours=3), },
"enterprise-rhel-80-64-bit-coverage": {
"replica_sets_jscore_passthrough": timedelta(hours=2, minutes=30),
},
"macos": {"replica_sets_jscore_passthrough": timedelta(hours=2, minutes=30), },
"enterprise-macos": {"replica_sets_jscore_passthrough": timedelta(hours=2, minutes=30), },
# unittests outliers
# repeated execution runs a suite 10 times
"linux-64-repeated-execution": {UNITTEST_TASK: 10 * UNITTESTS_TIMEOUT},
# some of the a/ub/t san variants need a little extra time
"enterprise-ubuntu2004-debug-tsan": {UNITTEST_TASK: 2 * UNITTESTS_TIMEOUT},
"ubuntu1804-asan": {UNITTEST_TASK: 2 * UNITTESTS_TIMEOUT},
"ubuntu1804-ubsan": {UNITTEST_TASK: 2 * UNITTESTS_TIMEOUT},
"ubuntu1804-debug-asan": {UNITTEST_TASK: 2 * UNITTESTS_TIMEOUT},
"ubuntu1804-debug-aubsan-lite": {UNITTEST_TASK: 2 * UNITTESTS_TIMEOUT},
"ubuntu1804-debug-ubsan": {UNITTEST_TASK: 2 * UNITTESTS_TIMEOUT},
}
def _is_required_build_variant(build_variant: str) -> bool:
"""
Determine if the given build variants is a required build variant.
:param build_variant: Name of build variant to check.
:return: True if the given build variant is required.
"""
return build_variant.endswith("-required")
def _has_override(variant: str, task_name: str) -> bool:
"""
Determine if the given task has a timeout override.
:param variant: Build Variant task is running on.
:param task_name: Task to check.
:return: True if override exists for task.
"""
return variant in SPECIFIC_TASK_OVERRIDES and task_name in SPECIFIC_TASK_OVERRIDES[variant]
def determine_timeout(task_name: str, variant: str, idle_timeout: Optional[timedelta] = None,
exec_timeout: Optional[timedelta] = None, evg_alias: str = '') -> timedelta:
"""
Determine what exec timeout should be used.
:param task_name: Name of task being run.
:param variant: Name of build variant being run.
:param idle_timeout: Idle timeout if specified.
:param exec_timeout: Override to use for exec_timeout or 0 if no override.
:param evg_alias: Evergreen alias running the task.
:return: Exec timeout to use for running task.
"""
determined_timeout = DEFAULT_NON_REQUIRED_BUILD_TIMEOUT
if exec_timeout and exec_timeout.total_seconds() != 0:
determined_timeout = exec_timeout
elif task_name == UNITTEST_TASK and not _has_override(variant, task_name):
determined_timeout = UNITTESTS_TIMEOUT
elif evg_alias == COMMIT_QUEUE_ALIAS:
determined_timeout = COMMIT_QUEUE_TIMEOUT
elif _has_override(variant, task_name):
determined_timeout = SPECIFIC_TASK_OVERRIDES[variant][task_name]
elif _is_required_build_variant(variant):
determined_timeout = DEFAULT_REQUIRED_BUILD_TIMEOUT
# The timeout needs to be at least as large as the idle timeout.
if idle_timeout and determined_timeout.total_seconds() < idle_timeout.total_seconds():
return idle_timeout
return determined_timeout
def output_timeout(task_timeout: timedelta, output_file: Optional[str]) -> None:
"""
Output timeout configuration to the specified location.
:param task_timeout: Timeout to output.
:param output_file: Location of output file to write.
"""
output = {
"exec_timeout_secs": math.ceil(task_timeout.total_seconds()),
}
if output_file:
with open(output_file, "w") as outfile:
yaml.dump(output, stream=outfile, default_flow_style=False)
yaml.dump(output, stream=sys.stdout, default_flow_style=False)
def main():
"""Determine the timeout value a task should use in evergreen."""
parser = argparse.ArgumentParser(description=main.__doc__)
parser.add_argument("--task-name", dest="task", required=True, help="Task being executed.")
parser.add_argument("--build-variant", dest="variant", required=True,
help="Build variant task is being executed on.")
parser.add_argument("--evg-alias", dest="evg_alias", required=True,
help="Evergreen alias used to trigger build.")
parser.add_argument("--timeout", dest="timeout", type=int, help="Timeout to use (in sec).")
parser.add_argument("--exec-timeout", dest="exec_timeout", type=int,
help="Exec timeout ot use (in sec).")
parser.add_argument("--out-file", dest="outfile", help="File to write configuration to.")
options = parser.parse_args()
timeout_override = timedelta(seconds=options.timeout) if options.timeout else None
exec_timeout_override = timedelta(
seconds=options.exec_timeout) if options.exec_timeout else None
task_timeout = determine_timeout(options.task, options.variant, timeout_override,
exec_timeout_override, options.evg_alias)
output_timeout(task_timeout, options.outfile)
if __name__ == "__main__":
main()