[buildbot] phase1,phase2: implement round robin builds

LEDE Commits lede-commits at lists.infradead.org
Wed Mar 17 10:40:10 GMT 2021


ynezz pushed a commit to buildbot.git, branch master:
https://git.openwrt.org/27f0b7fe926d70c4f3e9c5bb1b297b76d6e1f704

commit 27f0b7fe926d70c4f3e9c5bb1b297b76d6e1f704
Author: Petr Štetiar <ynezz at true.cz>
AuthorDate: Tue Mar 16 18:21:15 2021 +0100

    phase1,phase2: implement round robin builds
    
    Gather newest complete and not skipped build timestamps, reverse sort
    them and use that as builder priority, so the newest built targets are
    at the bottom.
    
    Signed-off-by: Petr Štetiar <ynezz at true.cz>
---
 phase1/master.cfg | 81 +++++++++++++++++++++++++++++++++++++++++++++++++------
 phase2/master.cfg | 71 +++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 140 insertions(+), 12 deletions(-)

diff --git a/phase1/master.cfg b/phase1/master.cfg
index 2ccac8e..53d1812 100644
--- a/phase1/master.cfg
+++ b/phase1/master.cfg
@@ -4,13 +4,17 @@
 import os
 import re
 import base64
-import random
 import subprocess
 import configparser
 
-from datetime import timedelta
+from dateutil.tz import tzutc
+from datetime import datetime, timedelta
+
+from twisted.internet import defer
+from twisted.python import log
 
 from buildbot import locks
+from buildbot.data import resultspec
 from buildbot.changes import filter
 from buildbot.changes.gitpoller import GitPoller
 from buildbot.config import BuilderConfig
@@ -19,6 +23,7 @@ from buildbot.plugins import schedulers
 from buildbot.plugins import steps
 from buildbot.plugins import util
 from buildbot.process import properties
+from buildbot.process import results
 from buildbot.process.factory import BuildFactory
 from buildbot.process.properties import Interpolate
 from buildbot.process.properties import Property
@@ -123,6 +128,69 @@ c['configurators'] = [util.JanitorConfigurator(
     hour=6,
 )]
 
+ at defer.inlineCallbacks
+def getNewestCompleteTime(bldr):
+	"""Returns the complete_at of the latest completed and not SKIPPED
+	build request for this builder, or None if there are no such build
+	requests. We need to filter out SKIPPED requests because we're
+	using collapseRequests=True which is unfortunately marking all
+	previous requests as complete when new buildset is created.
+
+	@returns: datetime instance or None, via Deferred
+	"""
+
+	bldrid = yield bldr.getBuilderId()
+	completed = yield bldr.master.data.get(
+			('builders', bldrid, 'buildrequests'),
+			[
+				resultspec.Filter('complete', 'eq', [True]),
+				resultspec.Filter('results', 'ne', [results.SKIPPED]),
+			],
+			order=['-complete_at'], limit=1)
+	if not completed:
+		return
+
+	return completed[0]['complete_at']
+
+ at defer.inlineCallbacks
+def prioritizeBuilders(master, builders):
+	"""Returns sorted list of builders by their last timestamp of completed and
+	not skipped build.
+
+	@returns: list of sorted builders
+	"""
+
+	def is_building(bldr):
+		return bool(bldr.building) or bool(bldr.old_building)
+
+	def bldr_info(bldr):
+		d = defer.maybeDeferred(getNewestCompleteTime, bldr)
+		d.addCallback(lambda complete_at: (complete_at, bldr))
+		return d
+
+	def bldr_sort(item):
+		(complete_at, bldr) = item
+
+		if not complete_at:
+			date = datetime.min
+			complete_at = date.replace(tzinfo=tzutc())
+
+		if is_building(bldr):
+			date = datetime.max
+			complete_at = date.replace(tzinfo=tzutc())
+
+		return (complete_at, bldr.name)
+
+	results = yield defer.gatherResults([bldr_info(bldr) for bldr in builders])
+	results.sort(key=bldr_sort)
+
+	for r in results:
+		log.msg("prioritizeBuilders: {:>20} complete_at: {}".format(r[1].name, r[0]))
+
+	return [r[1] for r in results]
+
+c['prioritizeBuilders'] = prioritizeBuilders
+
 ####### CHANGESOURCES
 
 work_dir = os.path.abspath(ini.get("general", "workdir") or ".")
@@ -456,13 +524,10 @@ def GetNextBuild(builder, requests):
 	for r in requests:
 		if r.properties and r.properties.hasProperty("tag"):
 			return r
-	return requests[0]
 
-def prioritizeBuilders(buildmaster, builders):
-	random.shuffle(builders)
-	return builders
-
-c['prioritizeBuilders'] = prioritizeBuilders
+	r = requests[0]
+	log.msg("GetNextBuild: {:>20} id: {} bsid: {}".format(builder.name, r.id, r.bsid))
+	return r
 
 def MakeEnv(overrides=None, tryccache=False):
 	env = {
diff --git a/phase2/master.cfg b/phase2/master.cfg
index ac44fcd..a54c191 100644
--- a/phase2/master.cfg
+++ b/phase2/master.cfg
@@ -8,15 +8,21 @@ import random
 import subprocess
 import configparser
 
-from datetime import timedelta
+from dateutil.tz import tzutc
+from datetime import datetime, timedelta
+
+from twisted.internet import defer
+from twisted.python import log
 
 from buildbot import locks
+from buildbot.data import resultspec
 from buildbot.changes import filter
 from buildbot.changes.gitpoller import GitPoller
 from buildbot.config import BuilderConfig
 from buildbot.plugins import schedulers
 from buildbot.plugins import steps
 from buildbot.plugins import util
+from buildbot.process import results
 from buildbot.process.factory import BuildFactory
 from buildbot.process.properties import Property
 from buildbot.process.properties import WithProperties
@@ -319,9 +325,66 @@ def UsignSec2Pub(seckey, comment="untrusted comment: secret key"):
 def IsSharedWorkdir(step):
 	return bool(step.getProperty("shared_wd"))
 
-def prioritizeBuilders(buildmaster, builders):
-	random.shuffle(builders)
-	return builders
+ at defer.inlineCallbacks
+def getNewestCompleteTime(bldr):
+	"""Returns the complete_at of the latest completed and not SKIPPED
+	build request for this builder, or None if there are no such build
+	requests. We need to filter out SKIPPED requests because we're
+	using collapseRequests=True which is unfortunately marking all
+	previous requests as complete when new buildset is created.
+
+	@returns: datetime instance or None, via Deferred
+	"""
+
+	bldrid = yield bldr.getBuilderId()
+	completed = yield bldr.master.data.get(
+			('builders', bldrid, 'buildrequests'),
+			[
+				resultspec.Filter('complete', 'eq', [True]),
+				resultspec.Filter('results', 'ne', [results.SKIPPED]),
+			],
+			order=['-complete_at'], limit=1)
+	if not completed:
+		return
+
+	return completed[0]['complete_at']
+
+ at defer.inlineCallbacks
+def prioritizeBuilders(master, builders):
+	"""Returns sorted list of builders by their last timestamp of completed and
+	not skipped build.
+
+	@returns: list of sorted builders
+	"""
+
+	def is_building(bldr):
+		return bool(bldr.building) or bool(bldr.old_building)
+
+	def bldr_info(bldr):
+		d = defer.maybeDeferred(getNewestCompleteTime, bldr)
+		d.addCallback(lambda complete_at: (complete_at, bldr))
+		return d
+
+	def bldr_sort(item):
+		(complete_at, bldr) = item
+
+		if not complete_at:
+			date = datetime.min
+			complete_at = date.replace(tzinfo=tzutc())
+
+		if is_building(bldr):
+			date = datetime.max
+			complete_at = date.replace(tzinfo=tzutc())
+
+		return (complete_at, bldr.name)
+
+	results = yield defer.gatherResults([bldr_info(bldr) for bldr in builders])
+	results.sort(key=bldr_sort)
+
+	for r in results:
+		log.msg("prioritizeBuilders: {:>20} complete_at: {}".format(r[1].name, r[0]))
+
+	return [r[1] for r in results]
 
 c['prioritizeBuilders'] = prioritizeBuilders
 c['builders'] = []



More information about the lede-commits mailing list