]> git.ipfire.org Git - pbs.git/commitdiff
jobs: Always select the fastest online builder to build a job
authorMichael Tremer <michael.tremer@ipfire.org>
Mon, 30 Oct 2017 10:00:50 +0000 (10:00 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 30 Oct 2017 10:00:50 +0000 (10:00 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/buildservice/builders.py
src/buildservice/jobs.py

index 96a0efcd287a6c056014d6bc21080b65282750fe..a2168207a8ba4fc90a810c553ce0666d2b634d3b 100644 (file)
@@ -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):
index 7b678f3a14d35f19b200716090871a91f2b2e6ea..8ee698014941884f9d664cc2860d7dcf4e746d2f 100644 (file)
@@ -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