Merge lp:~lifeless/launchpad/bug-734751 into lp:launchpad

Proposed by Robert Collins
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 12634
Proposed branch: lp:~lifeless/launchpad/bug-734751
Merge into: lp:launchpad
Diff against target: 146 lines (+55/-6)
5 files modified
lib/lp/code/model/branch.py (+1/-1)
lib/lp/code/model/branchcollection.py (+15/-0)
lib/lp/code/model/tests/test_branch.py (+2/-0)
lib/lp/services/database/bulk.py (+25/-5)
lib/lp/services/database/tests/test_bulk.py (+12/-0)
To merge this branch: bzr merge lp:~lifeless/launchpad/bug-734751
Reviewer Review Type Date Requested Status
Steve Kowalik (community) Approve
Review via email: mp+54146@code.launchpad.net

Commit message

[r=stevenk][bug=734751] Eager load branch code imports, products, owners, registrants and reviewers.

Description of the change

More eager loading good. I've extended bulk to have a higher level loader than load().

To post a comment you must log in.
Revision history for this message
Steve Kowalik (stevenk) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/model/branch.py'
--- lib/lp/code/model/branch.py 2011-03-15 14:31:00 +0000
+++ lib/lp/code/model/branch.py 2011-03-21 11:16:01 +0000
@@ -612,7 +612,7 @@
612 else:612 else:
613 return True613 return True
614614
615 @property615 @cachedproperty
616 def code_import(self):616 def code_import(self):
617 from lp.code.model.codeimport import CodeImportSet617 from lp.code.model.codeimport import CodeImportSet
618 return CodeImportSet().getByBranch(self)618 return CodeImportSet().getByBranch(self)
619619
=== modified file 'lib/lp/code/model/branchcollection.py'
--- lib/lp/code/model/branchcollection.py 2011-03-07 12:43:40 +0000
+++ lib/lp/code/model/branchcollection.py 2011-03-21 11:16:01 +0000
@@ -59,6 +59,7 @@
59 )59 )
60from lp.code.model.branchmergeproposal import BranchMergeProposal60from lp.code.model.branchmergeproposal import BranchMergeProposal
61from lp.code.model.branchsubscription import BranchSubscription61from lp.code.model.branchsubscription import BranchSubscription
62from lp.code.model.codeimport import CodeImport
62from lp.code.model.codereviewcomment import CodeReviewComment63from lp.code.model.codereviewcomment import CodeReviewComment
63from lp.code.model.codereviewvote import CodeReviewVoteReference64from lp.code.model.codereviewvote import CodeReviewVoteReference
64from lp.code.model.seriessourcepackagebranch import SeriesSourcePackageBranch65from lp.code.model.seriessourcepackagebranch import SeriesSourcePackageBranch
@@ -71,6 +72,7 @@
71from lp.registry.model.product import Product72from lp.registry.model.product import Product
72from lp.registry.model.sourcepackagename import SourcePackageName73from lp.registry.model.sourcepackagename import SourcePackageName
73from lp.registry.model.teammembership import TeamParticipation74from lp.registry.model.teammembership import TeamParticipation
75from lp.services.database.bulk import load_related
74from lp.services.propertycache import get_property_cache76from lp.services.propertycache import get_property_cache
7577
7678
@@ -199,6 +201,19 @@
199 cache = caches[link.branchID]201 cache = caches[link.branchID]
200 cache._associatedSuiteSourcePackages.append(202 cache._associatedSuiteSourcePackages.append(
201 link.suite_sourcepackage)203 link.suite_sourcepackage)
204 load_related(Product, rows, ['productID'])
205 # So far have only needed the persons for their canonical_url - no
206 # need for validity etc in the /branches API call.
207 load_related(Person, rows,
208 ['ownerID', 'registrantID', 'reviewerID'])
209 # Cache all branches as having no code imports to prevent fruitless
210 # lookups on the ones we don't find.
211 for cache in caches.values():
212 cache.code_import = None
213 for code_import in IStore(CodeImport).find(
214 CodeImport, CodeImport.branchID.is_in(branch_ids)):
215 cache = caches[code_import.branchID]
216 cache.code_import = code_import
202 return DecoratedResultSet(resultset, pre_iter_hook=do_eager_load)217 return DecoratedResultSet(resultset, pre_iter_hook=do_eager_load)
203218
204 def getMergeProposals(self, statuses=None, for_branches=None,219 def getMergeProposals(self, statuses=None, for_branches=None,
205220
=== modified file 'lib/lp/code/model/tests/test_branch.py'
--- lib/lp/code/model/tests/test_branch.py 2011-03-07 07:27:56 +0000
+++ lib/lp/code/model/tests/test_branch.py 2011-03-21 11:16:01 +0000
@@ -111,6 +111,7 @@
111from lp.registry.interfaces.pocket import PackagePublishingPocket111from lp.registry.interfaces.pocket import PackagePublishingPocket
112from lp.registry.model.sourcepackage import SourcePackage112from lp.registry.model.sourcepackage import SourcePackage
113from lp.services.osutils import override_environ113from lp.services.osutils import override_environ
114from lp.services.propertycache import clear_property_cache
114from lp.testing import (115from lp.testing import (
115 ANONYMOUS,116 ANONYMOUS,
116 celebrity_logged_in,117 celebrity_logged_in,
@@ -146,6 +147,7 @@
146 branch = code_import.branch147 branch = code_import.branch
147 self.assertEqual(code_import, branch.code_import)148 self.assertEqual(code_import, branch.code_import)
148 CodeImportSet().delete(code_import)149 CodeImportSet().delete(code_import)
150 clear_property_cache(branch)
149 self.assertEqual(None, branch.code_import)151 self.assertEqual(None, branch.code_import)
150152
151153
152154
=== modified file 'lib/lp/services/database/bulk.py'
--- lib/lp/services/database/bulk.py 2011-03-17 01:28:36 +0000
+++ lib/lp/services/database/bulk.py 2011-03-21 11:16:01 +0000
@@ -11,6 +11,7 @@
1111
1212
13from collections import defaultdict13from collections import defaultdict
14from functools import partial
1415
15from storm.info import get_cls_info16from storm.info import get_cls_info
16from storm.store import Store17from storm.store import Store
@@ -70,10 +71,29 @@
70 "Compound primary keys are not supported: %s." %71 "Compound primary keys are not supported: %s." %
71 object_type.__name__)72 object_type.__name__)
72 primary_key_column = primary_key[0]73 primary_key_column = primary_key[0]
73 # Turn the primary keys into a set to eliminate duplicates,74 primary_keys = set(primary_keys)
74 # shortening the query.75 primary_keys.discard(None)
75 condition = primary_key_column.is_in(set(primary_keys))76 if not primary_keys:
77 return []
78 condition = primary_key_column.is_in(primary_keys)
76 if store is None:79 if store is None:
77 store = IStore(object_type)80 store = IStore(object_type)
78 query = store.find(object_type, condition)81 return list(store.find(object_type, condition))
79 return list(query)82
83
84def load_related(object_type, owning_objects, foreign_keys):
85 """Load objects of object_type referred to by owning_objects.
86
87 Note that complex types like Person are best loaded through dedicated
88 helpers that can eager load other related things (e.g. validity for
89 Person).
90
91 :param object_type: The object type to load - e.g. Person.
92 :param owning_objects: The objects holding the references. E.g. Bug.
93 :param foreign_keys: A list of attributes that should be inspected for
94 keys. e.g. ['ownerID']
95 """
96 keys = set()
97 for owning_object in owning_objects:
98 keys.update(map(partial(getattr, owning_object), foreign_keys))
99 return load(object_type, keys)
80100
=== modified file 'lib/lp/services/database/tests/test_bulk.py'
--- lib/lp/services/database/tests/test_bulk.py 2011-03-08 09:14:30 +0000
+++ lib/lp/services/database/tests/test_bulk.py 2011-03-21 11:16:01 +0000
@@ -21,6 +21,7 @@
21 )21 )
22from canonical.testing.layers import DatabaseFunctionalLayer22from canonical.testing.layers import DatabaseFunctionalLayer
23from lp.bugs.model.bug import BugAffectsPerson23from lp.bugs.model.bug import BugAffectsPerson
24from lp.registry.model.person import Person
24from lp.services.database import bulk25from lp.services.database import bulk
25from lp.soyuz.model.component import Component26from lp.soyuz.model.component import Component
26from lp.testing import (27from lp.testing import (
@@ -191,3 +192,14 @@
191 Component, [db_object.id], store=slave_store)192 Component, [db_object.id], store=slave_store)
192 self.assertEqual(193 self.assertEqual(
193 Store.of(db_object_from_slave), slave_store)194 Store.of(db_object_from_slave), slave_store)
195
196 def test_load_related(self):
197 owning_objects = [
198 self.factory.makeBug(),
199 self.factory.makeBug(),
200 ]
201 expected = set(bug.owner for bug in owning_objects)
202 self.assertEqual(expected,
203 set(bulk.load_related(Person, owning_objects, ['ownerID'])))
204
205