From: Michael Tremer Date: Mon, 30 Oct 2017 10:00:50 +0000 (+0000) Subject: jobs: Always select the fastest online builder to build a job X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6d7cbfcd8797dcd9dafcf4b90b0d751ac315ba1d;p=pbs.git jobs: Always select the fastest online builder to build a job Signed-off-by: Michael Tremer --- diff --git a/src/buildservice/builders.py b/src/buildservice/builders.py index 96a0efcd..a2168207 100644 --- a/src/buildservice/builders.py +++ b/src/buildservice/builders.py @@ -76,6 +76,18 @@ class Builders(base.Object): return self._get_builder("SELECT * FROM builders \ WHERE name = %s AND deleted IS FALSE", name) + def get_for_arch(self, arch): + # noarch can be built on any builder + if arch == "noarch": + return self + + builds = self._get_builders("SELECT builders.* FROM builders \ + LEFT JOIN arches_compat ON builders.cpu_arch = arches_compat.native_arch \ + WHERE (builders.cpu_arch = %s OR arches_compat.build_arch = %s) \ + AND builders.deleted IS FALSE", arch, arch) + + return builds + def get_history(self, limit=None, offset=None, builder=None, user=None): query = "SELECT * FROM builders_history" args = [] @@ -162,6 +174,12 @@ class Builder(base.DataObject): description = property(lambda s: s.data.description or "", set_description) + def is_online(self): + """ + Returns True if the builder is online + """ + return self.keepalive >= datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + @property def keepalive(self): """ @@ -247,6 +265,20 @@ class Builder(base.DataObject): def passphrase(self): return self.data.passphrase + @property + def performance_index(self): + """ + Returns a number that determines how "fast" the builder is + """ + # XXX needs to be something better + index = self.cpu_bogomips + + # We devide the performance index by the number of already running + # builds to avoid that the fastest builder always gets all the jobs + index /= len(self.active_jobs) + 1 + + return index + # Load average @property @@ -379,13 +411,24 @@ class Builder(base.DataObject): # Don't return anything if the builder has already too many jobs running if self.too_many_jobs: + logging.debug("%s has too many jobs running" % self) return for job in self.jobqueue: + logging.debug("Looking at %s..." % job) # Only allow building test jobs in test mode if self.testmode and not job.test: continue + # If we are the fastest builder to handle this job, we will + # get it. + if job.candidate_builders: + fastest_builder = job.candidate_builders.pop(0) + + if not self == fastest_builder: + logging.debug("We are not the fastest builder for this job (%s is)" % fastest_builder) + continue + return job def get_history(self, *args, **kwargs): diff --git a/src/buildservice/jobs.py b/src/buildservice/jobs.py index 7b678f3a..8ee69801 100644 --- a/src/buildservice/jobs.py +++ b/src/buildservice/jobs.py @@ -333,6 +333,19 @@ class Job(base.DataObject): builder = lazy_property(get_builder, set_builder) + @lazy_property + def candidate_builders(self): + """ + Returns all active builders that could build this job + """ + builders = self.backend.builders.get_for_arch(self.arch) + + # Remove all builders that are not available + builders = (b for b in builders if b.enabled and b.is_online()) + + # Sort them by the fastest builder first + return sorted(builders, key=lambda b: -b.performance_index) + @property def arch(self): return self.data.arch