Colin Watson has proposed merging
lp:~cjwatson/launchpad/garbo-archivepermission-duplicates into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1025441 in Launchpad itself: "Please clean up the duplicates from
archivepermission"
https://bugs.launchpad.net/launchpad/+bug/1025441
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/garbo-archivepermission-duplicates/+merge/115554
== Summary ==
There are some duplicated rows in ArchivePermission left over from an old
(fixed) bug. These get in the way because Archive.removePackagesetUploader
uses .one().
== Proposed fix ==
Add a BulkPruner-based garbo job to get rid of the duplicates. Previous
analysis showed that the only problems have non-NULL packageset and duplicate
person, archive, packageset, permission, so we'll get rid of all but the first
id for all such groups.
== LOC Rationale ==
+99, but it will all be removed again once the job has completed.
== Tests ==
bin/test -vvct test_DuplicateArchivePermissionPruner
== Demo and Q/A ==
dogfood's database is already in an appropriately broken state. Dump the
archivepermission table, run the garbo job, and dump it again, and then verify
that all the removed IDs (there should be 190, I believe) correspond to
duplicates and that exactly one of each duplicated group remains.
--
https://code.launchpad.net/~cjwatson/launchpad/garbo-archivepermission-duplicates/+merge/115554
Your team Launchpad code reviewers is requested to review the proposed merge of
lp:~cjwatson/launchpad/garbo-archivepermission-duplicates into lp:launchpad.
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2012-07-17 06:21:47 +0000
+++ database/schema/security.cfg 2012-07-18 13:53:21 +0000
@@ -2201,6 +2201,7 @@
[garbo]
groups=script,read
public.account = SELECT, DELETE
+public.archivepermission = SELECT, DELETE
public.answercontact = SELECT, DELETE
public.branch = SELECT, UPDATE
public.branchjob = SELECT, DELETE
=== modified file 'lib/lp/scripts/garbo.py'
--- lib/lp/scripts/garbo.py 2012-07-10 06:57:47 +0000
+++ lib/lp/scripts/garbo.py 2012-07-18 13:53:21 +0000
@@ -93,6 +93,7 @@
MAIN_STORE,
MASTER_FLAVOR,
)
+from lp.soyuz.model.archivepermission import ArchivePermission
from lp.translations.interfaces.potemplate import IPOTemplateSet
from lp.translations.model.potmsgset import POTMsgSet
from lp.translations.model.potranslation import POTranslation
@@ -990,6 +991,23 @@
transaction.commit()
+class DuplicateArchivePermissionPruner(BulkPruner):
+ """Cleans up duplicate ArchivePermission rows created by bug 887185."""
+ target_table_class = ArchivePermission
+ ids_to_prune_query = """
+ SELECT id FROM (
+ SELECT id, rank() OVER w AS rank
+ FROM ArchivePermission
+ WHERE packageset IS NOT NULL
+ WINDOW w AS (
+ PARTITION BY person, archive, packageset, permission
+ ORDER BY id
+ )
+ ) AS whatever
+ WHERE rank > 1
+ """
+
+
class BaseDatabaseGarbageCollector(LaunchpadCronScript):
"""Abstract base class to run a collection of TunableLoops."""
script_name = None # Script name for locking and database user. Override.
@@ -1267,6 +1285,7 @@
BugWatchActivityPruner,
CodeImportEventPruner,
CodeImportResultPruner,
+ DuplicateArchivePermissionPruner,
HWSubmissionEmailLinker,
LoginTokenPruner,
ObsoleteBugAttachmentPruner,
=== modified file 'lib/lp/scripts/tests/test_garbo.py'
--- lib/lp/scripts/tests/test_garbo.py 2012-07-10 07:08:54 +0000
+++ lib/lp/scripts/tests/test_garbo.py 2012-07-18 13:53:21 +0000
@@ -97,6 +97,9 @@
MASTER_FLAVOR,
)
from lp.services.worlddata.interfaces.language import ILanguageSet
+from lp.soyuz.enums import ArchivePermissionType
+from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet
+from lp.soyuz.model.archivepermission import ArchivePermission
from lp.testing import (
person_logged_in,
TestCase,
@@ -1016,6 +1019,82 @@
self.runHourly()
self.assertNotEqual(old_update, naked_bug.heat_last_updated)
+ def test_DuplicateArchivePermissionPruner_removes_duplicate_rows(self):
+ # DuplicateArchivePermissionPruner removes duplicated packageset
+ # permissions.
+ switch_dbuser('testadmin')
+ archive = self.factory.makeArchive()
+ person = self.factory.makePerson()
+ packageset = self.factory.makePackageset()
+ for _ in range(3):
+ ArchivePermission(
+ archive=archive, person=person, packageset=packageset,
+ permission=ArchivePermissionType.UPLOAD)
+ ap_set = getUtility(IArchivePermissionSet)
+ self.assertEqual(
+ 3, ap_set.uploadersForPackageset(archive, packageset).count())
+ self.runDaily()
+ self.assertEqual(
+ 1, ap_set.uploadersForPackageset(archive, packageset).count())
+
+ def test_DuplicateArchivePermissionPruner_skips_unique_rows(self):
+ # DuplicateArchivePermissionPruner leaves unique packageset
+ # permissions alone.
+ switch_dbuser('testadmin')
+ archive_one = self.factory.makeArchive()
+ archive_two = self.factory.makeArchive()
+ person_one = self.factory.makePerson()
+ person_two = self.factory.makePerson()
+ packageset_one = self.factory.makePackageset()
+ packageset_two = self.factory.makePackageset()
+ for archive, person, packageset in (
+ (archive_one, person_one, packageset_one),
+ (archive_two, person_one, packageset_one),
+ (archive_one, person_two, packageset_one),
+ (archive_one, person_one, packageset_two),
+ ):
+ ArchivePermission(
+ archive=archive, person=person, packageset=packageset,
+ permission=ArchivePermissionType.UPLOAD)
+ ap_set = getUtility(IArchivePermissionSet)
+ self.assertEqual(
+ 2,
+ ap_set.uploadersForPackageset(archive_one, packageset_one).count())
+ self.assertEqual(
+ 1,
+ ap_set.uploadersForPackageset(archive_two, packageset_one).count())
+ self.assertEqual(
+ 1,
+ ap_set.uploadersForPackageset(archive_one, packageset_two).count())
+ self.runDaily()
+ self.assertEqual(
+ 2,
+ ap_set.uploadersForPackageset(archive_one, packageset_one).count())
+ self.assertEqual(
+ 1,
+ ap_set.uploadersForPackageset(archive_two, packageset_one).count())
+ self.assertEqual(
+ 1,
+ ap_set.uploadersForPackageset(archive_one, packageset_two).count())
+
+ def test_DuplicateArchivePermissionPruner_skips_non_packagesets(self):
+ # DuplicateArchivePermissionPruner leaves non-packageset permissions
+ # alone.
+ switch_dbuser('testadmin')
+ archive = self.factory.makeArchive()
+ person = self.factory.makePerson()
+ component = self.factory.makeComponent()
+ for _ in range(3):
+ ArchivePermission(
+ archive=archive, person=person, component=component,
+ permission=ArchivePermissionType.UPLOAD)
+ ap_set = getUtility(IArchivePermissionSet)
+ self.assertEqual(
+ 3, ap_set.uploadersForComponent(archive, component).count())
+ self.runDaily()
+ self.assertEqual(
+ 3, ap_set.uploadersForComponent(archive, component).count())
+
class TestGarboTasks(TestCaseWithFactory):
layer = LaunchpadZopelessLayer
_______________________________________________
Mailing list: https://launchpad.net/~launchpad-reviewers
Post to : [email protected]
Unsubscribe : https://launchpad.net/~launchpad-reviewers
More help : https://help.launchpad.net/ListHelp