launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #05964
[Merge] lp:~rvb/launchpad/builders-timeout-903827 into lp:launchpad
Raphaël Badin has proposed merging lp:~rvb/launchpad/builders-timeout-903827 into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #903827 in Launchpad itself: "https://launchpad.net/builders timeout"
https://bugs.launchpad.net/launchpad/+bug/903827
For more details, see:
https://code.launchpad.net/~rvb/launchpad/builders-timeout-903827/+merge/86029
This branch caches the various objects related to the builds displayed on the builders' homepage (launchpad.net/builders) prior to displaying the page.
= Summary =
This branch also:
- extracts the methods (previously defined as eager_load inner methods) used to cache the various objects related to the three possible types of build in order to reuse them (the new name for these method is preloadBuildsData).
- changes a few properties into cached properties so that they can be populated in bulk.
- refactors the tests in lib/lp/soyuz/browser/tests/test_builder_views.py to reuse build creation method from there.
= Tests =
./bin/test -vvc test_builder test_builders_binary_package_build_query_count
./bin/test -vvc test_builder test_builders_recipe_build_query_count
./bin/test -vvc test_builder test_builders_translation_template_build_query_count
= Q/A =
No repeated statements should be performed by launchpad.net/builders.
--
https://code.launchpad.net/~rvb/launchpad/builders-timeout-903827/+merge/86029
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/launchpad/builders-timeout-903827 into lp:launchpad.
=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2011-12-02 14:33:58 +0000
+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2011-12-16 12:18:28 +0000
@@ -140,6 +140,13 @@
has an entry associated with `job`.
"""
+ def getByJobs(jobs):
+ """Get the specific `IBuildFarmJob`s for the given `Job`s.
+
+ Invoked on the specific `IBuildFarmJob`-implementing class that
+ has entries associated with `job`s.
+ """
+
def generateSlaveBuildCookie():
"""Produce a cookie for the slave as a token of the job it's doing.
=== modified file 'lib/lp/buildmaster/model/builder.py'
--- lib/lp/buildmaster/model/builder.py 2011-12-13 13:33:04 +0000
+++ lib/lp/buildmaster/model/builder.py 2011-12-16 12:18:28 +0000
@@ -498,7 +498,7 @@
# XXX 2010-08-24 Julian bug=623281
# This should not be a property! It's masking a complicated query.
- @property
+ @cachedproperty
def currentjob(self):
"""See IBuilder"""
return getUtility(IBuildQueueSet).getByBuilder(self)
=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
--- lib/lp/buildmaster/model/buildfarmjob.py 2011-12-08 11:57:55 +0000
+++ lib/lp/buildmaster/model/buildfarmjob.py 2011-12-16 12:18:28 +0000
@@ -160,12 +160,28 @@
"""
raise NotImplementedError
+ @staticmethod
+ def preloadBuildFarmJobs(jobs):
+ """Preload the build farm jobs to which the given jobs will delegate.
+
+ """
+ pass
+
@classmethod
def getByJob(cls, job):
"""See `IBuildFarmJobOld`."""
store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
return store.find(cls, cls.job == job).one()
+ @classmethod
+ def getByJobs(cls, jobs):
+ """See `IBuildFarmJobOld`.
+ """
+ store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
+ job_ids = [job.id for job in jobs]
+ return store.find(
+ cls, cls.job_id.is_in(job_ids))
+
def generateSlaveBuildCookie(self):
"""See `IBuildFarmJobOld`."""
buildqueue = getUtility(IBuildQueueSet).getByJob(self.job)
=== modified file 'lib/lp/buildmaster/model/buildqueue.py'
--- lib/lp/buildmaster/model/buildqueue.py 2011-11-14 08:30:52 +0000
+++ lib/lp/buildmaster/model/buildqueue.py 2011-12-16 12:18:28 +0000
@@ -16,7 +16,9 @@
datetime,
timedelta,
)
+from itertools import groupby
import logging
+from operator import attrgetter
import pytz
from sqlobject import (
@@ -56,6 +58,10 @@
)
from lp.services.job.interfaces.job import JobStatus
from lp.services.job.model.job import Job
+from lp.services.propertycache import (
+ cachedproperty,
+ get_property_cache,
+ )
def normalize_virtualization(virtualized):
@@ -136,12 +142,34 @@
"""See `IBuildQueue`."""
return IBuildFarmJobBehavior(self.specific_job)
- @property
+ @cachedproperty
def specific_job(self):
"""See `IBuildQueue`."""
specific_class = specific_job_classes()[self.job_type]
return specific_class.getByJob(self.job)
+ @staticmethod
+ def preloadSpecificJobData(queues):
+ key = attrgetter('job_type')
+ specific_jobs_dict = {}
+ for job_type, grouped_queues in groupby(queues, key=key):
+ specific_class = specific_job_classes()[job_type]
+ queue_subset = list(grouped_queues)
+ # We need to preload the build farm jobs early to avoid
+ # the call to _set_build_farm_job to look up BuildFarmBuildJobs
+ # one by one.
+ specific_class.preloadBuildFarmJobs(queue_subset)
+ specific_jobs = specific_class.getByJobs(queue_subset)
+ if len(list(specific_jobs)) == 0:
+ return
+ specific_class.preloadJobsData(specific_jobs)
+ specific_jobs_dict = dict(
+ (specific_job.job, specific_job)
+ for specific_job in specific_jobs)
+ for queue in queue_subset:
+ cache = get_property_cache(queue)
+ cache.specific_job = specific_jobs_dict[queue.job]
+
@property
def date_started(self):
"""See `IBuildQueue`."""
=== modified file 'lib/lp/code/model/sourcepackagerecipebuild.py'
--- lib/lp/code/model/sourcepackagerecipebuild.py 2011-11-14 17:24:36 +0000
+++ lib/lp/code/model/sourcepackagerecipebuild.py 2011-12-16 12:18:28 +0000
@@ -289,6 +289,18 @@
PackageBuild.build_farm_job_id == build_farm_job.id).one()
@classmethod
+ def preloadBuildsData(cls, builds):
+ # Circular imports.
+ from lp.code.model.sourcepackagerecipe import SourcePackageRecipe
+ package_builds = load_related(
+ PackageBuild, builds, ['package_build_id'])
+ archives = load_related(Archive, package_builds, ['archive_id'])
+ load_related(Person, archives, ['ownerID'])
+ sprs = load_related(
+ SourcePackageRecipe, builds, ['recipe_id'])
+ SourcePackageRecipe.preLoadDataForSourcePackageRecipes(sprs)
+
+ @classmethod
def getByBuildFarmJobs(cls, build_farm_jobs):
"""See `ISpecificBuildFarmJobSource`."""
if len(build_farm_jobs) == 0:
@@ -296,20 +308,11 @@
build_farm_job_ids = [
build_farm_job.id for build_farm_job in build_farm_jobs]
- def eager_load(rows):
- # Circular imports.
- from lp.code.model.sourcepackagerecipe import SourcePackageRecipe
- package_builds = load_related(
- PackageBuild, rows, ['package_build_id'])
- archives = load_related(Archive, package_builds, ['archive_id'])
- load_related(Person, archives, ['ownerID'])
- sprs = load_related(
- SourcePackageRecipe, rows, ['recipe_id'])
- SourcePackageRecipe.preLoadDataForSourcePackageRecipes(sprs)
resultset = Store.of(build_farm_jobs[0]).find(cls,
cls.package_build_id == PackageBuild.id,
PackageBuild.build_farm_job_id.is_in(build_farm_job_ids))
- return DecoratedResultSet(resultset, pre_iter_hook=eager_load)
+ return DecoratedResultSet(
+ resultset, pre_iter_hook=cls.preloadBuildsData)
@classmethod
def getRecentBuilds(cls, requester, recipe, distroseries, _now=None):
@@ -430,6 +433,24 @@
We override this to provide a delegate specific to package builds."""
self.build_farm_job = BuildFarmBuildJob(self.build)
+ @staticmethod
+ def preloadBuildFarmJobs(jobs):
+ from lp.code.model.sourcepackagerecipebuild import (
+ SourcePackageRecipeBuild,
+ )
+ return list(IStore(SourcePackageRecipeBuildJob).find(
+ SourcePackageRecipeBuild,
+ [SourcePackageRecipeBuildJob.id.is_in([job.id for job in jobs]),
+ SourcePackageRecipeBuildJob.build_id ==
+ SourcePackageRecipeBuild.id]))
+
+ @classmethod
+ def preloadJobsData(cls, jobs):
+ load_related(Job, jobs, ['job_id'])
+ builds = load_related(
+ SourcePackageRecipeBuild, jobs, ['build_id'])
+ SourcePackageRecipeBuild.preloadBuildsData(builds)
+
@classmethod
def new(cls, build, job):
"""See `ISourcePackageRecipeBuildJobSource`."""
=== modified file 'lib/lp/soyuz/browser/builder.py'
--- lib/lp/soyuz/browser/builder.py 2011-09-13 05:23:16 +0000
+++ lib/lp/soyuz/browser/builder.py 2011-12-16 12:18:28 +0000
@@ -30,6 +30,10 @@
from zope.lifecycleevent import ObjectCreatedEvent
from canonical.launchpad import _
+from canonical.launchpad.components.decoratedresultset import (
+ DecoratedResultSet,
+ )
+from canonical.launchpad.interfaces.lpstorm import IStore
from canonical.launchpad.webapp import (
ApplicationMenu,
canonical_url,
@@ -53,7 +57,11 @@
IBuilder,
IBuilderSet,
)
-from lp.services.propertycache import cachedproperty
+from lp.buildmaster.model.buildqueue import BuildQueue
+from lp.services.propertycache import (
+ cachedproperty,
+ get_property_cache,
+ )
from lp.soyuz.browser.build import (
BuildNavigationMixin,
BuildRecordsView,
@@ -144,7 +152,22 @@
@cachedproperty
def builders(self):
"""All active builders"""
- return list(self.context.getBuilders())
+ def do_eager_load(builders):
+ # Populate builders' currentjob cachedproperty.
+ queues = IStore(BuildQueue).find(
+ BuildQueue,
+ BuildQueue.builderID.is_in(
+ builder.id for builder in builders))
+ queue_builders = dict(
+ (queue.builderID, queue) for queue in queues)
+ for builder in builders:
+ cache = get_property_cache(builder)
+ cache.currentjob = queue_builders.get(builder.id, None)
+ # Prefetch the jobs' data.
+ BuildQueue.preloadSpecificJobData(queues)
+
+ return list(DecoratedResultSet(
+ list(self.context.getBuilders()), pre_iter_hook=do_eager_load))
@property
def number_of_registered_builders(self):
=== modified file 'lib/lp/soyuz/browser/tests/test_builder.py'
--- lib/lp/soyuz/browser/tests/test_builder.py 2011-05-03 04:46:02 +0000
+++ lib/lp/soyuz/browser/tests/test_builder.py 2011-12-16 12:18:28 +0000
@@ -1,14 +1,32 @@
-# Copyright 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Tests for the lp.soyuz.browser.builder module."""
__metaclass__ = type
+from testtools.matchers import Equals
+from zope.component import getUtility
+from zope.security.proxy import removeSecurityProxy
+
from canonical.launchpad.webapp import canonical_url
-from canonical.testing.layers import DatabaseFunctionalLayer
-from lp.testing import TestCaseWithFactory
+from canonical.testing.layers import (
+ DatabaseFunctionalLayer,
+ LaunchpadFunctionalLayer,
+ )
+from lp.buildmaster.interfaces.builder import IBuilderSet
+from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
+from lp.soyuz.browser.tests.test_builder_views import BuildCreationMixin
+from lp.testing import (
+ record_two_runs,
+ TestCaseWithFactory,
+ )
+from lp.testing.matchers import HasQueryCount
from lp.testing.publication import test_traverse
+from lp.testing.views import create_initialized_view
+from lp.translations.interfaces.translationtemplatesbuildjob import (
+ ITranslationTemplatesBuildJobSource,
+ )
class TestBuildersNavigation(TestCaseWithFactory):
@@ -38,3 +56,52 @@
self.assertEqual(
canonical_url(build),
request.response.getHeader('location'))
+
+
+def builders_homepage_render():
+ builders = getUtility(IBuilderSet)
+ create_initialized_view(builders, "+index").render()
+
+
+class TestBuildersHomepage(TestCaseWithFactory, BuildCreationMixin):
+
+ layer = LaunchpadFunctionalLayer
+
+ def test_builders_binary_package_build_query_count(self):
+ def create_build():
+ build = self.createBinaryPackageBuild()
+ queue = build.queueBuild()
+ queue.markAsBuilding(build.builder)
+
+ recorder1, recorder2 = record_two_runs(
+ builders_homepage_render, create_build, 2)
+
+ self.assertThat(recorder2, HasQueryCount(Equals(recorder1.count)))
+
+ def test_builders_recipe_build_query_count(self):
+ def create_build():
+ build = self.createRecipeBuildWithBuilder()
+ queue = build.queueBuild()
+ queue.markAsBuilding(build.builder)
+
+ recorder1, recorder2 = record_two_runs(
+ builders_homepage_render, create_build, 2)
+
+ self.assertThat(recorder2, HasQueryCount(Equals(recorder1.count)))
+
+ def test_builders_translation_template_build_query_count(self):
+ def create_build():
+ jobset = getUtility(ITranslationTemplatesBuildJobSource)
+ branch = self.factory.makeBranch()
+ specific_job = jobset.create(branch)
+ queueset = getUtility(IBuildQueueSet)
+ # Using rSP is required to get the job id.
+ naked_job = removeSecurityProxy(specific_job.job)
+ job_id = naked_job.id
+ queue = queueset.get(job_id)
+ queue.markAsBuilding(self.factory.makeBuilder())
+
+ recorder1, recorder2 = record_two_runs(
+ builders_homepage_render, create_build, 2)
+
+ self.assertThat(recorder2, HasQueryCount(Equals(recorder1.count)))
=== modified file 'lib/lp/soyuz/browser/tests/test_builder_views.py'
--- lib/lp/soyuz/browser/tests/test_builder_views.py 2011-12-09 10:30:27 +0000
+++ lib/lp/soyuz/browser/tests/test_builder_views.py 2011-12-16 12:18:28 +0000
@@ -18,10 +18,7 @@
from canonical.launchpad.ftests import login
from canonical.launchpad.webapp.servers import LaunchpadTestRequest
from canonical.testing.layers import LaunchpadFunctionalLayer
-from lp.buildmaster.enums import (
- BuildFarmJobType,
- BuildStatus,
- )
+from lp.buildmaster.enums import BuildFarmJobType
from lp.buildmaster.interfaces.buildfarmjob import (
IBuildFarmJobSource,
InconsistentBuildFarmJobError,
@@ -159,28 +156,25 @@
getSpecificJobs, [build_farm_job])
-class TestBuilderHistoryView(TestCaseWithFactory):
-
- layer = LaunchpadFunctionalLayer
-
- nb_objects = 2
-
- def setUp(self):
- super(TestBuilderHistoryView, self).setUp()
- self.builder = self.factory.makeBuilder()
-
- def createTranslationTemplateBuildWithBuilder(self):
+class BuildCreationMixin(object):
+
+ def createTranslationTemplateBuildWithBuilder(self, builder=None):
+ if builder is None:
+ builder = self.factory.makeBuilder()
build_farm_job_source = getUtility(IBuildFarmJobSource)
build_farm_job = build_farm_job_source.new(
BuildFarmJobType.TRANSLATIONTEMPLATESBUILD)
source = getUtility(ITranslationTemplatesBuildSource)
branch = self.factory.makeBranch()
build = source.create(build_farm_job, branch)
- removeSecurityProxy(build).builder = self.builder
+ removeSecurityProxy(build).builder = builder
self.addFakeBuildLog(build)
return build
- def createRecipeBuildWithBuilder(self, private_branch=False):
+ def createRecipeBuildWithBuilder(self, private_branch=False,
+ builder=None):
+ if builder is None:
+ builder = self.factory.makeBuilder()
branch2 = self.factory.makeAnyBranch()
branch1 = self.factory.makeAnyBranch()
build = self.factory.makeSourcePackageRecipeBuild(
@@ -191,7 +185,7 @@
branch1.setPrivate(
True, getUtility(IPersonSet).getByEmail(ADMIN_EMAIL))
Store.of(build).flush()
- removeSecurityProxy(build).builder = self.builder
+ removeSecurityProxy(build).builder = builder
self.addFakeBuildLog(build)
return build
@@ -201,19 +195,31 @@
import transaction
transaction.commit()
- def createBinaryPackageBuild(self, in_ppa=False):
+ def createBinaryPackageBuild(self, in_ppa=False, builder=None):
+ if builder is None:
+ builder = self.factory.makeBuilder()
archive = None
if in_ppa:
archive = self.factory.makeArchive()
- build = self.factory.makeBinaryPackageBuild(
- archive=archive, status=BuildStatus.FULLYBUILT)
+ build = self.factory.makeBinaryPackageBuild(archive=archive)
naked_build = removeSecurityProxy(build)
- naked_build.builder = self.builder
+ naked_build.builder = builder
naked_build.date_started = self.factory.getUniqueDate()
naked_build.date_finished = self.factory.getUniqueDate()
self.addFakeBuildLog(build)
return build
+
+class TestBuilderHistoryView(TestCaseWithFactory, BuildCreationMixin):
+
+ layer = LaunchpadFunctionalLayer
+
+ nb_objects = 2
+
+ def setUp(self):
+ super(TestBuilderHistoryView, self).setUp()
+ self.builder = self.factory.makeBuilder()
+
def test_build_history_queries_count_view_recipe_builds(self):
# The builder's history view creation (i.e. the call to
# view.setupBuildList) issues a constant number of queries
@@ -221,7 +227,8 @@
def builder_history_render():
create_initialized_view(self.builder, '+history').render()
recorder1, recorder2 = record_two_runs(
- builder_history_render, self.createRecipeBuildWithBuilder,
+ builder_history_render,
+ partial(self.createRecipeBuildWithBuilder, builder=self.builder),
self.nb_objects)
# XXX: rvb 2011-11-14 bug=890326: The only query remaining is the
@@ -237,7 +244,8 @@
def builder_history_render():
create_initialized_view(self.builder, '+history').render()
recorder1, recorder2 = record_two_runs(
- builder_history_render, self.createBinaryPackageBuild,
+ builder_history_render,
+ partial(self.createBinaryPackageBuild, builder=self.builder),
self.nb_objects)
self.assertThat(recorder2, HasQueryCount(Equals(recorder1.count)))
@@ -248,7 +256,7 @@
def builder_history_render():
create_initialized_view(self.builder, '+history').render()
createBinaryPackageBuildInPPA = partial(
- self.createBinaryPackageBuild, in_ppa=True)
+ self.createBinaryPackageBuild, in_ppa=True, builder=self.builder)
recorder1, recorder2 = record_two_runs(
builder_history_render, createBinaryPackageBuildInPPA,
self.nb_objects)
@@ -262,21 +270,26 @@
create_initialized_view(self.builder, '+history').render()
recorder1, recorder2 = record_two_runs(
builder_history_render,
- self.createTranslationTemplateBuildWithBuilder, self.nb_objects)
+ partial(
+ self.createTranslationTemplateBuildWithBuilder,
+ builder=self.builder),
+ self.nb_objects)
self.assertThat(recorder2, HasQueryCount(Equals(recorder1.count)))
def test_build_history_private_build_view(self):
- self.createRecipeBuildWithBuilder()
- self.createRecipeBuildWithBuilder(private_branch=True)
+ self.createRecipeBuildWithBuilder(builder=self.builder)
+ self.createRecipeBuildWithBuilder(
+ private_branch=True, builder=self.builder)
view = create_initialized_view(self.builder, '+history')
view.setupBuildList()
self.assertIn(None, view.complete_builds)
def test_build_history_private_build_display(self):
- self.createRecipeBuildWithBuilder()
- self.createRecipeBuildWithBuilder(private_branch=True)
+ self.createRecipeBuildWithBuilder(builder=self.builder)
+ self.createRecipeBuildWithBuilder(
+ private_branch=True, builder=self.builder)
view = create_initialized_view(self.builder, '+history')
private_build_icon_matcher = soupmatchers.HTMLContains(
soupmatchers.Tag(
=== modified file 'lib/lp/soyuz/interfaces/binarypackagebuild.py'
--- lib/lp/soyuz/interfaces/binarypackagebuild.py 2011-10-31 14:02:47 +0000
+++ lib/lp/soyuz/interfaces/binarypackagebuild.py 2011-12-16 12:18:28 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
# pylint: disable-msg=E0211,E0213
@@ -426,6 +426,11 @@
asserted to not be None/empty.
"""
+ def preloadBuildsData(builds):
+ """Prefetch the data related to the builds.
+
+ """
+
class IBuildRescoreForm(Interface):
"""Form for rescoring a build."""
=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
--- lib/lp/soyuz/model/binarypackagebuild.py 2011-11-28 10:28:54 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py 2011-12-16 12:18:28 +0000
@@ -867,6 +867,31 @@
return None
return resulting_tuple[0]
+ def preloadBuildsData(self, builds):
+ # Circular imports.
+ from lp.soyuz.model.distroarchseries import (
+ DistroArchSeries
+ )
+ from lp.registry.model.distroseries import (
+ DistroSeries
+ )
+ from lp.registry.model.distribution import (
+ Distribution
+ )
+ from lp.soyuz.model.archive import Archive
+ from lp.registry.model.person import Person
+ self._prefetchBuildData(builds)
+ distro_arch_series = load_related(
+ DistroArchSeries, builds, ['distro_arch_series_id'])
+ package_builds = load_related(
+ PackageBuild, builds, ['package_build_id'])
+ archives = load_related(Archive, package_builds, ['archive_id'])
+ load_related(Person, archives, ['ownerID'])
+ distroseries = load_related(
+ DistroSeries, distro_arch_series, ['distroseriesID'])
+ load_related(
+ Distribution, distroseries, ['distributionID'])
+
def getByBuildFarmJobs(self, build_farm_jobs):
"""See `ISpecificBuildFarmJobSource`."""
if len(build_farm_jobs) == 0:
@@ -875,36 +900,13 @@
build_farm_job_ids = [
build_farm_job.id for build_farm_job in build_farm_jobs]
- def eager_load(rows):
- # Circular imports.
- from lp.soyuz.model.distroarchseries import (
- DistroArchSeries
- )
- from lp.registry.model.distroseries import (
- DistroSeries
- )
- from lp.registry.model.distribution import (
- Distribution
- )
- from lp.soyuz.model.archive import Archive
- from lp.registry.model.person import Person
- self._prefetchBuildData(rows)
- distro_arch_series = load_related(
- DistroArchSeries, rows, ['distro_arch_series_id'])
- package_builds = load_related(
- PackageBuild, rows, ['package_build_id'])
- archives = load_related(Archive, package_builds, ['archive_id'])
- load_related(Person, archives, ['ownerID'])
- distroseries = load_related(
- DistroSeries, distro_arch_series, ['distroseriesID'])
- load_related(
- Distribution, distroseries, ['distributionID'])
resultset = Store.of(build_farm_jobs[0]).using(*clause_tables).find(
BinaryPackageBuild,
BinaryPackageBuild.package_build == PackageBuild.id,
PackageBuild.build_farm_job == BuildFarmJob.id,
BuildFarmJob.id.is_in(build_farm_job_ids))
- return DecoratedResultSet(resultset, pre_iter_hook=eager_load)
+ return DecoratedResultSet(
+ resultset, pre_iter_hook=self.preloadBuildsData)
def getPendingBuildsForArchSet(self, archseries):
"""See `IBinaryPackageBuildSet`."""
=== modified file 'lib/lp/soyuz/model/buildpackagejob.py'
--- lib/lp/soyuz/model/buildpackagejob.py 2011-07-06 19:31:32 +0000
+++ lib/lp/soyuz/model/buildpackagejob.py 2011-12-16 12:18:28 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
@@ -19,10 +19,12 @@
from zope.interface import implements
from canonical.database.sqlbase import sqlvalues
+from canonical.launchpad.interfaces.lpstorm import IStore
from lp.buildmaster.enums import BuildStatus
+from lp.buildmaster.interfaces.builder import IBuilderSet
from lp.buildmaster.model.buildfarmjob import BuildFarmJobOldDerived
from lp.registry.interfaces.pocket import PackagePublishingPocket
-from lp.buildmaster.interfaces.builder import IBuilderSet
+from lp.services.database.bulk import load_related
from lp.soyuz.enums import (
ArchivePurpose,
PackagePublishingStatus,
@@ -36,7 +38,6 @@
SCORE_BY_POCKET,
SCORE_BY_URGENCY,
)
-
from lp.soyuz.model.buildfarmbuildjob import BuildFarmBuildJob
@@ -63,6 +64,14 @@
We override this to provide a delegate specific to package builds."""
self.build_farm_job = BuildFarmBuildJob(self.build)
+ @staticmethod
+ def preloadBuildFarmJobs(jobs):
+ from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
+ return list(IStore(BinaryPackageBuild).find(
+ BinaryPackageBuild,
+ [BuildPackageJob.id.is_in([job.id for job in jobs]),
+ BuildPackageJob.build_id == BinaryPackageBuild.id]))
+
def score(self):
"""See `IBuildPackageJob`."""
# Define a table we'll use to calculate the score based on the time
@@ -157,6 +166,14 @@
"""See `IBuildFarmJob`."""
return self.build.is_virtualized
+ @classmethod
+ def preloadJobsData(cls, jobs):
+ from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
+ from lp.services.job.model.job import Job
+ load_related(Job, jobs, ['job_id'])
+ builds = load_related(BinaryPackageBuild, jobs, ['build_id'])
+ getUtility(IBinaryPackageBuildSet).preloadBuildsData(list(builds))
+
@staticmethod
def addCandidateSelectionCriteria(processor, virtualized):
"""See `IBuildFarmJob`."""
=== modified file 'lib/lp/translations/model/translationtemplatesbuildjob.py'
--- lib/lp/translations/model/translationtemplatesbuildjob.py 2011-05-27 21:12:25 +0000
+++ lib/lp/translations/model/translationtemplatesbuildjob.py 2011-12-16 12:18:28 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010 Canonical Ltd. This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
@@ -40,6 +40,7 @@
BranchJobDerived,
BranchJobType,
)
+from lp.services.database.bulk import load_related
from lp.translations.interfaces.translationtemplatesbuild import (
ITranslationTemplatesBuildSource,
)
@@ -226,6 +227,31 @@
return cls(branch_job)
@classmethod
+ def getByJobs(cls, jobs):
+ """See `IBuildFarmJob`.
+
+ Overridden here to search via a BranchJob, rather than a Job.
+ """
+ store = IStore(BranchJob)
+ job_ids = [job.id for job in jobs]
+ branch_jobs = store.find(
+ BranchJob, BranchJob.jobID.is_in(job_ids))
+ return [cls(branch_job) for branch_job in branch_jobs]
+
+ @classmethod
+ def preloadJobsData(cls, jobs):
+ # Circular imports.
+ from lp.code.model.branch import Branch
+ from lp.registry.model.product import Product
+ from lp.code.model.branchcollection import GenericBranchCollection
+ from lp.services.job.model.job import Job
+ contexts = [job.context for job in jobs]
+ load_related(Job, contexts, ['jobID'])
+ branches = load_related(Branch, contexts, ['branchID'])
+ GenericBranchCollection.preloadDataForBranches(branches)
+ load_related(Product, branches, ['productID'])
+
+ @classmethod
def getByBranch(cls, branch):
"""See `ITranslationTemplatesBuildJobSource`."""
store = IStore(BranchJob)