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

Proposed by Robert Collins
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 12669
Proposed branch: lp:~lifeless/launchpad/bug-736008
Merge into: lp:launchpad
Diff against target: 156 lines (+40/-47)
1 file modified
lib/lp/code/model/branchcollection.py (+40/-47)
To merge this branch: bzr merge lp:~lifeless/launchpad/bug-736008
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Abel Deuring (community) code Approve
Review via email: mp+54820@code.launchpad.net

Commit message

[r=adeuring][bug=736008] Change to a constrained join rather than a nested query for source branch select in branchmergeproposal lookups.

Description of the change

The query used to determine BranchMergeProposals used differing nested subqueries including one with a product constraint; these perform poorly (because the planner chose a noddy plan), so have changed to a constrained join + identical subqueries (removing one level of nesting) and improving cold cache performance by 10 times for projects with no private branches, and similarly for those that do, except that the query to determine visibility still has some overheads implicit in the design - we may need to refactor further in future.

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) :
review: Approve (code)
Revision history for this message
Robert Collins (lifeless) wrote :

I fluffed it and missed this change:
=== modified file 'lib/lp/code/model/branchcollection.py'
--- lib/lp/code/model/branchcollection.py 2011-03-26 00:50:21 +0000
+++ lib/lp/code/model/branchcollection.py 2011-03-26 19:24:19 +0000
@@ -230,13 +230,8 @@
                 *self._branch_filter_expressions)),
             Join(Target, Target.id==BranchMergeProposal.target_branchID)
             ]
- expressions = []
- source_visible = self._getBranchVisibilityExpression()
- if source_visible:
- expressions.append(BranchMergeProposal.source_branchID.is_in(
- Select(Branch.id, source_visible)))
- expressions.append(BranchMergeProposal.target_branchID.is_in(
- Select(Target.id, self._getBranchVisibilityExpression(Target))))
+ expressions = self._getBranchVisibilityExpression()
+ expressions.extend(self._getBranchVisibilityExpression(Target))
         if for_branches is not None:
             branch_ids = [branch.id for branch in for_branches]
             expressions.append(

The whole point of all the other changes in the branch was to be able to do this :(. I'm self reviewing this incremental step and landing.

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/branchcollection.py'
2--- lib/lp/code/model/branchcollection.py 2011-03-23 16:28:51 +0000
3+++ lib/lp/code/model/branchcollection.py 2011-03-26 00:51:33 +0000
4@@ -21,6 +21,7 @@
5 Select,
6 Union,
7 )
8+from storm.info import ClassAlias
9 from zope.component import getUtility
10 from zope.interface import implements
11
12@@ -161,7 +162,12 @@
13
14 def _getBranchExpressions(self):
15 """Return the where expressions for this collection."""
16- return self._branch_filter_expressions
17+ return (self._branch_filter_expressions
18+ + self._getBranchVisibilityExpression())
19+
20+ def _getBranchVisibilityExpression(self, branch_class=None):
21+ """Return the where clauses for visibility."""
22+ return []
23
24 def getBranches(self, eager_load=False):
25 """See `IBranchCollection`."""
26@@ -217,10 +223,20 @@
27 def getMergeProposals(self, statuses=None, for_branches=None,
28 target_branch=None, merged_revnos=None):
29 """See `IBranchCollection`."""
30- expressions = [
31- BranchMergeProposal.source_branchID.is_in(
32- self._getBranchIdQuery()),
33+ Target = ClassAlias(Branch, "target")
34+ tables = [Branch] + self._tables.values() + [
35+ Join(BranchMergeProposal, And(
36+ Branch.id==BranchMergeProposal.source_branchID,
37+ *self._branch_filter_expressions)),
38+ Join(Target, Target.id==BranchMergeProposal.target_branchID)
39 ]
40+ expressions = []
41+ source_visible = self._getBranchVisibilityExpression()
42+ if source_visible:
43+ expressions.append(BranchMergeProposal.source_branchID.is_in(
44+ Select(Branch.id, source_visible)))
45+ expressions.append(BranchMergeProposal.target_branchID.is_in(
46+ Select(Target.id, self._getBranchVisibilityExpression(Target))))
47 if for_branches is not None:
48 branch_ids = [branch.id for branch in for_branches]
49 expressions.append(
50@@ -231,18 +247,10 @@
51 if merged_revnos is not None:
52 expressions.append(
53 BranchMergeProposal.merged_revno.is_in(merged_revnos))
54- expressions.extend(self._getExtraMergeProposalExpressions())
55 if statuses is not None:
56 expressions.append(
57 BranchMergeProposal.queue_status.is_in(statuses))
58- return self.store.find(BranchMergeProposal, expressions)
59-
60- def _getExtraMergeProposalExpressions(self):
61- """Extra storm expressions needed for merge proposal queries.
62-
63- Used primarily by the visibility check for target branches.
64- """
65- return []
66+ return self.store.using(*tables).find(BranchMergeProposal, *expressions)
67
68 def getMergeProposalsForPerson(self, person, status=None):
69 """See `IBranchCollection`."""
70@@ -266,12 +274,15 @@
71 CodeReviewVoteReference.reviewer == reviewer,
72 BranchMergeProposal.source_branchID.is_in(
73 self._getBranchIdQuery())]
74- expressions.extend(self._getExtraMergeProposalExpressions())
75+ visibility = self._getBranchVisibilityExpression()
76+ if visibility:
77+ expressions.append(BranchMergeProposal.target_branchID.is_in(
78+ Select(Branch.id, visibility)))
79 if status is not None:
80 expressions.append(
81 BranchMergeProposal.queue_status.is_in(status))
82 proposals = self.store.using(*tables).find(
83- BranchMergeProposal, expressions)
84+ BranchMergeProposal, *expressions)
85 # Apply sorting here as we can't do it in the browser code. We need
86 # to think carefully about the best places to do this, but not here
87 # nor now.
88@@ -557,16 +568,10 @@
89 store=store,
90 branch_filter_expressions=list(branch_filter_expressions),
91 tables=tables, exclude_from_search=exclude_from_search)
92- self._branch_filter_expressions.append(Branch.private == False)
93-
94- def _getExtraMergeProposalExpressions(self):
95- """Extra storm expressions needed for merge proposal queries.
96-
97- Used primarily by the visibility check for target branches.
98- """
99- return [
100- BranchMergeProposal.target_branchID.is_in(
101- Select(Branch.id, Branch.private == False))]
102+
103+ def _getBranchVisibilityExpression(self, branch_class=Branch):
104+ """Return the where clauses for visibility."""
105+ return [branch_class.private == False]
106
107
108 class VisibleBranchCollection(GenericBranchCollection):
109@@ -635,17 +640,21 @@
110 Branch.private == True)))
111 return private_branches
112
113- def _getBranchExpressions(self):
114- """Return the where expressions for this collection."""
115- public_branches = Branch.private == False
116+ def _getBranchVisibilityExpression(self, branch_class=Branch):
117+ """Return the where clauses for visibility.
118+
119+ :param branch_class: The Branch class to use - permits using
120+ ClassAliases.
121+ """
122+ public_branches = branch_class.private == False
123 if self._private_branch_ids is None:
124 # Public only.
125- return self._branch_filter_expressions + [public_branches]
126+ return [public_branches]
127 else:
128 public_or_private = Or(
129 public_branches,
130- Branch.id.is_in(self._private_branch_ids))
131- return self._branch_filter_expressions + [public_or_private]
132+ branch_class.id.is_in(self._private_branch_ids))
133+ return [public_or_private]
134
135 def visibleByUser(self, person):
136 """See `IBranchCollection`."""
137@@ -654,19 +663,3 @@
138 raise InvalidFilter(
139 "Cannot filter for branches visible by user %r, already "
140 "filtering for %r" % (person, self._user))
141-
142- def _getExtraMergeProposalExpressions(self):
143- """Extra storm expressions needed for merge proposal queries.
144-
145- Used primarily by the visibility check for target branches.
146- """
147- if self._private_branch_ids is None:
148- # Public only.
149- visible_branches = Select(Branch.id, Branch.private == False)
150- else:
151- visible_branches = Select(
152- Branch.id,
153- Or(Branch.private == False,
154- Branch.id.is_in(self._private_branch_ids)))
155- return [
156- BranchMergeProposal.target_branchID.is_in(visible_branches)]