Merge lp:~gary/launchpad/bug787294 into lp:launchpad

Proposed by Gary Poster
Status: Merged
Approved by: Gary Poster
Approved revision: no longer in the source branch.
Merged at revision: 13572
Proposed branch: lp:~gary/launchpad/bug787294
Merge into: lp:launchpad
Diff against target: 142 lines (+55/-53)
1 file modified
lib/lp/bugs/model/bugtask.py (+55/-53)
To merge this branch: bzr merge lp:~gary/launchpad/bug787294
Reviewer Review Type Date Requested Status
Henning Eggers (community) Approve
Review via email: mp+69887@code.launchpad.net

Commit message

Translate the raw SQL optimization work Robert did for bug 787294 into code.

Description of the change

This branch translates the raw SQL optimization work Robert did for bug 787294 into code.

This was attempted once before, but the translation was not close enough. This time, I think we are close enough. I can share the SQL we are generating now if desired. The proof is in the pudding: the SQL from the previous attempt consistently takes > 35 seconds on staging; using the SQL generated in this branch, I've gotten < 1 second on staging after the cache is warm. (Of course, with a cold cache, staging takes some gargantuan amount of time.)

lint is happy. ec2 test is happy.

Thanks

Gary

To post a comment you must log in.
Revision history for this message
Henning Eggers (henninge) wrote :

Congratulations on fixing this! The code looks good formally and since it passes tests I approve landing this. I do not pretend to understand the particulars of this query or its optimization so if you want a review on that, feel free to request another one. ;-)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/model/bugtask.py'
2--- lib/lp/bugs/model/bugtask.py 2011-07-27 04:34:19 +0000
3+++ lib/lp/bugs/model/bugtask.py 2011-07-30 13:24:38 +0000
4@@ -128,7 +128,6 @@
5 )
6 from lp.bugs.model.bugnomination import BugNomination
7 from lp.bugs.model.bugsubscription import BugSubscription
8-from lp.bugs.model.structuralsubscription import StructuralSubscription
9 from lp.registry.interfaces.distribution import (
10 IDistribution,
11 IDistributionSet,
12@@ -1946,55 +1945,55 @@
13 sqlvalues(personid=params.subscriber.id))
14
15 if params.structural_subscriber is not None:
16- ssub_match_product = (
17- BugTask.productID ==
18- StructuralSubscription.productID)
19- ssub_match_productseries = (
20- BugTask.productseriesID ==
21- StructuralSubscription.productseriesID)
22- # Prevent circular import problems.
23- from lp.registry.model.product import Product
24- ssub_match_project = And(
25- Product.projectID ==
26- StructuralSubscription.projectID,
27- BugTask.product == Product.id)
28- ssub_match_distribution = (
29- BugTask.distributionID ==
30- StructuralSubscription.distributionID)
31- ssub_match_sourcepackagename = (
32- BugTask.sourcepackagenameID ==
33- StructuralSubscription.sourcepackagenameID)
34- ssub_match_null_sourcepackagename = (
35- StructuralSubscription.sourcepackagename == None)
36- ssub_match_distribution_with_optional_package = And(
37- ssub_match_distribution, Or(
38- ssub_match_sourcepackagename,
39- ssub_match_null_sourcepackagename))
40- ssub_match_distribution_series = (
41- BugTask.distroseriesID ==
42- StructuralSubscription.distroseriesID)
43- ssub_match_milestone = (
44- BugTask.milestoneID ==
45- StructuralSubscription.milestoneID)
46-
47- join_clause = Or(
48- ssub_match_product,
49- ssub_match_productseries,
50- ssub_match_project,
51- ssub_match_distribution_with_optional_package,
52- ssub_match_distribution_series,
53- ssub_match_milestone)
54-
55- join_tables.append(
56- (Product, LeftJoin(Product, And(
57- BugTask.productID == Product.id,
58- Product.active))))
59- join_tables.append(
60- (StructuralSubscription,
61- Join(StructuralSubscription, join_clause)))
62- extra_clauses.append(
63- 'StructuralSubscription.subscriber = %s'
64+ # See bug 787294 for the story that led to the query elements
65+ # below. Please change with care.
66+ with_clauses.append(
67+ '''ss as (SELECT * from StructuralSubscription
68+ WHERE StructuralSubscription.subscriber = %s)'''
69 % sqlvalues(params.structural_subscriber))
70+ # Prevent circular import problems.
71+ from lp.registry.model.product import Product
72+ join_tables.append(
73+ (Product, LeftJoin(Product, And(
74+ BugTask.productID == Product.id,
75+ Product.active))))
76+ join_tables.append(
77+ (None,
78+ LeftJoin(
79+ SQL('ss ss1'),
80+ BugTask.product == SQL('ss1.product'))))
81+ join_tables.append(
82+ (None,
83+ LeftJoin(
84+ SQL('ss ss2'),
85+ BugTask.productseries == SQL('ss2.productseries'))))
86+ join_tables.append(
87+ (None,
88+ LeftJoin(
89+ SQL('ss ss3'),
90+ Product.project == SQL('ss3.project'))))
91+ join_tables.append(
92+ (None,
93+ LeftJoin(
94+ SQL('ss ss4'),
95+ And(BugTask.distribution == SQL('ss4.distribution'),
96+ Or(BugTask.sourcepackagename ==
97+ SQL('ss4.sourcepackagename'),
98+ SQL('ss4.sourcepackagename IS NULL'))))))
99+ join_tables.append(
100+ (None,
101+ LeftJoin(
102+ SQL('ss ss5'),
103+ BugTask.distroseries == SQL('ss5.distroseries'))))
104+ join_tables.append(
105+ (None,
106+ LeftJoin(
107+ SQL('ss ss6'),
108+ BugTask.milestone == SQL('ss6.milestone'))))
109+ extra_clauses.append(
110+ "NULL_COUNT("
111+ "ARRAY[ss1.id, ss2.id, ss3.id, ss4.id, ss5.id, ss6.id]"
112+ ") < 6")
113 has_duplicate_results = True
114
115 # Remove bugtasks from deactivated products, if necessary.
116@@ -2488,9 +2487,10 @@
117 origin = [BugTask]
118 already_joined = set(origin)
119 for table, join in join_tables:
120- if table not in already_joined:
121+ if table is None or table not in already_joined:
122 origin.append(join)
123- already_joined.add(table)
124+ if table is not None:
125+ already_joined.add(table)
126 for table, join in prejoin_tables:
127 if table not in already_joined:
128 origin.append(join)
129@@ -2544,9 +2544,11 @@
130 [query, clauseTables, ignore, decorator, join_tables,
131 has_duplicate_results, with_clause] = self.buildQuery(arg)
132 origin = self.buildOrigin(join_tables, [], clauseTables)
133+ localstore = store
134 if with_clause:
135- store = orig_store.with_(with_clause)
136- next_result = store.using(*origin).find(inner_resultrow, query)
137+ localstore = orig_store.with_(with_clause)
138+ next_result = localstore.using(*origin).find(
139+ inner_resultrow, query)
140 resultset = resultset.union(next_result)
141 # NB: assumes the decorators are all compatible.
142 # This may need revisiting if e.g. searches on behalf of different