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
=== modified file 'lib/lp/code/model/branchcollection.py'
--- lib/lp/code/model/branchcollection.py 2011-03-23 16:28:51 +0000
+++ lib/lp/code/model/branchcollection.py 2011-03-26 00:51:33 +0000
@@ -21,6 +21,7 @@
21 Select,21 Select,
22 Union,22 Union,
23 )23 )
24from storm.info import ClassAlias
24from zope.component import getUtility25from zope.component import getUtility
25from zope.interface import implements26from zope.interface import implements
2627
@@ -161,7 +162,12 @@
161162
162 def _getBranchExpressions(self):163 def _getBranchExpressions(self):
163 """Return the where expressions for this collection."""164 """Return the where expressions for this collection."""
164 return self._branch_filter_expressions165 return (self._branch_filter_expressions
166 + self._getBranchVisibilityExpression())
167
168 def _getBranchVisibilityExpression(self, branch_class=None):
169 """Return the where clauses for visibility."""
170 return []
165171
166 def getBranches(self, eager_load=False):172 def getBranches(self, eager_load=False):
167 """See `IBranchCollection`."""173 """See `IBranchCollection`."""
@@ -217,10 +223,20 @@
217 def getMergeProposals(self, statuses=None, for_branches=None,223 def getMergeProposals(self, statuses=None, for_branches=None,
218 target_branch=None, merged_revnos=None):224 target_branch=None, merged_revnos=None):
219 """See `IBranchCollection`."""225 """See `IBranchCollection`."""
220 expressions = [226 Target = ClassAlias(Branch, "target")
221 BranchMergeProposal.source_branchID.is_in(227 tables = [Branch] + self._tables.values() + [
222 self._getBranchIdQuery()),228 Join(BranchMergeProposal, And(
229 Branch.id==BranchMergeProposal.source_branchID,
230 *self._branch_filter_expressions)),
231 Join(Target, Target.id==BranchMergeProposal.target_branchID)
223 ]232 ]
233 expressions = []
234 source_visible = self._getBranchVisibilityExpression()
235 if source_visible:
236 expressions.append(BranchMergeProposal.source_branchID.is_in(
237 Select(Branch.id, source_visible)))
238 expressions.append(BranchMergeProposal.target_branchID.is_in(
239 Select(Target.id, self._getBranchVisibilityExpression(Target))))
224 if for_branches is not None:240 if for_branches is not None:
225 branch_ids = [branch.id for branch in for_branches]241 branch_ids = [branch.id for branch in for_branches]
226 expressions.append(242 expressions.append(
@@ -231,18 +247,10 @@
231 if merged_revnos is not None:247 if merged_revnos is not None:
232 expressions.append(248 expressions.append(
233 BranchMergeProposal.merged_revno.is_in(merged_revnos))249 BranchMergeProposal.merged_revno.is_in(merged_revnos))
234 expressions.extend(self._getExtraMergeProposalExpressions())
235 if statuses is not None:250 if statuses is not None:
236 expressions.append(251 expressions.append(
237 BranchMergeProposal.queue_status.is_in(statuses))252 BranchMergeProposal.queue_status.is_in(statuses))
238 return self.store.find(BranchMergeProposal, expressions)253 return self.store.using(*tables).find(BranchMergeProposal, *expressions)
239
240 def _getExtraMergeProposalExpressions(self):
241 """Extra storm expressions needed for merge proposal queries.
242
243 Used primarily by the visibility check for target branches.
244 """
245 return []
246254
247 def getMergeProposalsForPerson(self, person, status=None):255 def getMergeProposalsForPerson(self, person, status=None):
248 """See `IBranchCollection`."""256 """See `IBranchCollection`."""
@@ -266,12 +274,15 @@
266 CodeReviewVoteReference.reviewer == reviewer,274 CodeReviewVoteReference.reviewer == reviewer,
267 BranchMergeProposal.source_branchID.is_in(275 BranchMergeProposal.source_branchID.is_in(
268 self._getBranchIdQuery())]276 self._getBranchIdQuery())]
269 expressions.extend(self._getExtraMergeProposalExpressions())277 visibility = self._getBranchVisibilityExpression()
278 if visibility:
279 expressions.append(BranchMergeProposal.target_branchID.is_in(
280 Select(Branch.id, visibility)))
270 if status is not None:281 if status is not None:
271 expressions.append(282 expressions.append(
272 BranchMergeProposal.queue_status.is_in(status))283 BranchMergeProposal.queue_status.is_in(status))
273 proposals = self.store.using(*tables).find(284 proposals = self.store.using(*tables).find(
274 BranchMergeProposal, expressions)285 BranchMergeProposal, *expressions)
275 # Apply sorting here as we can't do it in the browser code. We need286 # Apply sorting here as we can't do it in the browser code. We need
276 # to think carefully about the best places to do this, but not here287 # to think carefully about the best places to do this, but not here
277 # nor now.288 # nor now.
@@ -557,16 +568,10 @@
557 store=store,568 store=store,
558 branch_filter_expressions=list(branch_filter_expressions),569 branch_filter_expressions=list(branch_filter_expressions),
559 tables=tables, exclude_from_search=exclude_from_search)570 tables=tables, exclude_from_search=exclude_from_search)
560 self._branch_filter_expressions.append(Branch.private == False)571
561572 def _getBranchVisibilityExpression(self, branch_class=Branch):
562 def _getExtraMergeProposalExpressions(self):573 """Return the where clauses for visibility."""
563 """Extra storm expressions needed for merge proposal queries.574 return [branch_class.private == False]
564
565 Used primarily by the visibility check for target branches.
566 """
567 return [
568 BranchMergeProposal.target_branchID.is_in(
569 Select(Branch.id, Branch.private == False))]
570575
571576
572class VisibleBranchCollection(GenericBranchCollection):577class VisibleBranchCollection(GenericBranchCollection):
@@ -635,17 +640,21 @@
635 Branch.private == True)))640 Branch.private == True)))
636 return private_branches641 return private_branches
637642
638 def _getBranchExpressions(self):643 def _getBranchVisibilityExpression(self, branch_class=Branch):
639 """Return the where expressions for this collection."""644 """Return the where clauses for visibility.
640 public_branches = Branch.private == False645
646 :param branch_class: The Branch class to use - permits using
647 ClassAliases.
648 """
649 public_branches = branch_class.private == False
641 if self._private_branch_ids is None:650 if self._private_branch_ids is None:
642 # Public only.651 # Public only.
643 return self._branch_filter_expressions + [public_branches]652 return [public_branches]
644 else:653 else:
645 public_or_private = Or(654 public_or_private = Or(
646 public_branches,655 public_branches,
647 Branch.id.is_in(self._private_branch_ids))656 branch_class.id.is_in(self._private_branch_ids))
648 return self._branch_filter_expressions + [public_or_private]657 return [public_or_private]
649658
650 def visibleByUser(self, person):659 def visibleByUser(self, person):
651 """See `IBranchCollection`."""660 """See `IBranchCollection`."""
@@ -654,19 +663,3 @@
654 raise InvalidFilter(663 raise InvalidFilter(
655 "Cannot filter for branches visible by user %r, already "664 "Cannot filter for branches visible by user %r, already "
656 "filtering for %r" % (person, self._user))665 "filtering for %r" % (person, self._user))
657
658 def _getExtraMergeProposalExpressions(self):
659 """Extra storm expressions needed for merge proposal queries.
660
661 Used primarily by the visibility check for target branches.
662 """
663 if self._private_branch_ids is None:
664 # Public only.
665 visible_branches = Select(Branch.id, Branch.private == False)
666 else:
667 visible_branches = Select(
668 Branch.id,
669 Or(Branch.private == False,
670 Branch.id.is_in(self._private_branch_ids)))
671 return [
672 BranchMergeProposal.target_branchID.is_in(visible_branches)]