Merge lp:~julian-edwards/launchpad/slow-retry-depwait-bug-590757 into lp:launchpad

Proposed by Julian Edwards
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 10965
Proposed branch: lp:~julian-edwards/launchpad/slow-retry-depwait-bug-590757
Merge into: lp:launchpad
Diff against target: 232 lines (+45/-24)
6 files modified
lib/lp/soyuz/doc/archive.txt (+2/-2)
lib/lp/soyuz/doc/binarypackagebuild.txt (+3/-0)
lib/lp/soyuz/interfaces/archive.py (+2/-2)
lib/lp/soyuz/model/archive.py (+22/-15)
lib/lp/soyuz/model/binarypackagebuild.py (+14/-5)
lib/lp/soyuz/tests/test_binarypackagebuild.py (+2/-0)
To merge this branch: bzr merge lp:~julian-edwards/launchpad/slow-retry-depwait-bug-590757
Reviewer Review Type Date Requested Status
Francis J. Lacoste (community) release-critical Approve
Graham Binns (community) code Approve
Review via email: mp+27038@code.launchpad.net

Description of the change

= Summary =
Fix slow retry-depwait script

== Proposed fix ==
buildd-retry-depwait was taking 10 minutes to run but since 10.05 it's taking
over 2 hours. This is due to some very inefficient queries which seem to have
been exposed as such by the recent build schema re-modelling.

== Pre-implementation notes ==
I've chatted to stub and there's a bunch of improvements made to the code and
to the queries:
 * Materialise query results early to avoid re-issuing long ones
 * Use the slave database where possible which will be much quicker than the
master, and also frees the master to make the whole of LP faster
 * The query in IArchive.findDepCandidateByName() was re-wrtitten in Storm.
It was using a view called PublishedPackage which was causing many unnecessary
joins on unrelated tables. The new query only joins what it has to, and it
also uses the slave store.

Unfortunately the use of a slave store means the tests needed a liberal
sprinkling of commit() which will slow them down. :(

There's also a bunch of lint fixed.

== Tests ==

bin/test -cvvt binarypackagebuild.txt -t test_binarypackagebuild

== Demo and Q/A ==

I've QA'ed this already on dogfood. Without the change it takes 9 hours to
run the script. With the change, it takes 50 minutes. I expect a similar
relative improvement on production.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/soyuz/doc/archive.txt
  lib/lp/soyuz/interfaces/archive.py
  lib/lp/soyuz/tests/test_binarypackagebuild.py
  lib/lp/soyuz/model/archive.py
  lib/lp/soyuz/model/binarypackagebuild.py
  lib/lp/soyuz/doc/binarypackagebuild.txt

== Pylint notices ==

*Ridiculously wrong output deleted*

To post a comment you must log in.
Revision history for this message
Graham Binns (gmb) :
review: Approve (code)
Revision history for this message
Francis J. Lacoste (flacoste) :
review: Approve (release-critical)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/doc/archive.txt'
2--- lib/lp/soyuz/doc/archive.txt 2010-05-19 15:39:52 +0000
3+++ lib/lp/soyuz/doc/archive.txt 2010-06-08 11:45:47 +0000
4@@ -1296,12 +1296,12 @@
5
6 >>> candidate = ubuntu.main_archive.findDepCandidateByName(
7 ... warty_i386, "pmount")
8- >>> print candidate.binarypackagename
9+ >>> print candidate.binarypackagerelease.binarypackagename.name
10 pmount
11
12 >>> candidate = cprov.archive.findDepCandidateByName(
13 ... warty_i386, "pmount")
14- >>> print candidate.binarypackagename
15+ >>> print candidate.binarypackagerelease.binarypackagename.name
16 pmount
17
18 Since 'python2.4' isn't available in our sampledata (not even
19
20=== modified file 'lib/lp/soyuz/doc/binarypackagebuild.txt'
21--- lib/lp/soyuz/doc/binarypackagebuild.txt 2010-05-13 15:46:46 +0000
22+++ lib/lp/soyuz/doc/binarypackagebuild.txt 2010-06-08 11:45:47 +0000
23@@ -1004,10 +1004,13 @@
24 >>> login('foo.bar@canonical.com')
25 >>> from lp.soyuz.interfaces.component import IComponentSet
26 >>> main_component = getUtility(IComponentSet)['main']
27+ >>> pmount_pub = hoary_i386[
28+ ... 'pmount'].currentrelease.current_publishing_record
29 >>> pmount_pub.component = main_component
30 >>> depwait_build.dependencies = u'mozilla-firefox, pmount'
31 >>> from canonical.database.sqlbase import flush_database_caches
32 >>> flush_database_caches()
33+ >>> transaction.commit()
34 >>> login(ANONYMOUS)
35
36 >>> flush_database_updates()
37
38=== modified file 'lib/lp/soyuz/interfaces/archive.py'
39--- lib/lp/soyuz/interfaces/archive.py 2010-05-27 15:20:24 +0000
40+++ lib/lp/soyuz/interfaces/archive.py 2010-06-08 11:45:47 +0000
41@@ -428,8 +428,8 @@
42 def findDepCandidateByName(distroarchseries, name):
43 """Return the last published binarypackage by given name.
44
45- Return the PublishedPackage record by binarypackagename or None if
46- not found.
47+ Return the `BinaryPackagePublishingHistory` record by distroarchseries
48+ and name, or None if not found.
49 """
50
51 def removeArchiveDependency(dependency):
52
53=== modified file 'lib/lp/soyuz/model/archive.py'
54--- lib/lp/soyuz/model/archive.py 2010-06-04 15:00:40 +0000
55+++ lib/lp/soyuz/model/archive.py 2010-06-08 11:45:47 +0000
56@@ -15,7 +15,7 @@
57 from sqlobject import (
58 BoolCol, ForeignKey, IntCol, StringCol)
59 from sqlobject.sqlbuilder import SQLConstant
60-from storm.expr import Or, And, Select, Sum
61+from storm.expr import Or, And, Select, Sum, In, Desc
62 from storm.locals import Count, Join
63 from storm.store import Store
64 from storm.zope.interfaces import IResultSet
65@@ -32,6 +32,7 @@
66 from canonical.database.enumcol import EnumCol
67 from canonical.database.sqlbase import (
68 cursor, quote, quote_like, sqlvalues, SQLBase)
69+from canonical.launchpad.interfaces.lpstorm import ISlaveStore
70 from lp.buildmaster.interfaces.buildbase import BuildStatus
71 from lp.buildmaster.model.buildfarmjob import BuildFarmJob
72 from lp.buildmaster.model.packagebuild import PackageBuild
73@@ -42,6 +43,7 @@
74 from lp.soyuz.model.archivedependency import ArchiveDependency
75 from lp.soyuz.model.archiveauthtoken import ArchiveAuthToken
76 from lp.soyuz.model.archivesubscriber import ArchiveSubscriber
77+from lp.soyuz.model.binarypackagename import BinaryPackageName
78 from lp.soyuz.model.binarypackagerelease import (
79 BinaryPackageReleaseDownloadCount)
80 from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
81@@ -53,7 +55,6 @@
82 from canonical.launchpad.database.librarian import (
83 LibraryFileAlias, LibraryFileContent)
84 from lp.soyuz.model.packagediff import PackageDiff
85-from lp.soyuz.model.publishedpackage import PublishedPackage
86 from lp.soyuz.model.publishing import (
87 SourcePackagePublishingHistory, BinaryPackagePublishingHistory)
88 from lp.soyuz.model.queue import PackageUpload, PackageUploadSource
89@@ -801,15 +802,20 @@
90 IResultSet(self.dependencies).values(
91 ArchiveDependency.dependencyID))
92
93- query = """
94- binarypackagename = %s AND
95- distroarchseries = %s AND
96- archive IN %s AND
97- packagepublishingstatus = %s
98- """ % sqlvalues(name, distroarchseries, archives,
99- PackagePublishingStatus.PUBLISHED)
100-
101- return PublishedPackage.selectFirst(query, orderBy=['-id'])
102+ store = ISlaveStore(BinaryPackagePublishingHistory)
103+ candidate = store.find(
104+ BinaryPackagePublishingHistory,
105+ BinaryPackageName.name == name,
106+ BinaryPackageRelease.binarypackagename == BinaryPackageName.id,
107+ BinaryPackagePublishingHistory.binarypackagerelease ==
108+ BinaryPackageRelease.id,
109+ BinaryPackagePublishingHistory.distroarchseries ==
110+ distroarchseries,
111+ In(BinaryPackagePublishingHistory.archiveID, archives),
112+ BinaryPackagePublishingHistory.status ==
113+ PackagePublishingStatus.PUBLISHED
114+ ).order_by(Desc(BinaryPackagePublishingHistory.id))
115+ return candidate.first()
116
117 def getArchiveDependency(self, dependency):
118 """See `IArchive`."""
119@@ -993,9 +999,10 @@
120 distroseries = sourcepackage.distroseries
121 sourcepackagename = sourcepackage.sourcepackagename
122 component = sourcepackage.latest_published_component
123- # strict_component is True because the source package already exists
124- # (otherwise we couldn't have a suitesourcepackage object) and
125- # nascentupload passes True as a matter of policy when the package exists.
126+ # strict_component is True because the source package already
127+ # exists (otherwise we couldn't have a suitesourcepackage
128+ # object) and nascentupload passes True as a matter of policy
129+ # when the package exists.
130 reason = self.checkUpload(
131 person, distroseries, sourcepackagename, component, pocket,
132 strict_component=True)
133@@ -1019,7 +1026,7 @@
134 if not distroseries.canUploadToPocket(pocket):
135 return CannotUploadToPocket(distroseries, pocket)
136
137- def _checkUpload(self, person, distroseries, sourcepackagename, component,
138+ def _checkUpload(self, person, distroseries, sourcepackagename, component,
139 pocket, strict_component=True):
140 """See `IArchive`."""
141 if isinstance(component, basestring):
142
143=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
144--- lib/lp/soyuz/model/binarypackagebuild.py 2010-05-21 09:42:21 +0000
145+++ lib/lp/soyuz/model/binarypackagebuild.py 2010-06-08 11:45:47 +0000
146@@ -33,6 +33,7 @@
147 get_contact_email_addresses, get_email_template)
148 from canonical.launchpad.interfaces.launchpad import (
149 NotFoundError, ILaunchpadCelebrities)
150+from canonical.launchpad.interfaces.lpstorm import IMasterObject, ISlaveStore
151 from canonical.launchpad.mail import (
152 simple_sendmail, format_address)
153 from canonical.launchpad.webapp import canonical_url
154@@ -380,7 +381,7 @@
155 return False
156
157 if not self._checkDependencyVersion(
158- dep_candidate.binarypackageversion, version, relation):
159+ dep_candidate.binarypackagerelease.version, version, relation):
160 return False
161
162 # Only PRIMARY archive build dependencies should be restricted
163@@ -391,7 +392,7 @@
164 # only contains packages in 'main' component.
165 ogre_components = get_components_for_building(self)
166 if (self.archive.purpose == ArchivePurpose.PRIMARY and
167- dep_candidate.component not in ogre_components):
168+ dep_candidate.component.name not in ogre_components):
169 return False
170
171 return True
172@@ -542,7 +543,8 @@
173 extra_headers = {
174 'X-Launchpad-Build-State': self.status.name,
175 'X-Launchpad-Build-Component' : self.current_component.name,
176- 'X-Launchpad-Build-Arch' : self.distro_arch_series.architecturetag,
177+ 'X-Launchpad-Build-Arch' :
178+ self.distro_arch_series.architecturetag,
179 }
180
181 # XXX cprov 2006-10-27: Temporary extra debug info about the
182@@ -947,7 +949,7 @@
183 logger = logging.getLogger('retry-depwait')
184
185 # Get the MANUALDEPWAIT records for all archives.
186- store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)
187+ store = ISlaveStore(BinaryPackageBuild)
188
189 candidates = store.find(
190 BinaryPackageBuild,
191@@ -956,7 +958,11 @@
192 PackageBuild.build_farm_job == BuildFarmJob.id,
193 BuildFarmJob.status == BuildStatus.MANUALDEPWAIT)
194
195- candidates_count = candidates.count()
196+ # Materialise the results right away to avoid hitting the
197+ # database multiple times.
198+ candidates = list(candidates)
199+
200+ candidates_count = len(candidates)
201 if candidates_count == 0:
202 logger.info("No MANUALDEPWAIT record found.")
203 return
204@@ -967,6 +973,9 @@
205 for build in candidates:
206 if not build.can_be_retried:
207 continue
208+ # We're changing 'build' so make sure we have an object from
209+ # the master store.
210+ build = IMasterObject(build)
211 build.updateDependencies()
212 if build.dependencies:
213 logger.debug(
214
215=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuild.py'
216--- lib/lp/soyuz/tests/test_binarypackagebuild.py 2010-05-11 21:52:52 +0000
217+++ lib/lp/soyuz/tests/test_binarypackagebuild.py 2010-06-08 11:45:47 +0000
218@@ -161,6 +161,7 @@
219 # Calling `IBinaryPackageBuild.updateDependencies` makes the build
220 # record ready for dispatch.
221 depwait_build = self._setupSimpleDepwaitContext()
222+ self.layer.txn.commit()
223 depwait_build.updateDependencies()
224 self.assertEquals(depwait_build.dependencies, '')
225
226@@ -203,6 +204,7 @@
227 contrib = getUtility(IComponentSet).new('contrib')
228 removeSecurityProxy(spr).component = contrib
229
230+ self.layer.txn.commit()
231 depwait_build.updateDependencies()
232 self.assertEquals(depwait_build.dependencies, '')
233