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
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py 2011-07-27 04:34:19 +0000
+++ lib/lp/bugs/model/bugtask.py 2011-07-30 13:24:38 +0000
@@ -128,7 +128,6 @@
128 )128 )
129from lp.bugs.model.bugnomination import BugNomination129from lp.bugs.model.bugnomination import BugNomination
130from lp.bugs.model.bugsubscription import BugSubscription130from lp.bugs.model.bugsubscription import BugSubscription
131from lp.bugs.model.structuralsubscription import StructuralSubscription
132from lp.registry.interfaces.distribution import (131from lp.registry.interfaces.distribution import (
133 IDistribution,132 IDistribution,
134 IDistributionSet,133 IDistributionSet,
@@ -1946,55 +1945,55 @@
1946 sqlvalues(personid=params.subscriber.id))1945 sqlvalues(personid=params.subscriber.id))
19471946
1948 if params.structural_subscriber is not None:1947 if params.structural_subscriber is not None:
1949 ssub_match_product = (1948 # See bug 787294 for the story that led to the query elements
1950 BugTask.productID ==1949 # below. Please change with care.
1951 StructuralSubscription.productID)1950 with_clauses.append(
1952 ssub_match_productseries = (1951 '''ss as (SELECT * from StructuralSubscription
1953 BugTask.productseriesID ==1952 WHERE StructuralSubscription.subscriber = %s)'''
1954 StructuralSubscription.productseriesID)
1955 # Prevent circular import problems.
1956 from lp.registry.model.product import Product
1957 ssub_match_project = And(
1958 Product.projectID ==
1959 StructuralSubscription.projectID,
1960 BugTask.product == Product.id)
1961 ssub_match_distribution = (
1962 BugTask.distributionID ==
1963 StructuralSubscription.distributionID)
1964 ssub_match_sourcepackagename = (
1965 BugTask.sourcepackagenameID ==
1966 StructuralSubscription.sourcepackagenameID)
1967 ssub_match_null_sourcepackagename = (
1968 StructuralSubscription.sourcepackagename == None)
1969 ssub_match_distribution_with_optional_package = And(
1970 ssub_match_distribution, Or(
1971 ssub_match_sourcepackagename,
1972 ssub_match_null_sourcepackagename))
1973 ssub_match_distribution_series = (
1974 BugTask.distroseriesID ==
1975 StructuralSubscription.distroseriesID)
1976 ssub_match_milestone = (
1977 BugTask.milestoneID ==
1978 StructuralSubscription.milestoneID)
1979
1980 join_clause = Or(
1981 ssub_match_product,
1982 ssub_match_productseries,
1983 ssub_match_project,
1984 ssub_match_distribution_with_optional_package,
1985 ssub_match_distribution_series,
1986 ssub_match_milestone)
1987
1988 join_tables.append(
1989 (Product, LeftJoin(Product, And(
1990 BugTask.productID == Product.id,
1991 Product.active))))
1992 join_tables.append(
1993 (StructuralSubscription,
1994 Join(StructuralSubscription, join_clause)))
1995 extra_clauses.append(
1996 'StructuralSubscription.subscriber = %s'
1997 % sqlvalues(params.structural_subscriber))1953 % sqlvalues(params.structural_subscriber))
1954 # Prevent circular import problems.
1955 from lp.registry.model.product import Product
1956 join_tables.append(
1957 (Product, LeftJoin(Product, And(
1958 BugTask.productID == Product.id,
1959 Product.active))))
1960 join_tables.append(
1961 (None,
1962 LeftJoin(
1963 SQL('ss ss1'),
1964 BugTask.product == SQL('ss1.product'))))
1965 join_tables.append(
1966 (None,
1967 LeftJoin(
1968 SQL('ss ss2'),
1969 BugTask.productseries == SQL('ss2.productseries'))))
1970 join_tables.append(
1971 (None,
1972 LeftJoin(
1973 SQL('ss ss3'),
1974 Product.project == SQL('ss3.project'))))
1975 join_tables.append(
1976 (None,
1977 LeftJoin(
1978 SQL('ss ss4'),
1979 And(BugTask.distribution == SQL('ss4.distribution'),
1980 Or(BugTask.sourcepackagename ==
1981 SQL('ss4.sourcepackagename'),
1982 SQL('ss4.sourcepackagename IS NULL'))))))
1983 join_tables.append(
1984 (None,
1985 LeftJoin(
1986 SQL('ss ss5'),
1987 BugTask.distroseries == SQL('ss5.distroseries'))))
1988 join_tables.append(
1989 (None,
1990 LeftJoin(
1991 SQL('ss ss6'),
1992 BugTask.milestone == SQL('ss6.milestone'))))
1993 extra_clauses.append(
1994 "NULL_COUNT("
1995 "ARRAY[ss1.id, ss2.id, ss3.id, ss4.id, ss5.id, ss6.id]"
1996 ") < 6")
1998 has_duplicate_results = True1997 has_duplicate_results = True
19991998
2000 # Remove bugtasks from deactivated products, if necessary.1999 # Remove bugtasks from deactivated products, if necessary.
@@ -2488,9 +2487,10 @@
2488 origin = [BugTask]2487 origin = [BugTask]
2489 already_joined = set(origin)2488 already_joined = set(origin)
2490 for table, join in join_tables:2489 for table, join in join_tables:
2491 if table not in already_joined:2490 if table is None or table not in already_joined:
2492 origin.append(join)2491 origin.append(join)
2493 already_joined.add(table)2492 if table is not None:
2493 already_joined.add(table)
2494 for table, join in prejoin_tables:2494 for table, join in prejoin_tables:
2495 if table not in already_joined:2495 if table not in already_joined:
2496 origin.append(join)2496 origin.append(join)
@@ -2544,9 +2544,11 @@
2544 [query, clauseTables, ignore, decorator, join_tables,2544 [query, clauseTables, ignore, decorator, join_tables,
2545 has_duplicate_results, with_clause] = self.buildQuery(arg)2545 has_duplicate_results, with_clause] = self.buildQuery(arg)
2546 origin = self.buildOrigin(join_tables, [], clauseTables)2546 origin = self.buildOrigin(join_tables, [], clauseTables)
2547 localstore = store
2547 if with_clause:2548 if with_clause:
2548 store = orig_store.with_(with_clause)2549 localstore = orig_store.with_(with_clause)
2549 next_result = store.using(*origin).find(inner_resultrow, query)2550 next_result = localstore.using(*origin).find(
2551 inner_resultrow, query)
2550 resultset = resultset.union(next_result)2552 resultset = resultset.union(next_result)
2551 # NB: assumes the decorators are all compatible.2553 # NB: assumes the decorators are all compatible.
2552 # This may need revisiting if e.g. searches on behalf of different2554 # This may need revisiting if e.g. searches on behalf of different