Colin Watson has proposed merging lp:~cjwatson/launchpad/pcj-queue-ordering 
into lp:launchpad.

Commit message:
Prioritise mass-sync copies below others, and allow others to jump the queue.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #1064274 in Launchpad itself: "PCJ runner has no prioritisation 
facilities"
  https://bugs.launchpad.net/launchpad/+bug/1064274

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/pcj-queue-ordering/+merge/130108

== Summary ==

The PackageCopyJob queue is strictly first-come-first-served.  Package-copy 
requests can be starved for hours by a long mass-sync queue, of the kind that 
occurs when auto-syncing packages from Debian into Ubuntu at the start of an 
Ubuntu release cycle.

== Proposed fix ==

We went back and forth on this quite a bit on IRC (initially trying to increase 
capacity with more runners, only to find that all the obvious paths to that are 
blocked for one reason or another) and agreed that a sensible approach would be 
to allow single copies to pre-empt mass copies.  We generally don't care very 
much about the response time to mass copies, but people copying individual 
packages between PPAs expect something close to an interactive response time.

== Pre-implementation notes ==

Simply ordering the queue by copy_policy is the obvious approach, but it 
occurred to me that we also need to run the query more than once or else it'll 
simply pick up 3000 mass-sync copies right at the start and we have the same 
problem.  William Grant suggested that it would probably be reasonable to just 
select the first job each time round the loop, so I've done that.  Technically 
I suppose this has something like O(n^2 log n) complexity, but the numbers 
aren't such that I would expect this to be a serious problem in practice and at 
the moment the query step at the start of a PCJ run doesn't take enough time to 
show up in logs; if we turn out to have a problem then we could mitigate it by 
working in batches of (say) ten at a time.

== LOC Rationale ==

+40, but this should be the last substantial addition before we can get rid of 
synchronous PPA copies and delayed copies.

== Tests ==

bin/test -vvct lp.soyuz.tests.test_packagecopyjob

== Demo and Q/A ==

Copy a reasonably substantial number of packages (say, 10) from Ubuntu to a 
dogfood PPA, including binaries, using Archive.copyPackages.  Run the PCJ 
processor.  While it's running, copy another package between PPAs using the web 
UI or Archive.copyPackage.  Verify in the logs that the single copy jumps the 
queue.
-- 
https://code.launchpad.net/~cjwatson/launchpad/pcj-queue-ordering/+merge/130108
Your team Launchpad code reviewers is requested to review the proposed merge of 
lp:~cjwatson/launchpad/pcj-queue-ordering into lp:launchpad.
=== modified file 'lib/lp/soyuz/model/packagecopyjob.py'
--- lib/lp/soyuz/model/packagecopyjob.py	2012-10-04 05:50:31 +0000
+++ lib/lp/soyuz/model/packagecopyjob.py	2012-10-17 13:01:44 +0000
@@ -206,13 +206,26 @@
 
     @classmethod
     def iterReady(cls):
-        """Iterate through all ready PackageCopyJobs."""
-        jobs = IStore(PackageCopyJob).find(
-            PackageCopyJob,
-            And(PackageCopyJob.job_type == cls.class_job_type,
-                PackageCopyJob.job == Job.id,
-                Job.id.is_in(Job.ready_jobs)))
-        return (cls(job) for job in jobs)
+        """Iterate through all ready PackageCopyJobs.
+
+        Even though it's slower, we repeat the query each time in order that
+        very long queues of mass syncs can be pre-empted by other jobs.
+        """
+        seen = set()
+        while True:
+            jobs = IStore(PackageCopyJob).find(
+                PackageCopyJob,
+                And(PackageCopyJob.job_type == cls.class_job_type,
+                    PackageCopyJob.job == Job.id,
+                    Job.id.is_in(Job.ready_jobs)))
+            jobs.order_by(PackageCopyJob.copy_policy)
+            for job in jobs:
+                if job.id not in seen:
+                    seen.add(job.id)
+                    yield cls(job)
+                    break
+            else:
+                break
 
     def getOopsVars(self):
         """See `IRunnableJob`."""

=== modified file 'lib/lp/soyuz/tests/test_packagecopyjob.py'
--- lib/lp/soyuz/tests/test_packagecopyjob.py	2012-10-04 07:02:08 +0000
+++ lib/lp/soyuz/tests/test_packagecopyjob.py	2012-10-17 13:01:44 +0000
@@ -478,6 +478,33 @@
         emails = pop_notifications()
         self.assertEqual(len(emails), 1)
 
+    def test_iterReady_orders_by_copy_policy(self):
+        # iterReady prioritises mass-sync copies below anything else.
+        self.makeJob(copy_policy=PackageCopyPolicy.MASS_SYNC)
+        self.makeJob()
+        self.makeJob(copy_policy=PackageCopyPolicy.MASS_SYNC)
+        ready_jobs = list(getUtility(IPlainPackageCopyJobSource).iterReady())
+        self.assertEqual([
+            PackageCopyPolicy.INSECURE,
+            PackageCopyPolicy.MASS_SYNC,
+            PackageCopyPolicy.MASS_SYNC,
+            ], [job.copy_policy for job in ready_jobs])
+
+    def test_iterReady_preempt(self):
+        # Ordinary ("insecure") copy jobs that arrive in the middle of a
+        # long mass-sync run take precedence immediately.
+        for i in range(2):
+            self.makeJob(copy_policy=PackageCopyPolicy.MASS_SYNC)
+        iterator = getUtility(IPlainPackageCopyJobSource).iterReady()
+        self.assertEqual(
+            PackageCopyPolicy.MASS_SYNC, next(iterator).copy_policy)
+        self.makeJob()
+        self.assertEqual(
+            PackageCopyPolicy.INSECURE, next(iterator).copy_policy)
+        self.assertEqual(
+            PackageCopyPolicy.MASS_SYNC, next(iterator).copy_policy)
+        self.assertRaises(StopIteration, next, iterator)
+
     def test_getOopsVars(self):
         distroseries = self.factory.makeDistroSeries()
         archive1 = self.factory.makeArchive(distroseries.distribution)

_______________________________________________
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to     : [email protected]
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help   : https://help.launchpad.net/ListHelp

Reply via email to