launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #06020
[Merge] lp:~stevenk/launchpad/db-merge-stable-redux into lp:launchpad/db-devel
Steve Kowalik has proposed merging lp:~stevenk/launchpad/db-merge-stable-redux into lp:launchpad/db-devel.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~stevenk/launchpad/db-merge-stable-redux/+merge/87299
Merge stable at r14617.
--
https://code.launchpad.net/~stevenk/launchpad/db-merge-stable-redux/+merge/87299
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/db-merge-stable-redux into lp:launchpad/db-devel.
=== modified file 'lib/lp/buildmaster/interfaces/buildfarmjob.py'
--- lib/lp/buildmaster/interfaces/buildfarmjob.py 2011-12-24 16:54:44 +0000
+++ lib/lp/buildmaster/interfaces/buildfarmjob.py 2012-01-02 22:40:32 +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/buildfarmjob.py'
--- lib/lp/buildmaster/model/buildfarmjob.py 2011-12-30 06:14:56 +0000
+++ lib/lp/buildmaster/model/buildfarmjob.py 2012-01-02 22:40:32 +0000
@@ -100,6 +100,10 @@
"""See `IBuildFarmJobOld`."""
raise NotImplementedError
+ def getByJobs(self, job):
+ """See `IBuildFarmJobOld`."""
+ raise NotImplementedError
+
def jobStarted(self):
"""See `IBuildFarmJobOld`."""
pass
@@ -160,12 +164,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-12-30 06:14:56 +0000
+++ lib/lp/buildmaster/model/buildqueue.py 2012-01-02 22:40:32 +0000
@@ -16,7 +16,9 @@
datetime,
timedelta,
)
+from itertools import groupby
import logging
+from operator import attrgetter
import pytz
from sqlobject import (
@@ -142,6 +144,21 @@
specific_class = specific_job_classes()[self.job_type]
return specific_class.getByJob(self.job)
+ @staticmethod
+ def preloadSpecificJobData(queues):
+ key = attrgetter('job_type')
+ 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:
+ continue
+ specific_class.preloadJobsData(specific_jobs)
+
@property
def date_started(self):
"""See `IBuildQueue`."""
=== modified file 'lib/lp/code/model/sourcepackagerecipebuild.py'
--- lib/lp/code/model/sourcepackagerecipebuild.py 2011-12-30 06:14:56 +0000
+++ lib/lp/code/model/sourcepackagerecipebuild.py 2012-01-02 22:40:32 +0000
@@ -287,6 +287,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:
@@ -294,20 +306,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):
@@ -428,6 +431,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`."""
=== renamed directory 'lib/lp/services/session/stories' => 'lib/lp/services/feeds/stories'
=== modified file 'lib/lp/soyuz/browser/builder.py'
--- lib/lp/soyuz/browser/builder.py 2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/browser/builder.py 2012-01-02 22:40:32 +0000
@@ -41,6 +41,9 @@
IBuilder,
IBuilderSet,
)
+from lp.buildmaster.model.buildqueue import BuildQueue
+from lp.services.database.decoratedresultset import DecoratedResultSet
+from lp.services.database.lpstorm import IStore
from lp.services.propertycache import cachedproperty
from lp.services.webapp import (
ApplicationMenu,
@@ -144,7 +147,16 @@
@cachedproperty
def builders(self):
"""All active builders"""
- return list(self.context.getBuilders())
+ def do_eager_load(builders):
+ # Prefetch the jobs' data.
+ queues = IStore(BuildQueue).find(
+ BuildQueue,
+ BuildQueue.builderID.is_in(
+ builder.id for builder in builders))
+ 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 2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/browser/tests/test_builder.py 2012-01-02 22:40:32 +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 LessThan
+from zope.component import getUtility
+from zope.security.proxy import removeSecurityProxy
+
+from lp.buildmaster.interfaces.builder import IBuilderSet
+from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
from lp.services.webapp import canonical_url
-from lp.testing import TestCaseWithFactory
-from lp.testing.layers import DatabaseFunctionalLayer
+from lp.soyuz.browser.tests.test_builder_views import BuildCreationMixin
+from lp.testing import (
+ record_two_runs,
+ TestCaseWithFactory,
+ )
+from lp.testing.layers import (
+ DatabaseFunctionalLayer,
+ LaunchpadFunctionalLayer,
+ )
+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,67 @@
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
+
+ # XXX rvb: the 3 additional queries per build are the result of the calls
+ # to:
+ # - builder.currentjob
+ # - buildqueue.specific_job
+ # These could be converted into cachedproperty and pre-populated in
+ # bulk but several tests assert that the value returned by these
+ # these properties are up to date. Since they are not really expensive
+ # to compute I'll leave them as regular properties for now.
+
+ 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(LessThan(recorder1.count + 3 * 2 + 1)))
+
+ 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(LessThan(recorder1.count + 3 * 2 + 1)))
+
+ 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(LessThan(recorder1.count + 3 * 2 + 1)))
=== modified file 'lib/lp/soyuz/browser/tests/test_builder_views.py'
--- lib/lp/soyuz/browser/tests/test_builder_views.py 2011-12-30 06:14:56 +0000
+++ lib/lp/soyuz/browser/tests/test_builder_views.py 2012-01-02 22:40:32 +0000
@@ -14,10 +14,7 @@
from zope.component import getUtility
from zope.security.proxy import removeSecurityProxy
-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-12-24 16:54:44 +0000
+++ lib/lp/soyuz/interfaces/binarypackagebuild.py 2012-01-02 22:40:32 +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-12-30 06:14:56 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py 2012-01-02 22:40:32 +0000
@@ -865,6 +865,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:
@@ -873,36 +898,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-12-30 06:14:56 +0000
+++ lib/lp/soyuz/model/buildpackagejob.py 2012-01-02 22:40:32 +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
@@ -22,6 +22,8 @@
from lp.buildmaster.interfaces.builder import IBuilderSet
from lp.buildmaster.model.buildfarmjob import BuildFarmJobOldDerived
from lp.registry.interfaces.pocket import PackagePublishingPocket
+from lp.services.database.bulk import load_related
+from lp.services.database.lpstorm import IStore
from lp.services.database.sqlbase import sqlvalues
from lp.soyuz.enums import (
ArchivePurpose,
@@ -62,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
@@ -156,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/testing/tests/test_layers_functional.py'
--- lib/lp/testing/tests/test_layers_functional.py 2011-12-31 00:35:01 +0000
+++ lib/lp/testing/tests/test_layers_functional.py 2012-01-02 22:40:32 +0000
@@ -256,7 +256,7 @@
def xxxtestMemcachedWorking(self):
# XXX sinzui 2011-12-27 bug=729062: Disabled because lucid_db_lp
- # reports memcached did not die.
+ # reports memcached did not die.(self):
client = MemcachedLayer.client or memcache_client_factory()
key = "BaseTestCase.testMemcachedWorking"
client.forget_dead_hosts()
=== modified file 'lib/lp/translations/model/translationtemplatesbuildjob.py'
--- lib/lp/translations/model/translationtemplatesbuildjob.py 2012-01-01 02:58:52 +0000
+++ lib/lp/translations/model/translationtemplatesbuildjob.py 2012-01-02 22:40:32 +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
@@ -36,6 +36,7 @@
BranchJobType,
)
from lp.services.config import config
+from lp.services.database.bulk import load_related
from lp.services.database.lpstorm import (
IMasterStore,
IStore,
@@ -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)