launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #06731
[Merge] lp:~wgrant/launchpad/garbo-up-legacy-access into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/garbo-up-legacy-access into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #956707 in Launchpad itself: "No way to migrate existing bugs to the new access model"
https://bugs.launchpad.net/launchpad/+bug/956707
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/garbo-up-legacy-access/+merge/97798
This branch adds a one-off garbo job to mirror existing bugs to the new access model. There are triggers to cope with changes, but previously nothing to migrate existing untouched data.
Since it's difficult and slow to check if a bug has already been mirrored, and this only needs to be done once for each bug, I make a single pass over the bugs by ID, storing a watermark in memcached after each batch. It's not problematic if it gets forgotten, just a bit of a performance hit.
--
https://code.launchpad.net/~wgrant/launchpad/garbo-up-legacy-access/+merge/97798
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/garbo-up-legacy-access into lp:launchpad.
=== modified file 'lib/lp/registry/model/accesspolicy.py'
--- lib/lp/registry/model/accesspolicy.py 2012-03-15 08:54:40 +0000
+++ lib/lp/registry/model/accesspolicy.py 2012-03-16 05:52:21 +0000
@@ -32,6 +32,7 @@
IAccessArtifactGrantSource,
IAccessPolicy,
IAccessPolicyArtifact,
+ IAccessPolicyArtifactSource,
IAccessPolicyGrant,
)
from lp.registry.model.person import Person
@@ -113,6 +114,7 @@
return
ids = [abstract.id for abstract in abstracts]
getUtility(IAccessArtifactGrantSource).revokeByArtifact(abstracts)
+ getUtility(IAccessPolicyArtifactSource).deleteByArtifact(abstracts)
IStore(abstract).find(cls, cls.id.is_in(ids)).remove()
=== modified file 'lib/lp/scripts/garbo.py'
--- lib/lp/scripts/garbo.py 2012-03-16 00:43:22 +0000
+++ lib/lp/scripts/garbo.py 2012-03-16 05:52:21 +0000
@@ -81,6 +81,7 @@
from lp.services.librarian.model import TimeLimitedToken
from lp.services.log.logger import PrefixFilter
from lp.services.looptuner import TunableLoop
+from lp.services.memcache.interfaces import IMemcacheClient
from lp.services.oauth.model import OAuthNonce
from lp.services.openid.model.openidconsumer import OpenIDConsumerNonce
from lp.services.propertycache import cachedproperty
@@ -1113,6 +1114,40 @@
self.transaction.commit()
+class BugLegacyAccessMirrorer(TunableLoop):
+ """A `TunableLoop` to populate the access policy schema for all bugs."""
+
+ maximum_chunk_size = 5000
+
+ def __init__(self, log, abort_time=None):
+ super(BugLegacyAccessMirrorer, self).__init__(log, abort_time)
+ watermark = getUtility(IMemcacheClient).get(
+ '%s:bug-legacy-access-mirrorer' % config.instance_name)
+ self.start_at = watermark or 0
+
+ def findBugIDs(self):
+ return IMasterStore(Bug).find(
+ (Bug.id,), Bug.id >= self.start_at).order_by(Bug.id)
+
+ def isDone(self):
+ return self.findBugIDs().is_empty()
+
+ def __call__(self, chunk_size):
+ ids = [row[0] for row in self.findBugIDs()[:chunk_size]]
+ list(IMasterStore(Bug).using(Bug).find(
+ SQL('bug_mirror_legacy_access(Bug.id)'),
+ Bug.id.is_in(ids)))
+
+ self.start_at = ids[-1] + 1
+ result = getUtility(IMemcacheClient).set(
+ '%s:bug-legacy-access-mirrorer' % config.instance_name,
+ self.start_at)
+ if not result:
+ self.log.warning('Failed to set start_at in memcache.')
+
+ transaction.commit()
+
+
class BaseDatabaseGarbageCollector(LaunchpadCronScript):
"""Abstract base class to run a collection of TunableLoops."""
script_name = None # Script name for locking and database user. Override.
@@ -1365,6 +1400,7 @@
DuplicateSessionPruner,
BugHeatUpdater,
BugsInformationTypeMigrator,
+ BugLegacyAccessMirrorer,
]
experimental_tunable_loops = []
=== modified file 'lib/lp/scripts/tests/test_garbo.py'
--- lib/lp/scripts/tests/test_garbo.py 2012-03-16 00:43:22 +0000
+++ lib/lp/scripts/tests/test_garbo.py 2012-03-16 05:52:21 +0000
@@ -55,6 +55,7 @@
from lp.code.model.codeimportevent import CodeImportEvent
from lp.code.model.codeimportresult import CodeImportResult
from lp.registry.enums import InformationType
+from lp.registry.interfaces.accesspolicy import IAccessArtifactSource
from lp.registry.interfaces.distribution import IDistributionSet
from lp.registry.interfaces.person import IPersonSet
from lp.scripts.garbo import (
@@ -1115,6 +1116,26 @@
self.runHourly()
self.assertEqual(InformationType.USERDATA, bug.information_type)
+ def test_BugLegacyAccessMirrorer(self):
+ # Private bugs without corresponding data in the access policy
+ # schema get mirrored.
+ switch_dbuser('testadmin')
+ bug = self.factory.makeBug(private=True)
+ # Remove the existing mirrored data.
+ getUtility(IAccessArtifactSource).delete([bug])
+ transaction.commit()
+ self.runHourly()
+ # Check that there's an artifact again, and delete it.
+ switch_dbuser('testadmin')
+ [artifact] = getUtility(IAccessArtifactSource).find([bug])
+ getUtility(IAccessArtifactSource).delete([bug])
+ transaction.commit()
+ self.runHourly()
+ # A watermark is kept in memcache, so a second run doesn't
+ # consider the same bug.
+ self.assertContentEqual(
+ [], getUtility(IAccessArtifactSource).find([bug]))
+
class TestGarboTasks(TestCaseWithFactory):
layer = LaunchpadZopelessLayer