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
=== modified file 'lib/lp/soyuz/doc/archive.txt'
--- lib/lp/soyuz/doc/archive.txt 2010-05-19 15:39:52 +0000
+++ lib/lp/soyuz/doc/archive.txt 2010-06-08 11:45:47 +0000
@@ -1296,12 +1296,12 @@
12961296
1297 >>> candidate = ubuntu.main_archive.findDepCandidateByName(1297 >>> candidate = ubuntu.main_archive.findDepCandidateByName(
1298 ... warty_i386, "pmount")1298 ... warty_i386, "pmount")
1299 >>> print candidate.binarypackagename1299 >>> print candidate.binarypackagerelease.binarypackagename.name
1300 pmount1300 pmount
13011301
1302 >>> candidate = cprov.archive.findDepCandidateByName(1302 >>> candidate = cprov.archive.findDepCandidateByName(
1303 ... warty_i386, "pmount")1303 ... warty_i386, "pmount")
1304 >>> print candidate.binarypackagename1304 >>> print candidate.binarypackagerelease.binarypackagename.name
1305 pmount1305 pmount
13061306
1307Since 'python2.4' isn't available in our sampledata (not even1307Since 'python2.4' isn't available in our sampledata (not even
13081308
=== modified file 'lib/lp/soyuz/doc/binarypackagebuild.txt'
--- lib/lp/soyuz/doc/binarypackagebuild.txt 2010-05-13 15:46:46 +0000
+++ lib/lp/soyuz/doc/binarypackagebuild.txt 2010-06-08 11:45:47 +0000
@@ -1004,10 +1004,13 @@
1004 >>> login('foo.bar@canonical.com')1004 >>> login('foo.bar@canonical.com')
1005 >>> from lp.soyuz.interfaces.component import IComponentSet1005 >>> from lp.soyuz.interfaces.component import IComponentSet
1006 >>> main_component = getUtility(IComponentSet)['main']1006 >>> main_component = getUtility(IComponentSet)['main']
1007 >>> pmount_pub = hoary_i386[
1008 ... 'pmount'].currentrelease.current_publishing_record
1007 >>> pmount_pub.component = main_component1009 >>> pmount_pub.component = main_component
1008 >>> depwait_build.dependencies = u'mozilla-firefox, pmount'1010 >>> depwait_build.dependencies = u'mozilla-firefox, pmount'
1009 >>> from canonical.database.sqlbase import flush_database_caches1011 >>> from canonical.database.sqlbase import flush_database_caches
1010 >>> flush_database_caches()1012 >>> flush_database_caches()
1013 >>> transaction.commit()
1011 >>> login(ANONYMOUS)1014 >>> login(ANONYMOUS)
10121015
1013 >>> flush_database_updates()1016 >>> flush_database_updates()
10141017
=== modified file 'lib/lp/soyuz/interfaces/archive.py'
--- lib/lp/soyuz/interfaces/archive.py 2010-05-27 15:20:24 +0000
+++ lib/lp/soyuz/interfaces/archive.py 2010-06-08 11:45:47 +0000
@@ -428,8 +428,8 @@
428 def findDepCandidateByName(distroarchseries, name):428 def findDepCandidateByName(distroarchseries, name):
429 """Return the last published binarypackage by given name.429 """Return the last published binarypackage by given name.
430430
431 Return the PublishedPackage record by binarypackagename or None if431 Return the `BinaryPackagePublishingHistory` record by distroarchseries
432 not found.432 and name, or None if not found.
433 """433 """
434434
435 def removeArchiveDependency(dependency):435 def removeArchiveDependency(dependency):
436436
=== modified file 'lib/lp/soyuz/model/archive.py'
--- lib/lp/soyuz/model/archive.py 2010-06-04 15:00:40 +0000
+++ lib/lp/soyuz/model/archive.py 2010-06-08 11:45:47 +0000
@@ -15,7 +15,7 @@
15from sqlobject import (15from sqlobject import (
16 BoolCol, ForeignKey, IntCol, StringCol)16 BoolCol, ForeignKey, IntCol, StringCol)
17from sqlobject.sqlbuilder import SQLConstant17from sqlobject.sqlbuilder import SQLConstant
18from storm.expr import Or, And, Select, Sum18from storm.expr import Or, And, Select, Sum, In, Desc
19from storm.locals import Count, Join19from storm.locals import Count, Join
20from storm.store import Store20from storm.store import Store
21from storm.zope.interfaces import IResultSet21from storm.zope.interfaces import IResultSet
@@ -32,6 +32,7 @@
32from canonical.database.enumcol import EnumCol32from canonical.database.enumcol import EnumCol
33from canonical.database.sqlbase import (33from canonical.database.sqlbase import (
34 cursor, quote, quote_like, sqlvalues, SQLBase)34 cursor, quote, quote_like, sqlvalues, SQLBase)
35from canonical.launchpad.interfaces.lpstorm import ISlaveStore
35from lp.buildmaster.interfaces.buildbase import BuildStatus36from lp.buildmaster.interfaces.buildbase import BuildStatus
36from lp.buildmaster.model.buildfarmjob import BuildFarmJob37from lp.buildmaster.model.buildfarmjob import BuildFarmJob
37from lp.buildmaster.model.packagebuild import PackageBuild38from lp.buildmaster.model.packagebuild import PackageBuild
@@ -42,6 +43,7 @@
42from lp.soyuz.model.archivedependency import ArchiveDependency43from lp.soyuz.model.archivedependency import ArchiveDependency
43from lp.soyuz.model.archiveauthtoken import ArchiveAuthToken44from lp.soyuz.model.archiveauthtoken import ArchiveAuthToken
44from lp.soyuz.model.archivesubscriber import ArchiveSubscriber45from lp.soyuz.model.archivesubscriber import ArchiveSubscriber
46from lp.soyuz.model.binarypackagename import BinaryPackageName
45from lp.soyuz.model.binarypackagerelease import (47from lp.soyuz.model.binarypackagerelease import (
46 BinaryPackageReleaseDownloadCount)48 BinaryPackageReleaseDownloadCount)
47from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild49from lp.soyuz.model.binarypackagebuild import BinaryPackageBuild
@@ -53,7 +55,6 @@
53from canonical.launchpad.database.librarian import (55from canonical.launchpad.database.librarian import (
54 LibraryFileAlias, LibraryFileContent)56 LibraryFileAlias, LibraryFileContent)
55from lp.soyuz.model.packagediff import PackageDiff57from lp.soyuz.model.packagediff import PackageDiff
56from lp.soyuz.model.publishedpackage import PublishedPackage
57from lp.soyuz.model.publishing import (58from lp.soyuz.model.publishing import (
58 SourcePackagePublishingHistory, BinaryPackagePublishingHistory)59 SourcePackagePublishingHistory, BinaryPackagePublishingHistory)
59from lp.soyuz.model.queue import PackageUpload, PackageUploadSource60from lp.soyuz.model.queue import PackageUpload, PackageUploadSource
@@ -801,15 +802,20 @@
801 IResultSet(self.dependencies).values(802 IResultSet(self.dependencies).values(
802 ArchiveDependency.dependencyID))803 ArchiveDependency.dependencyID))
803804
804 query = """805 store = ISlaveStore(BinaryPackagePublishingHistory)
805 binarypackagename = %s AND806 candidate = store.find(
806 distroarchseries = %s AND807 BinaryPackagePublishingHistory,
807 archive IN %s AND808 BinaryPackageName.name == name,
808 packagepublishingstatus = %s809 BinaryPackageRelease.binarypackagename == BinaryPackageName.id,
809 """ % sqlvalues(name, distroarchseries, archives,810 BinaryPackagePublishingHistory.binarypackagerelease ==
810 PackagePublishingStatus.PUBLISHED)811 BinaryPackageRelease.id,
811812 BinaryPackagePublishingHistory.distroarchseries ==
812 return PublishedPackage.selectFirst(query, orderBy=['-id'])813 distroarchseries,
814 In(BinaryPackagePublishingHistory.archiveID, archives),
815 BinaryPackagePublishingHistory.status ==
816 PackagePublishingStatus.PUBLISHED
817 ).order_by(Desc(BinaryPackagePublishingHistory.id))
818 return candidate.first()
813819
814 def getArchiveDependency(self, dependency):820 def getArchiveDependency(self, dependency):
815 """See `IArchive`."""821 """See `IArchive`."""
@@ -993,9 +999,10 @@
993 distroseries = sourcepackage.distroseries999 distroseries = sourcepackage.distroseries
994 sourcepackagename = sourcepackage.sourcepackagename1000 sourcepackagename = sourcepackage.sourcepackagename
995 component = sourcepackage.latest_published_component1001 component = sourcepackage.latest_published_component
996 # strict_component is True because the source package already exists1002 # strict_component is True because the source package already
997 # (otherwise we couldn't have a suitesourcepackage object) and1003 # exists (otherwise we couldn't have a suitesourcepackage
998 # nascentupload passes True as a matter of policy when the package exists.1004 # object) and nascentupload passes True as a matter of policy
1005 # when the package exists.
999 reason = self.checkUpload(1006 reason = self.checkUpload(
1000 person, distroseries, sourcepackagename, component, pocket,1007 person, distroseries, sourcepackagename, component, pocket,
1001 strict_component=True)1008 strict_component=True)
@@ -1019,7 +1026,7 @@
1019 if not distroseries.canUploadToPocket(pocket):1026 if not distroseries.canUploadToPocket(pocket):
1020 return CannotUploadToPocket(distroseries, pocket)1027 return CannotUploadToPocket(distroseries, pocket)
10211028
1022 def _checkUpload(self, person, distroseries, sourcepackagename, component, 1029 def _checkUpload(self, person, distroseries, sourcepackagename, component,
1023 pocket, strict_component=True):1030 pocket, strict_component=True):
1024 """See `IArchive`."""1031 """See `IArchive`."""
1025 if isinstance(component, basestring):1032 if isinstance(component, basestring):
10261033
=== modified file 'lib/lp/soyuz/model/binarypackagebuild.py'
--- lib/lp/soyuz/model/binarypackagebuild.py 2010-05-21 09:42:21 +0000
+++ lib/lp/soyuz/model/binarypackagebuild.py 2010-06-08 11:45:47 +0000
@@ -33,6 +33,7 @@
33 get_contact_email_addresses, get_email_template)33 get_contact_email_addresses, get_email_template)
34from canonical.launchpad.interfaces.launchpad import (34from canonical.launchpad.interfaces.launchpad import (
35 NotFoundError, ILaunchpadCelebrities)35 NotFoundError, ILaunchpadCelebrities)
36from canonical.launchpad.interfaces.lpstorm import IMasterObject, ISlaveStore
36from canonical.launchpad.mail import (37from canonical.launchpad.mail import (
37 simple_sendmail, format_address)38 simple_sendmail, format_address)
38from canonical.launchpad.webapp import canonical_url39from canonical.launchpad.webapp import canonical_url
@@ -380,7 +381,7 @@
380 return False381 return False
381382
382 if not self._checkDependencyVersion(383 if not self._checkDependencyVersion(
383 dep_candidate.binarypackageversion, version, relation):384 dep_candidate.binarypackagerelease.version, version, relation):
384 return False385 return False
385386
386 # Only PRIMARY archive build dependencies should be restricted387 # Only PRIMARY archive build dependencies should be restricted
@@ -391,7 +392,7 @@
391 # only contains packages in 'main' component.392 # only contains packages in 'main' component.
392 ogre_components = get_components_for_building(self)393 ogre_components = get_components_for_building(self)
393 if (self.archive.purpose == ArchivePurpose.PRIMARY and394 if (self.archive.purpose == ArchivePurpose.PRIMARY and
394 dep_candidate.component not in ogre_components):395 dep_candidate.component.name not in ogre_components):
395 return False396 return False
396397
397 return True398 return True
@@ -542,7 +543,8 @@
542 extra_headers = {543 extra_headers = {
543 'X-Launchpad-Build-State': self.status.name,544 'X-Launchpad-Build-State': self.status.name,
544 'X-Launchpad-Build-Component' : self.current_component.name,545 'X-Launchpad-Build-Component' : self.current_component.name,
545 'X-Launchpad-Build-Arch' : self.distro_arch_series.architecturetag,546 'X-Launchpad-Build-Arch' :
547 self.distro_arch_series.architecturetag,
546 }548 }
547549
548 # XXX cprov 2006-10-27: Temporary extra debug info about the550 # XXX cprov 2006-10-27: Temporary extra debug info about the
@@ -947,7 +949,7 @@
947 logger = logging.getLogger('retry-depwait')949 logger = logging.getLogger('retry-depwait')
948950
949 # Get the MANUALDEPWAIT records for all archives.951 # Get the MANUALDEPWAIT records for all archives.
950 store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR)952 store = ISlaveStore(BinaryPackageBuild)
951953
952 candidates = store.find(954 candidates = store.find(
953 BinaryPackageBuild,955 BinaryPackageBuild,
@@ -956,7 +958,11 @@
956 PackageBuild.build_farm_job == BuildFarmJob.id,958 PackageBuild.build_farm_job == BuildFarmJob.id,
957 BuildFarmJob.status == BuildStatus.MANUALDEPWAIT)959 BuildFarmJob.status == BuildStatus.MANUALDEPWAIT)
958960
959 candidates_count = candidates.count()961 # Materialise the results right away to avoid hitting the
962 # database multiple times.
963 candidates = list(candidates)
964
965 candidates_count = len(candidates)
960 if candidates_count == 0:966 if candidates_count == 0:
961 logger.info("No MANUALDEPWAIT record found.")967 logger.info("No MANUALDEPWAIT record found.")
962 return968 return
@@ -967,6 +973,9 @@
967 for build in candidates:973 for build in candidates:
968 if not build.can_be_retried:974 if not build.can_be_retried:
969 continue975 continue
976 # We're changing 'build' so make sure we have an object from
977 # the master store.
978 build = IMasterObject(build)
970 build.updateDependencies()979 build.updateDependencies()
971 if build.dependencies:980 if build.dependencies:
972 logger.debug(981 logger.debug(
973982
=== modified file 'lib/lp/soyuz/tests/test_binarypackagebuild.py'
--- lib/lp/soyuz/tests/test_binarypackagebuild.py 2010-05-11 21:52:52 +0000
+++ lib/lp/soyuz/tests/test_binarypackagebuild.py 2010-06-08 11:45:47 +0000
@@ -161,6 +161,7 @@
161 # Calling `IBinaryPackageBuild.updateDependencies` makes the build161 # Calling `IBinaryPackageBuild.updateDependencies` makes the build
162 # record ready for dispatch.162 # record ready for dispatch.
163 depwait_build = self._setupSimpleDepwaitContext()163 depwait_build = self._setupSimpleDepwaitContext()
164 self.layer.txn.commit()
164 depwait_build.updateDependencies()165 depwait_build.updateDependencies()
165 self.assertEquals(depwait_build.dependencies, '')166 self.assertEquals(depwait_build.dependencies, '')
166167
@@ -203,6 +204,7 @@
203 contrib = getUtility(IComponentSet).new('contrib')204 contrib = getUtility(IComponentSet).new('contrib')
204 removeSecurityProxy(spr).component = contrib205 removeSecurityProxy(spr).component = contrib
205206
207 self.layer.txn.commit()
206 depwait_build.updateDependencies()208 depwait_build.updateDependencies()
207 self.assertEquals(depwait_build.dependencies, '')209 self.assertEquals(depwait_build.dependencies, '')
208210