Merge lp:~stevenk/launchpad/builder-history-better-query into lp:launchpad

Proposed by Steve Kowalik on 2012-09-11
Status: Merged
Approved by: Steve Kowalik on 2012-09-11
Approved revision: no longer in the source branch.
Merged at revision: 15934
Proposed branch: lp:~stevenk/launchpad/builder-history-better-query
Merge into: lp:launchpad
Diff against target: 177 lines (+25/-72)
2 files modified
lib/lp/buildmaster/model/buildfarmjob.py (+22/-68)
lib/lp/soyuz/browser/builder.py (+3/-4)
To merge this branch: bzr merge lp:~stevenk/launchpad/builder-history-better-query
Reviewer Review Type Date Requested Status
William Grant code 2012-09-11 Approve on 2012-09-11
Review via email: mp+123676@code.launchpad.net

Commit Message

Rewrite the two (seperate) queries that are constructed by IBuildFarmJobSet.getBuildsForBuilder().

Description of the Change

Rewrite the two (seperate) queries that are constructed by IBuildFarmJobSet.getBuildsForBuilder(). This, in conjunction with https://code.launchpad.net/~stevenk/launchpad/buildfarmjob-index/+merge/123675 pulls down the major query used on Builder:+history from 9 seconds to about 2 ms. For a further win, it reduces the line count of the function by a fair bit.

Also switch BuilderHistoryView to making use of StormRangeFactory.

To post a comment you must log in.
William Grant (wgrant) wrote :

126 + TeamParticipation.teamID,
127 + where=(TeamParticipation.person == user),
128 + distinct=True))))

DISTINCT isn't necessary here. There's a unique constraint which implies it.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/buildmaster/model/buildfarmjob.py'
2--- lib/lp/buildmaster/model/buildfarmjob.py 2012-01-02 11:21:14 +0000
3+++ lib/lp/buildmaster/model/buildfarmjob.py 2012-09-11 06:20:24 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
6+# Copyright 2009-2012 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 __metaclass__ = type
10@@ -9,18 +9,16 @@
11 'BuildFarmJobOldDerived',
12 ]
13
14-
15 import hashlib
16
17 from lazr.delegates import delegates
18 import pytz
19 from storm.expr import (
20- And,
21 Desc,
22 LeftJoin,
23+ Not,
24 Or,
25 Select,
26- Union,
27 )
28 from storm.locals import (
29 Bool,
30@@ -42,7 +40,6 @@
31 from zope.security.proxy import removeSecurityProxy
32
33 from lp.app.errors import NotFoundError
34-from lp.app.interfaces.launchpad import ILaunchpadCelebrities
35 from lp.buildmaster.enums import (
36 BuildFarmJobType,
37 BuildStatus,
38@@ -56,6 +53,7 @@
39 ISpecificBuildFarmJobSource,
40 )
41 from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
42+from lp.registry.interfaces.role import IPersonRoles
43 from lp.registry.model.teammembership import TeamParticipation
44 from lp.services.database.constants import UTC_NOW
45 from lp.services.database.enumcol import DBEnum
46@@ -422,9 +420,9 @@
47 from lp.buildmaster.model.packagebuild import PackageBuild
48 from lp.soyuz.model.archive import Archive
49
50- extra_clauses = [BuildFarmJob.builder == builder_id]
51+ clauses = [BuildFarmJob.builder == builder_id]
52 if status is not None:
53- extra_clauses.append(BuildFarmJob.status == status)
54+ clauses.append(BuildFarmJob.status == status)
55
56 # We need to ensure that we don't include any private builds.
57 # Currently only package builds can be private (via their
58@@ -437,70 +435,26 @@
59 PackageBuild.build_farm_job == BuildFarmJob.id),
60 ]
61
62- # STORM syntax has totally obfuscated this query and wasted
63- # THREE hours of my time converting perfectly good SQL syntax. I'm
64- # really sorry if you're the poor sap who has to maintain this.
65-
66- inner_privacy_query = (
67- Union(
68- Select(
69- Archive.id,
70- tables=(Archive,),
71- where=(Archive._private == False)
72- ),
73- Select(
74- Archive.id,
75- tables=(Archive,),
76- where=And(
77- Archive._private == True,
78- Archive.ownerID.is_in(
79- Select(
80- TeamParticipation.teamID,
81- where=(TeamParticipation.person == user),
82- distinct=True
83- )
84- )
85- )
86- )
87- )
88- )
89-
90 if user is None:
91 # Anonymous requests don't get to see private builds at all.
92- extra_clauses.append(
93- Or(
94- PackageBuild.id == None,
95- PackageBuild.archive_id.is_in(
96+ origin.append(
97+ LeftJoin(Archive, Archive.id == PackageBuild.archive_id))
98+ clauses.append(Or(PackageBuild.id == None, Not(Archive._private)))
99+ elif not IPersonRoles(user).in_admin:
100+ # Non-admin users see all public builds and the specific
101+ # private builds to which they have access.
102+ origin.append(
103+ LeftJoin(Archive, Archive.id == PackageBuild.archive_id))
104+ clauses.append(
105+ Or(PackageBuild.id == None, Not(Archive._private),
106+ Archive.ownerID.is_in(
107 Select(
108- Archive.id,
109- tables=(Archive,),
110- where=(Archive._private == False)
111- )
112- )
113- )
114- )
115-
116- elif user.inTeam(getUtility(ILaunchpadCelebrities).admin):
117- # Admins get to see everything.
118- pass
119- else:
120- # Everyone else sees all public builds and the
121- # specific private builds to which they have access.
122- extra_clauses.append(
123- Or(
124- PackageBuild.id == None,
125- PackageBuild.archive_id.is_in(inner_privacy_query)
126- )
127- )
128-
129- filtered_builds = IStore(BuildFarmJob).using(*origin).find(
130- BuildFarmJob, *extra_clauses)
131-
132- filtered_builds.order_by(
133- Desc(BuildFarmJob.date_finished), BuildFarmJob.id)
134- filtered_builds.config(distinct=True)
135-
136- return filtered_builds
137+ TeamParticipation.teamID,
138+ where=(TeamParticipation.person == user)))))
139+
140+ return IStore(BuildFarmJob).using(*origin).find(
141+ BuildFarmJob, *clauses).order_by(
142+ Desc(BuildFarmJob.date_finished), BuildFarmJob.id)
143
144 def getByID(self, job_id):
145 """See `IBuildfarmJobSet`."""
146
147=== modified file 'lib/lp/soyuz/browser/builder.py'
148--- lib/lp/soyuz/browser/builder.py 2012-05-23 10:28:40 +0000
149+++ lib/lp/soyuz/browser/builder.py 2012-09-11 06:20:24 +0000
150@@ -21,10 +21,7 @@
151 import operator
152
153 from lazr.restful.utils import smartquote
154-from zope.app.form.browser import (
155- TextAreaWidget,
156- TextWidget,
157- )
158+from zope.app.form.browser import TextWidget
159 from zope.component import getUtility
160 from zope.event import notify
161 from zope.lifecycleevent import ObjectCreatedEvent
162@@ -58,6 +55,7 @@
163 StandardLaunchpadFacets,
164 stepthrough,
165 )
166+from lp.services.webapp.batching import StormRangeFactory
167 from lp.services.webapp.breadcrumb import Breadcrumb
168 from lp.soyuz.browser.build import (
169 BuildNavigationMixin,
170@@ -296,6 +294,7 @@
171
172 page_title = 'Build history'
173 binary_only = False
174+ range_factory = StormRangeFactory
175
176 @property
177 def label(self):