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
1=== modified file 'lib/lp/code/model/branch.py'
2--- lib/lp/code/model/branch.py 2011-03-15 14:31:00 +0000
3+++ lib/lp/code/model/branch.py 2011-03-21 11:16:01 +0000
4@@ -612,7 +612,7 @@
5 else:
6 return True
7
8- @property
9+ @cachedproperty
10 def code_import(self):
11 from lp.code.model.codeimport import CodeImportSet
12 return CodeImportSet().getByBranch(self)
13
14=== modified file 'lib/lp/code/model/branchcollection.py'
15--- lib/lp/code/model/branchcollection.py 2011-03-07 12:43:40 +0000
16+++ lib/lp/code/model/branchcollection.py 2011-03-21 11:16:01 +0000
17@@ -59,6 +59,7 @@
18 )
19 from lp.code.model.branchmergeproposal import BranchMergeProposal
20 from lp.code.model.branchsubscription import BranchSubscription
21+from lp.code.model.codeimport import CodeImport
22 from lp.code.model.codereviewcomment import CodeReviewComment
23 from lp.code.model.codereviewvote import CodeReviewVoteReference
24 from lp.code.model.seriessourcepackagebranch import SeriesSourcePackageBranch
25@@ -71,6 +72,7 @@
26 from lp.registry.model.product import Product
27 from lp.registry.model.sourcepackagename import SourcePackageName
28 from lp.registry.model.teammembership import TeamParticipation
29+from lp.services.database.bulk import load_related
30 from lp.services.propertycache import get_property_cache
31
32
33@@ -199,6 +201,19 @@
34 cache = caches[link.branchID]
35 cache._associatedSuiteSourcePackages.append(
36 link.suite_sourcepackage)
37+ load_related(Product, rows, ['productID'])
38+ # So far have only needed the persons for their canonical_url - no
39+ # need for validity etc in the /branches API call.
40+ load_related(Person, rows,
41+ ['ownerID', 'registrantID', 'reviewerID'])
42+ # Cache all branches as having no code imports to prevent fruitless
43+ # lookups on the ones we don't find.
44+ for cache in caches.values():
45+ cache.code_import = None
46+ for code_import in IStore(CodeImport).find(
47+ CodeImport, CodeImport.branchID.is_in(branch_ids)):
48+ cache = caches[code_import.branchID]
49+ cache.code_import = code_import
50 return DecoratedResultSet(resultset, pre_iter_hook=do_eager_load)
51
52 def getMergeProposals(self, statuses=None, for_branches=None,
53
54=== modified file 'lib/lp/code/model/tests/test_branch.py'
55--- lib/lp/code/model/tests/test_branch.py 2011-03-07 07:27:56 +0000
56+++ lib/lp/code/model/tests/test_branch.py 2011-03-21 11:16:01 +0000
57@@ -111,6 +111,7 @@
58 from lp.registry.interfaces.pocket import PackagePublishingPocket
59 from lp.registry.model.sourcepackage import SourcePackage
60 from lp.services.osutils import override_environ
61+from lp.services.propertycache import clear_property_cache
62 from lp.testing import (
63 ANONYMOUS,
64 celebrity_logged_in,
65@@ -146,6 +147,7 @@
66 branch = code_import.branch
67 self.assertEqual(code_import, branch.code_import)
68 CodeImportSet().delete(code_import)
69+ clear_property_cache(branch)
70 self.assertEqual(None, branch.code_import)
71
72
73
74=== modified file 'lib/lp/services/database/bulk.py'
75--- lib/lp/services/database/bulk.py 2011-03-17 01:28:36 +0000
76+++ lib/lp/services/database/bulk.py 2011-03-21 11:16:01 +0000
77@@ -11,6 +11,7 @@
78
79
80 from collections import defaultdict
81+from functools import partial
82
83 from storm.info import get_cls_info
84 from storm.store import Store
85@@ -70,10 +71,29 @@
86 "Compound primary keys are not supported: %s." %
87 object_type.__name__)
88 primary_key_column = primary_key[0]
89- # Turn the primary keys into a set to eliminate duplicates,
90- # shortening the query.
91- condition = primary_key_column.is_in(set(primary_keys))
92+ primary_keys = set(primary_keys)
93+ primary_keys.discard(None)
94+ if not primary_keys:
95+ return []
96+ condition = primary_key_column.is_in(primary_keys)
97 if store is None:
98 store = IStore(object_type)
99- query = store.find(object_type, condition)
100- return list(query)
101+ return list(store.find(object_type, condition))
102+
103+
104+def load_related(object_type, owning_objects, foreign_keys):
105+ """Load objects of object_type referred to by owning_objects.
106+
107+ Note that complex types like Person are best loaded through dedicated
108+ helpers that can eager load other related things (e.g. validity for
109+ Person).
110+
111+ :param object_type: The object type to load - e.g. Person.
112+ :param owning_objects: The objects holding the references. E.g. Bug.
113+ :param foreign_keys: A list of attributes that should be inspected for
114+ keys. e.g. ['ownerID']
115+ """
116+ keys = set()
117+ for owning_object in owning_objects:
118+ keys.update(map(partial(getattr, owning_object), foreign_keys))
119+ return load(object_type, keys)
120
121=== modified file 'lib/lp/services/database/tests/test_bulk.py'
122--- lib/lp/services/database/tests/test_bulk.py 2011-03-08 09:14:30 +0000
123+++ lib/lp/services/database/tests/test_bulk.py 2011-03-21 11:16:01 +0000
124@@ -21,6 +21,7 @@
125 )
126 from canonical.testing.layers import DatabaseFunctionalLayer
127 from lp.bugs.model.bug import BugAffectsPerson
128+from lp.registry.model.person import Person
129 from lp.services.database import bulk
130 from lp.soyuz.model.component import Component
131 from lp.testing import (
132@@ -191,3 +192,14 @@
133 Component, [db_object.id], store=slave_store)
134 self.assertEqual(
135 Store.of(db_object_from_slave), slave_store)
136+
137+ def test_load_related(self):
138+ owning_objects = [
139+ self.factory.makeBug(),
140+ self.factory.makeBug(),
141+ ]
142+ expected = set(bug.owner for bug in owning_objects)
143+ self.assertEqual(expected,
144+ set(bulk.load_related(Person, owning_objects, ['ownerID'])))
145+
146+