Merge lp:~cjwatson/launchpad/remove-ppa-builder-limits into lp:launchpad

Proposed by Colin Watson
Status: Merged
Approved by: William Grant
Approved revision: no longer in the source branch.
Merged at revision: 16758
Proposed branch: lp:~cjwatson/launchpad/remove-ppa-builder-limits
Merge into: lp:launchpad
Diff against target: 120 lines (+9/-61)
2 files modified
lib/lp/buildmaster/tests/test_builder.py (+7/-17)
lib/lp/soyuz/model/buildpackagejob.py (+2/-44)
To merge this branch: bzr merge lp:~cjwatson/launchpad/remove-ppa-builder-limits
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+183834@code.launchpad.net

Commit message

Remove 80%-of-builders limitation on builds for any given public PPA and architecture.

Description of the change

== Summary ==

Bug 666308 reports unnecessary limitations on certain types of PPAs, as well as a request for more flexibility.

== Proposed fix ==

This is an RFC, but my position is that the limitation on how many builders a given PPA may use at any one time does more harm than good at this point. Now that we have reasonably reliable build cancellation, in the rare cases of abuse or needing something to jump the queue we can always cancel something and retry it later. Other than that, all that this limitation really achieves at this point is to require us to occasionally play silly games with builders' architectures to circumvent it.

We could add more complexity to make this controllable per-PPA or per-owner, as the bug suggests, but I don't think it's worth it.

== Tests ==

bin/test -vvct buildmaster -t soyuz

== Demo and Q/A ==

I don't think we can currently QA this on dogfood since it only has a single working PPA builder at the moment; but since this is pretty much just deleting code I'm not too worried.

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/buildmaster/tests/test_builder.py'
--- lib/lp/buildmaster/tests/test_builder.py 2013-09-02 07:30:53 +0000
+++ lib/lp/buildmaster/tests/test_builder.py 2013-09-04 09:02:03 +0000
@@ -252,20 +252,11 @@
252252
253class TestFindBuildCandidatePPA(TestFindBuildCandidatePPABase):253class TestFindBuildCandidatePPA(TestFindBuildCandidatePPABase):
254254
255 def test_findBuildCandidate_first_build_started(self):255 def test_findBuildCandidate(self):
256 # A PPA cannot start a build if it would use 80% or more of the256 # joe's fourth i386 build will be the next build candidate.
257 # builders.257 next_job = removeSecurityProxy(self.builder4)._findBuildCandidate()
258 next_job = removeSecurityProxy(self.builder4)._findBuildCandidate()258 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(next_job)
259 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(next_job)259 self.assertEqual('joesppa', build.archive.name)
260 self.failIfEqual('joesppa', build.archive.name)
261
262 def test_findBuildCandidate_first_build_finished(self):
263 # When joe's first ppa build finishes, his fourth i386 build
264 # will be the next build candidate.
265 self.joe_builds[0].updateStatus(BuildStatus.FAILEDTOBUILD)
266 next_job = removeSecurityProxy(self.builder4)._findBuildCandidate()
267 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(next_job)
268 self.failUnlessEqual('joesppa', build.archive.name)
269260
270 def test_findBuildCandidate_with_disabled_archive(self):261 def test_findBuildCandidate_with_disabled_archive(self):
271 # Disabled archives should not be considered for dispatching262 # Disabled archives should not be considered for dispatching
@@ -283,11 +274,10 @@
283 ppa_joe_private = True274 ppa_joe_private = True
284275
285 def test_findBuildCandidate_for_private_ppa(self):276 def test_findBuildCandidate_for_private_ppa(self):
286 # If a ppa is private it will be able to have parallel builds277 # joe's fourth i386 build will be the next build candidate.
287 # for the one architecture.
288 next_job = removeSecurityProxy(self.builder4)._findBuildCandidate()278 next_job = removeSecurityProxy(self.builder4)._findBuildCandidate()
289 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(next_job)279 build = getUtility(IBinaryPackageBuildSet).getByQueueEntry(next_job)
290 self.failUnlessEqual('joesppa', build.archive.name)280 self.assertEqual('joesppa', build.archive.name)
291281
292 # If the source for the build is still pending, it won't be282 # If the source for the build is still pending, it won't be
293 # dispatched because the builder has to fetch the source files283 # dispatched because the builder has to fetch the source files
294284
=== modified file 'lib/lp/soyuz/model/buildpackagejob.py'
--- lib/lp/soyuz/model/buildpackagejob.py 2013-09-02 06:34:15 +0000
+++ lib/lp/soyuz/model/buildpackagejob.py 2013-09-04 09:02:03 +0000
@@ -16,17 +16,13 @@
16from zope.interface import implements16from zope.interface import implements
1717
18from lp.buildmaster.enums import BuildStatus18from lp.buildmaster.enums import BuildStatus
19from lp.buildmaster.interfaces.builder import IBuilderSet
20from lp.buildmaster.model.buildfarmjob import BuildFarmJobOld19from lp.buildmaster.model.buildfarmjob import BuildFarmJobOld
21from lp.registry.interfaces.pocket import PackagePublishingPocket20from lp.registry.interfaces.pocket import PackagePublishingPocket
22from lp.registry.interfaces.series import SeriesStatus21from lp.registry.interfaces.series import SeriesStatus
23from lp.services.database.bulk import load_related22from lp.services.database.bulk import load_related
24from lp.services.database.interfaces import IStore23from lp.services.database.interfaces import IStore
25from lp.services.database.sqlbase import sqlvalues24from lp.services.database.sqlbase import sqlvalues
26from lp.soyuz.enums import (25from lp.soyuz.enums import PackagePublishingStatus
27 ArchivePurpose,
28 PackagePublishingStatus,
29 )
30from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet26from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
31from lp.soyuz.interfaces.buildpackagejob import (27from lp.soyuz.interfaces.buildpackagejob import (
32 COPY_ARCHIVE_SCORE_PENALTY,28 COPY_ARCHIVE_SCORE_PENALTY,
@@ -129,7 +125,7 @@
129 PackagePublishingStatus.SUPERSEDED,125 PackagePublishingStatus.SUPERSEDED,
130 PackagePublishingStatus.DELETED,126 PackagePublishingStatus.DELETED,
131 )127 )
132 sub_query = """128 return """
133 SELECT TRUE FROM Archive, BinaryPackageBuild, BuildPackageJob,129 SELECT TRUE FROM Archive, BinaryPackageBuild, BuildPackageJob,
134 DistroArchSeries130 DistroArchSeries
135 WHERE131 WHERE
@@ -154,44 +150,6 @@
154 BinaryPackageBuild.status = %s150 BinaryPackageBuild.status = %s
155 """ % sqlvalues(private_statuses, BuildStatus.NEEDSBUILD)151 """ % sqlvalues(private_statuses, BuildStatus.NEEDSBUILD)
156152
157 # Ensure that if BUILDING builds exist for the same
158 # public ppa archive and architecture and another would not
159 # leave at least 20% of them free, then we don't consider
160 # another as a candidate.
161 #
162 # This clause selects the count of currently building builds on
163 # the arch in question, then adds one to that total before
164 # deriving a percentage of the total available builders on that
165 # arch. It then makes sure that percentage is under 80.
166 #
167 # The extra clause is only used if the number of available
168 # builders is greater than one, or nothing would get dispatched
169 # at all.
170 num_arch_builders = getUtility(IBuilderSet).getBuildersForQueue(
171 processor, virtualized).count()
172 if num_arch_builders > 1:
173 sub_query += """
174 AND Archive.id NOT IN (
175 SELECT Archive.id
176 FROM Archive, BinaryPackageBuild, DistroArchSeries
177 WHERE
178 BinaryPackageBuild.distro_arch_series = DistroArchSeries.id
179 AND DistroArchSeries.processorfamily = %s
180 AND BinaryPackageBuild.status = %s
181 AND BinaryPackageBuild.archive = Archive.id
182 AND Archive.purpose = %s
183 AND Archive.private IS FALSE
184 GROUP BY Archive.id
185 HAVING (
186 (count(*)+1) * 100.0 / %s
187 ) >= 80
188 )
189 """ % sqlvalues(
190 processor.family, BuildStatus.BUILDING,
191 ArchivePurpose.PPA, num_arch_builders)
192
193 return sub_query
194
195 @staticmethod153 @staticmethod
196 def postprocessCandidate(job, logger):154 def postprocessCandidate(job, logger):
197 """See `IBuildFarmJob`."""155 """See `IBuildFarmJob`."""