launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #17884
[Merge] lp:~wgrant/launchpad/bug-1422562 into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/bug-1422562 into lp:launchpad.
Commit message:
Only retrieve the top 10 items in the build queue at a time, fixing buildd-manager performance with deep queues.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1422562 in Launchpad itself: "Builder._findBuildCandidate is very slow"
https://bugs.launchpad.net/launchpad/+bug/1422562
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/bug-1422562/+merge/249920
Only retrieve the top 10 items in the build queue at a time.
Previously we'd retrieve every candidate BuildQueue.id in order and
invoke postprocessCandidate on each until we found one that passed. But
in the real world there's always a valid candidate near the top of the
queue, postprocessCandidate unqueues any jobs that it rejects, and we
want bounded per-cycle query times anyway. If we do end up with 1000
unusable jobs at the top of the queue, we just have to wait a minute or
so for them to be killed, 10 at a time.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/bug-1422562 into lp:launchpad.
=== modified file 'lib/lp/buildmaster/model/builder.py'
--- lib/lp/buildmaster/model/builder.py 2014-06-26 12:52:06 +0000
+++ lib/lp/buildmaster/model/builder.py 2015-02-17 05:37:28 +0000
@@ -19,8 +19,14 @@
StringCol,
)
from storm.expr import (
+ And,
Coalesce,
Count,
+ Desc,
+ Exists,
+ Or,
+ Select,
+ SQL,
Sum,
)
from storm.properties import Int
@@ -43,6 +49,7 @@
)
from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJobSet
from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
+from lp.buildmaster.model.buildfarmjob import BuildFarmJob
from lp.buildmaster.model.buildqueue import (
BuildQueue,
specific_build_farm_job_sources,
@@ -244,45 +251,38 @@
qualified_query %= sub_query
return qualified_query
- logger = self._getSlaveScannerLogger()
- candidate = None
-
- general_query = """
- SELECT buildqueue.id FROM buildqueue, buildfarmjob
- WHERE
- buildfarmjob.id = buildqueue.build_farm_job
- AND buildqueue.status = %s
- AND (
- -- The processor values either match or the candidate
- -- job is processor-independent.
- buildqueue.processor IN (
- SELECT processor FROM BuilderProcessor
- WHERE builder = %s) OR
- buildqueue.processor IS NULL)
- AND buildqueue.virtualized = %s
- AND buildqueue.builder IS NULL
- """ % sqlvalues(
- BuildQueueStatus.WAITING, self, self.virtualized)
- order_clause = " ORDER BY buildqueue.lastscore DESC, buildqueue.id"
-
- extra_queries = []
+ job_type_conditions = []
job_sources = specific_build_farm_job_sources()
for job_type, job_source in job_sources.iteritems():
query = job_source.addCandidateSelectionCriteria(
self.processor, self.virtualized)
- if query == '':
- # This job class does not need to refine candidate jobs
- # further.
- continue
-
- # The sub-query should only apply to jobs of the right type.
- extra_queries.append(qualify_subquery(job_type, query))
- query = ' AND '.join([general_query] + extra_queries) + order_clause
+ if query:
+ job_type_conditions.append(
+ Or(
+ BuildFarmJob.job_type != job_type,
+ Exists(SQL(query))))
store = IStore(self.__class__)
- candidate_jobs = store.execute(query).get_all()
+ candidate_jobs = store.using(BuildQueue, BuildFarmJob).find(
+ (BuildQueue.id,),
+ BuildFarmJob.id == BuildQueue._build_farm_job_id,
+ BuildQueue.status == BuildQueueStatus.WAITING,
+ Or(
+ BuildQueue.processorID.is_in(Select(
+ BuilderProcessor.processor_id, tables=[BuilderProcessor],
+ where=BuilderProcessor.builder == self)),
+ BuildQueue.processor == None),
+ BuildQueue.virtualized == self.virtualized,
+ BuildQueue.builder == None,
+ And(*job_type_conditions)
+ ).order_by(Desc(BuildQueue.lastscore), BuildQueue.id)
- for (candidate_id,) in candidate_jobs:
+ logger = self._getSlaveScannerLogger()
+ # Only try the first handful of jobs. It's much easier on the
+ # database, the chance of a large prefix of the queue being
+ # bad candidates is negligible, and we want reasonably bounded
+ # per-cycle performance even if the prefix is large.
+ for (candidate_id,) in candidate_jobs[:10]:
candidate = getUtility(IBuildQueueSet).get(candidate_id)
job_source = job_sources[
removeSecurityProxy(candidate)._build_farm_job.job_type]
Follow ups