Bug 985418 - prioritize l10n nightlies below everything, regardless of branch. r=catlee
--- a/mozilla/master_common.py
+++ b/mozilla/master_common.py
@@ -59,52 +59,64 @@ BRANCH_PRIORITIES = {
# Idle jobs like fuzzing
'idle': 6,
}
# List of (regular expression, priority) tuples. If no expression matches the
# builder name, a priority of 100 is used. Lower priority values get run
# earlier.
BUILDER_PRIORITIES = [
- (re.compile('l10n'), 150),
+ (re.compile('l10n'), 25),
]
def builderPriority(builder, request):
"""
- Our builder sorting function
- Returns (branch_priority, request_priority, builder_priority, submitted_at)
+ Builder sorting function
+ Returns (release, magic_number, submitted_at)
+ where magic_number = 5 * branch_priority + req_priority + builder_priority
- NB: lower values returned by this function correspond to a "higher" or
- "better" priority
+ req_priority is the negative value of the request priority in the
+ database: increasing the request priority increases the builderPriority()
+
+ builder_priority, branch_priority, submitted_at: as they increase their
+ values, the final priority gets less important
"""
# larger request priorities correspond to more important jobs
# see buildbot's DBConnector.get_unclaimed_buildrequests which sorts the
# buildrequests by request.priority DESC
# since lower is better for us, we should negate this value
req_priority = -request[1]
submitted_at = request[2]
- branch_priority = DEFAULT_BRANCH_PRIORITY
+ # is this a release?
+ release = 0
if builder.builder_status.category.startswith('release'):
- branch_priority = 0
- elif builder.properties and 'branch' in builder.properties:
- for branch, p in BRANCH_PRIORITIES.iteritems():
- if branch in builder.properties['branch']:
- branch_priority = p
- break
+ # this is a release, set it to a negative value
+ # lower value means higher priority
+ release = -1
- # Default builder priority is 100
- builder_priority = 100
- for exp, p in BUILDER_PRIORITIES:
+ try:
+ branch_priority = BRANCH_PRIORITIES.get(builder.properties['branch'],
+ DEFAULT_BRANCH_PRIORITY)
+ except (AttributeError, KeyError):
+ # AttributeError => builder has no properties
+ # KeyError => builder has no 'branch'
+ branch_priority = DEFAULT_BRANCH_PRIORITY
+
+ # Default builder priority is 0
+ builder_priority = 0
+ for exp, priority in BUILDER_PRIORITIES:
if exp.search(builder.name):
- builder_priority = p
+ builder_priority = priority
break
+ return (release,
+ branch_priority * 5 + req_priority + builder_priority,
+ submitted_at)
- return branch_priority, req_priority, builder_priority, submitted_at
cached_twlog = None
def getTwlog():
global cached_twlog
if cached_twlog:
return cached_twlog
else:
from twisted.python import log as twlog
new file mode 100644
--- /dev/null
+++ b/mozilla/test/test_builderPriority.py
@@ -0,0 +1,278 @@
+"""test for master.common builderPriority"""
+import unittest
+import time
+import master_common
+
+
+class builderStatusStub(object):
+ """builder status stub"""
+ def __init__(self, category):
+ self.category = category
+
+
+def builderPriorityStub(branch):
+ """builder priority stub"""
+ status = {}
+ status['branch'] = branch
+ return status
+
+
+class builderStub(object):
+ """builder stub"""
+ def __init__(self, name, branch, category):
+ self.properties = builderPriorityStub(branch)
+ self.name = name
+ self.category = category
+ self.builder_status = builderStatusStub(category)
+
+ def get_priority_request(self, request):
+ """returns the calculated priority of a builder/request"""
+ return master_common.builderPriority(self, request)
+
+ def __repr__(self):
+ msg = "name: {0}\n".format(self.name)
+ msg = "{0}branch: {1}\n".format(msg, self.properties['branch'])
+ msg = "{0}category: {1}".format(msg, self.category)
+ return msg
+
+
+def requestStub(priority, submitted_at):
+ """request stub"""
+ return (None, priority, submitted_at)
+
+
+class builderProrityTest(unittest.TestCase):
+ """tests for builderPriority"""
+ def setUp(self):
+ """Prepares a bunch of builders/priorities"""
+ # low priority branches:
+ # generic builder, oak, release
+ self.mozilla_beta = builderStub(name='mozilla beta builder',
+ branch='mozilla-beta',
+ category='release')
+ # generic builder, oak, non release
+ self.oak_builder = builderStub(name='oak release',
+ branch='oak',
+ category='non-release')
+ # high priority branches:
+ # generic builder, mozilla-release, release
+ self.mr_release = builderStub(name='release job',
+ branch='mozilla-release',
+ category='release')
+
+ # generic builder, mozilla-release, non release
+ self.mr_builder = builderStub(name='generic release job',
+ branch='mozilla-release',
+ category='non-release')
+
+ # l10n stuff... very low priority
+ self.l10n_builder = builderStub(name='l10n',
+ branch='oak',
+ category='non-release')
+
+ # requests
+ # higher priority means it's more important
+ submitted_at = int(time.time())
+ last_hour = submitted_at - 3600
+ last_day = submitted_at - (3600 * 24)
+ last_week = submitted_at - (3600 * 24 * 7)
+ epoch = 0
+
+ self.high_priority_request = requestStub(9, submitted_at)
+ self.low_priority_request = requestStub(1, submitted_at)
+
+ self.one_hour_old_hp_request = requestStub(9, last_hour)
+ self.one_hour_old_lp_request = requestStub(1, last_hour)
+
+ self.one_day_old_hp_request = requestStub(9, last_day)
+ self.one_day_old_lp_request = requestStub(1, last_day)
+
+ self.one_week_old_hp_request = requestStub(9, last_week)
+ self.one_week_old_lp_request = requestStub(1, last_week)
+
+ self.epoch_old_hp_request = requestStub(9, epoch)
+ self.epoch_old_lp_request = requestStub(1, epoch)
+
+ def test_stablity(self):
+ """same input, same output"""
+ mozilla_beta = self.oak_builder
+ request = self.high_priority_request
+ priority_0 = mozilla_beta.get_priority_request(request)
+ priority_1 = mozilla_beta.get_priority_request(request)
+ self.assertTrue(priority_0 == priority_1)
+
+ def test_release(self):
+ """Release gets the precedence"""
+ oak_builder = self.oak_builder
+ mozilla_beta = self.mozilla_beta
+
+ request = self.high_priority_request
+
+ # same request, different builders
+ priority_0 = mozilla_beta.get_priority_request(request)
+ priority_1 = oak_builder.get_priority_request(request)
+ self.assertTrue(priority_0 < priority_1)
+
+ def test_branch_priority(self):
+ """same request priority, different branches"""
+ mozilla_beta = self.mozilla_beta
+ oak_builder = self.oak_builder
+
+ # request
+ request = self.low_priority_request
+
+ priority_0 = mozilla_beta.get_priority_request(request)
+ priority_1 = oak_builder.get_priority_request(request)
+ self.assertTrue(priority_0 < priority_1)
+
+ def test_timestamp_priority(self):
+ """same builder, same priority different submitted_at"""
+ oak_builder = self.oak_builder
+ request_1 = self.one_hour_old_lp_request
+ request_2 = self.low_priority_request
+
+ priority_0 = oak_builder.get_priority_request(request_1)
+ priority_1 = oak_builder.get_priority_request(request_2)
+ self.assertTrue(priority_0 < priority_1)
+
+ request_1 = self.one_week_old_hp_request
+ request_2 = self.high_priority_request
+ priority_0 = oak_builder.get_priority_request(request_1)
+ priority_1 = oak_builder.get_priority_request(request_2)
+ self.assertTrue(priority_0 < priority_1)
+
+ def test_request_priority(self):
+ """same builder, different priority requests"""
+ oak_builder = self.oak_builder
+
+ low_priority_request = self.low_priority_request
+ high_priority_request = self.high_priority_request
+
+ priority_0 = oak_builder.get_priority_request(high_priority_request)
+ priority_1 = oak_builder.get_priority_request(low_priority_request)
+ self.assertTrue(priority_0 < priority_1)
+
+ def test_release_priority(self):
+ """release builder vs generic builder, release wins regardless other
+ builder gets higher priority request"""
+ mr_release = self.mr_release
+ oak_builder = self.oak_builder
+
+ low_priority_request = self.low_priority_request
+ priority_0 = mr_release.get_priority_request(low_priority_request)
+ priority_1 = oak_builder.get_priority_request(low_priority_request)
+ self.assertTrue(priority_0 < priority_1)
+
+ # now increase oak_builder priority
+ high_priority_request = self.high_priority_request
+ priority_1 = oak_builder.get_priority_request(high_priority_request)
+ self.assertTrue(priority_0 < priority_1)
+
+ # now move back in time oak_builder
+ # 1 hour
+ one_hour_old_hp_request = self.one_hour_old_hp_request
+ priority_1 = oak_builder.get_priority_request(one_hour_old_hp_request)
+ self.assertTrue(priority_0 < priority_1)
+
+ # 1 day
+ one_day_old_hp_request = self.one_day_old_hp_request
+ priority_1 = oak_builder.get_priority_request(one_day_old_hp_request)
+ self.assertTrue(priority_0 < priority_1)
+
+ # 1 week
+ one_week_old_hp_request = self.one_week_old_hp_request
+ priority_1 = oak_builder.get_priority_request(one_week_old_hp_request)
+ self.assertTrue(priority_0 < priority_1)
+
+ # well this is a lot of time
+ epoch_old_hp_request = self.epoch_old_hp_request
+ priority_1 = oak_builder.get_priority_request(epoch_old_hp_request)
+ self.assertTrue(priority_0 < priority_1)
+
+ # if you are here, time is not affecting the ordering,
+ # update builder type (was mozilla-release vs mozilla-release)
+ # now it's mozilla-release vs mozilla-beta
+ mozilla_beta = self.mozilla_beta
+ priority_1 = mozilla_beta.get_priority_request(epoch_old_hp_request)
+ self.assertTrue(priority_0 < priority_1)
+
+ # mr_release always wins
+ mr_builder = self.mr_builder
+ priority_1 = mr_builder.get_priority_request(epoch_old_hp_request)
+ self.assertTrue(priority_0 < priority_1)
+
+ def test_l10n_always_last(self):
+ """l10n builders get lowest priority"""
+ mr_builder = self.mr_builder
+ oak_builder = self.oak_builder
+ l10n_builder = self.l10n_builder
+
+ high_priority_request = self.high_priority_request
+ low_priority_request = self.low_priority_request
+
+ # different builders same priorities
+ priority_0 = mr_builder.get_priority_request(high_priority_request)
+ priority_1 = oak_builder.get_priority_request(high_priority_request)
+ priority_2 = l10n_builder.get_priority_request(high_priority_request)
+ expected = [priority_0, priority_1, priority_2]
+ import random
+ # reverse expected results and the sort it,
+ # result must be identical to expected results
+ result = sorted(reversed(expected))
+ self.assertTrue(result == expected)
+
+ # different builders, l10n has the highest priority
+ # but it must get the lowest overall prioritization
+ priority_0 = mr_builder.get_priority_request(low_priority_request)
+ priority_1 = oak_builder.get_priority_request(low_priority_request)
+ priority_2 = l10n_builder.get_priority_request(high_priority_request)
+ expected = [priority_0, priority_1, priority_2]
+ # shuffle elements
+ result = sorted(expected, key=lambda *args: random.random())
+ # and now sort them
+ result = sorted(result)
+ self.assertTrue(result == expected)
+
+ def test_slaveapi_priority_increase(self):
+ """Same builder and we compare a random priority (rp) with a list
+ of priorities. Results are stable and unique"""
+
+ import random
+ oak_builder = self.oak_builder
+ submitted_at = 0
+
+ priorities_range = range(5, 200)
+ random.shuffle(priorities_range)
+ random_priority = priorities_range.pop(0)
+ request = requestStub(random_priority, submitted_at)
+
+ priority_0 = oak_builder.get_priority_request(request)
+
+ lesser = []
+ greater = []
+ for priority in priorities_range:
+ priority_1 = oak_builder.get_priority_request(
+ requestStub(priority, submitted_at))
+ if priority_1 < priority_0:
+ # req has an higher priority request than request
+ greater.append(priority)
+ elif priority_1 > priority_0:
+ lesser.append(priority)
+ else:
+ # this means that we have two different values that generate
+ # the same tuple, make this test fail
+ self.assertTrue(priority_0 > priority_1)
+ # no repetitions
+ self.assertTrue(len(greater) == len(set(greater)))
+ self.assertTrue(len(lesser) == len(set(lesser)))
+ # disjoint sets
+ self.assertTrue(set(greater).isdisjoint(set(lesser)))
+ # random_priority is not part of any list
+ self.assertFalse(random_priority in greater)
+ self.assertFalse(random_priority in lesser)
+ #
+ self.assertTrue(min(greater) > max(lesser))
+
+
+if __name__ == '__main__':
+ unittest.main()
--- a/test-masters.sh
+++ b/test-masters.sh
@@ -6,29 +6,52 @@ exit=0
# even though it isn't fully used, the config check does require a valid
# shared memory setup AT THE DEFAULT LOCATION. If you're running on a
# laptop, that may not exist. Fail early.
#
# OSX note: it "works" (for test-masters purposes) to just create the
# directory, even though that isn't how shared memory is
# handled on OSX. The directories must be owned by the id
# running the tests.
+#
+# if you want to run trial tests without needing to execute the full test suite
+# call this script with: run-test
+
shm=(/dev/shm)
good_shm=true
for needed_dir in ${shm[@]}; do
if ! test -w $needed_dir; then
echo 1>&2 "No shm setup, please create writable directory '$needed_dir'"
good_shm=false
fi
done
$good_shm || exit 1
WORK=test-output
mkdir $WORK 2>/dev/null
+
+function run_unittests {
+for dir in mozilla mozilla-tests; do
+ cd $dir
+ for f in test/*.py; do
+ trial $f || exit=1
+ done
+ rm -rf _trial_temp
+ cd ..
+done
+}
+
+if [ "$1" == "run-tests" ]
+then
+ # run trial and exit
+ run_unittests
+ exit
+fi
+
actioning="Checking"
MASTERS_JSON_URL="${MASTERS_JSON_URL:-https://hg.mozilla.org/build/tools/raw-file/tip/buildfarm/maintenance/production-masters.json}"
atexit=()
trap 'for cmd in "${atexit[@]}"; do eval $cmd; done' EXIT
# I have had problems where a whole bunch of parallel HTTP requests caused
# errors (?), so fetch it once here and pass it in.
@@ -66,22 +89,15 @@ check_for_virtual_env() {
if [ -s $FAILFILE ]; then
echo "*** $(wc -l < $FAILFILE) master tests failed ***" >&2
echo "Failed masters:" >&2
sed -e 's/^/ /' "$FAILFILE" >&2
check_for_virtual_env
exit 1
fi
-for dir in mozilla mozilla-tests; do
- cd $dir
- for f in test/*.py; do
- trial $f || exit=1
- done
- rm -rf _trial_temp
- cd ..
-done
+run_unittests
if test "$exit" -ne 0 ; then
check_for_virtual_env
fi
exit $exit