Merge lp:~stevenk/launchpad/set-spph-packageupload into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: Steve Kowalik
Approved revision: no longer in the source branch.
Merged at revision: 15369
Proposed branch: lp:~stevenk/launchpad/set-spph-packageupload
Merge into: lp:launchpad
Diff against target: 177 lines (+133/-1)
2 files modified
lib/lp/scripts/garbo.py (+78/-1)
lib/lp/scripts/tests/test_garbo.py (+55/-0)
To merge this branch: bzr merge lp:~stevenk/launchpad/set-spph-packageupload
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+108513@code.launchpad.net

Commit message

Write a garbo job to try and set SourcePackagePublishingHistory.packageupload for existing publications.

Description of the change

Write a garbo job to try and set SourcePackagePublishingHistory.packageupload for existing publications. Since every publication isn't going to have a packageupload, we make sure to sort by id and use memcache to keep track of where we are up to. Since a publication may have multiple packageuploads related to a source (due to delayed copies or other close madness), we make sure the archive, distroseries, pocket, component and section all match.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

Needs an index like sourcepackagepublishinghistory(id) WHERE packageupload IS NULL, but otherwise fine.

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/scripts/garbo.py'
2--- lib/lp/scripts/garbo.py 2012-05-24 05:43:49 +0000
3+++ lib/lp/scripts/garbo.py 2012-06-04 02:49:19 +0000
4@@ -26,8 +26,13 @@
5 import iso8601
6 from psycopg2 import IntegrityError
7 import pytz
8-from storm.expr import In
9+from storm.expr import (
10+ And,
11+ In,
12+ Select,
13+ )
14 from storm.locals import (
15+ ClassAlias,
16 Max,
17 Min,
18 SQL,
19@@ -98,6 +103,12 @@
20 MAIN_STORE,
21 MASTER_FLAVOR,
22 )
23+from lp.soyuz.model.publishing import SourcePackagePublishingHistory
24+from lp.soyuz.model.queue import (
25+ PackageUpload,
26+ PackageUploadSource,
27+ )
28+from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
29 from lp.translations.interfaces.potemplate import IPOTemplateSet
30 from lp.translations.model.potmsgset import POTMsgSet
31 from lp.translations.model.potranslation import POTranslation
32@@ -1137,6 +1148,71 @@
33 transaction.commit()
34
35
36+class PopulateSourcePackagePublishingHistoryPackageUpload(TunableLoop):
37+
38+ maximum_chunk_size = 5000
39+
40+ def __init__(self, log, abort_time=None):
41+ super(
42+ PopulateSourcePackagePublishingHistoryPackageUpload,
43+ self).__init__(log, abort_time)
44+ self.store = IMasterStore(SourcePackagePublishingHistory)
45+ self.memcache_key = '%s:populate-spph-pu' % config.instance_name
46+ watermark = getUtility(IMemcacheClient).get(self.memcache_key)
47+ self.start_at = watermark or 0
48+
49+ def findSPPHs(self):
50+ return self.store.find(
51+ SourcePackagePublishingHistory.id,
52+ SourcePackagePublishingHistory.packageuploadID == None,
53+ SourcePackagePublishingHistory.id >= self.start_at).order_by(
54+ SourcePackagePublishingHistory.id)
55+
56+ def isDone(self):
57+ return self.findSPPHs().is_empty()
58+
59+ def __call__(self, chunk_size):
60+ ids = [spph_id for spph_id in self.findSPPHs()[:chunk_size]]
61+ matching_spph = ClassAlias(SourcePackagePublishingHistory)
62+ packageuploads = self.store.find(
63+ (SourcePackagePublishingHistory, PackageUpload),
64+ SourcePackagePublishingHistory.sourcepackagereleaseID ==
65+ PackageUploadSource.sourcepackagereleaseID,
66+ SourcePackagePublishingHistory.sourcepackagereleaseID ==
67+ SourcePackageRelease.id,
68+ PackageUploadSource.packageuploadID == PackageUpload.id,
69+ # If the changesfile is not None, it is an original upload
70+ # (IE. not a delayed copy, or package copy job.)
71+ PackageUpload.changesfile != None,
72+ # Make sure we grab the minimum SPPH, just in case the
73+ # publication has had overrides changed.
74+ SourcePackagePublishingHistory.id == Select(
75+ Min(matching_spph.id), tables=matching_spph, where=And(
76+ SourcePackagePublishingHistory.sourcepackagereleaseID ==
77+ matching_spph.sourcepackagereleaseID,
78+ SourcePackagePublishingHistory.archiveID ==
79+ matching_spph.archiveID)),
80+ SourcePackagePublishingHistory.archiveID ==
81+ PackageUpload.archiveID,
82+ SourcePackagePublishingHistory.distroseriesID ==
83+ PackageUpload.distroseriesID,
84+ SourcePackagePublishingHistory.pocket == PackageUpload.pocket,
85+ SourcePackagePublishingHistory.componentID ==
86+ SourcePackageRelease.componentID,
87+ SourcePackagePublishingHistory.sectionID ==
88+ SourcePackageRelease.sectionID,
89+ SourcePackagePublishingHistory.id.is_in(ids))
90+ for (spph, pu) in packageuploads:
91+ if pu is not None:
92+ spph.packageupload = pu
93+ self.start_at = ids[-1] + 1
94+ result = getUtility(IMemcacheClient).set(
95+ self.memcache_key, self.start_at)
96+ if not result:
97+ self.log.warning('Failed to set start_at in memcache.')
98+ transaction.commit()
99+
100+
101 class BaseDatabaseGarbageCollector(LaunchpadCronScript):
102 """Abstract base class to run a collection of TunableLoops."""
103 script_name = None # Script name for locking and database user. Override.
104@@ -1392,6 +1468,7 @@
105 DuplicateSessionPruner,
106 BugHeatUpdater,
107 BugTaskFlattener,
108+ PopulateSourcePackagePublishingHistoryPackageUpload,
109 ]
110 experimental_tunable_loops = []
111
112
113=== modified file 'lib/lp/scripts/tests/test_garbo.py'
114--- lib/lp/scripts/tests/test_garbo.py 2012-05-09 01:35:41 +0000
115+++ lib/lp/scripts/tests/test_garbo.py 2012-06-04 02:49:19 +0000
116@@ -1149,6 +1149,61 @@
117 self.runHourly()
118 self.assertEqual((task.id,), get_flat())
119
120+ def test_PopulateSPPHPU_no_pu(self):
121+ # PopulateSourcePackagePublishingHistoryPackageUpload correctly
122+ # handles a SPPH with no packageupload.
123+ with dbuser('testadmin'):
124+ spph = self.factory.makeSourcePackagePublishingHistory()
125+ self.runHourly()
126+ with dbuser('testadmin'):
127+ self.assertIs(None, spph.packageupload)
128+
129+ def createSPPHwithPU(self):
130+ distroseries = self.factory.makeDistroSeries()
131+ spph = self.factory.makeSourcePackagePublishingHistory(
132+ distroseries=distroseries, archive=distroseries.main_archive,
133+ pocket=self.factory.getAnyPocket())
134+ pu = self.factory.makePackageUpload(
135+ distroseries=distroseries, archive=distroseries.main_archive,
136+ pocket=self.factory.getAnyPocket())
137+ pu.addSource(spph.sourcepackagerelease)
138+ self.assertIs(None, spph.packageupload)
139+ return (spph, pu)
140+
141+ def test_PopulateSourcePackagePublishingHistoryPackageUpload(self):
142+ # PopulateSourcePackagePublishingHistoryPackageUpload handles a
143+ # SPPH with a matching packageupload.
144+ with dbuser('testadmin'):
145+ (spph, pu) = self.createSPPHwithPU()
146+ self.runHourly()
147+ with dbuser('testadmin'):
148+ self.assertEqual(pu, spph.packageupload)
149+
150+ def test_PopulateSPPHPU_no_changesfile(self):
151+ # PopulateSourcePackagePublishingHistoryPackageUpload will not
152+ # set a SPPH's packageupload to a PU that is not the original upload.
153+ with dbuser('testadmin'):
154+ (spph, pu) = self.createSPPHwithPU()
155+ pu.changesfile = None
156+ self.runHourly()
157+ with dbuser('testadmin'):
158+ self.assertIs(None, spph.packageupload)
159+
160+ def test_PopulateSPPHPU_multiple_publications(self):
161+ # If there are multiple publications (due to overrides changing),
162+ # PopulateSourcePackagePublishingHistoryPackageUpload will set the
163+ # correct publication's packageupload.
164+ with dbuser('testadmin'):
165+ (spph, pu) = self.createSPPHwithPU()
166+ cs = self.factory.makeComponentSelection(
167+ distroseries=spph.distroseries)
168+ overridden_spph = spph.changeOverride(cs.component)
169+ self.assertIs(None, overridden_spph.packageupload)
170+ self.runHourly()
171+ with dbuser('testadmin'):
172+ self.assertEqual(pu, spph.packageupload)
173+ self.assertIs(None, overridden_spph.packageupload)
174+
175
176 class TestGarboTasks(TestCaseWithFactory):
177 layer = LaunchpadZopelessLayer