Merge lp:~wgrant/launchpad/bugtaskflat-garbo into lp:launchpad

Proposed by William Grant
Status: Merged
Approved by: William Grant
Approved revision: no longer in the source branch.
Merged at revision: 15055
Proposed branch: lp:~wgrant/launchpad/bugtaskflat-garbo
Merge into: lp:launchpad
Prerequisite: lp:launchpad/db-devel
Diff against target: 116 lines (+67/-0)
2 files modified
lib/lp/scripts/garbo.py (+37/-0)
lib/lp/scripts/tests/test_garbo.py (+30/-0)
To merge this branch: bzr merge lp:~wgrant/launchpad/bugtaskflat-garbo
Reviewer Review Type Date Requested Status
Steve Kowalik (community) code Approve
Review via email: mp+100363@code.launchpad.net

Commit message

Add a garbo job to populate BugTaskFlat.

Description of the change

BugTaskFlat will soon exist on production, but it needs to be populated. This is a garbo job to do that, pretty much the same as the bug legacy access mirrorer that I removed a couple of weeks back.

To post a comment you must log in.
Revision history for this message
Steve Kowalik (stevenk) wrote :

Looks good.

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-03-26 22:47:47 +0000
3+++ lib/lp/scripts/garbo.py 2012-04-02 07:16:34 +0000
4@@ -44,6 +44,7 @@
5 from lp.bugs.model.bug import Bug
6 from lp.bugs.model.bugattachment import BugAttachment
7 from lp.bugs.model.bugnotification import BugNotification
8+from lp.bugs.model.bugtask import BugTask
9 from lp.bugs.model.bugwatch import BugWatchActivity
10 from lp.bugs.scripts.checkwatches.scheduler import (
11 BugWatchScheduler,
12@@ -81,6 +82,7 @@
13 from lp.services.librarian.model import TimeLimitedToken
14 from lp.services.log.logger import PrefixFilter
15 from lp.services.looptuner import TunableLoop
16+from lp.services.memcache.interfaces import IMemcacheClient
17 from lp.services.oauth.model import OAuthNonce
18 from lp.services.openid.model.openidconsumer import OpenIDConsumerNonce
19 from lp.services.propertycache import cachedproperty
20@@ -1091,6 +1093,40 @@
21 self.offset += chunk_size
22
23
24+class BugTaskFlattener(TunableLoop):
25+ """A `TunableLoop` to populate BugTaskFlat for all bugtasks."""
26+
27+ maximum_chunk_size = 5000
28+
29+ def __init__(self, log, abort_time=None):
30+ super(BugTaskFlattener, self).__init__(log, abort_time)
31+ watermark = getUtility(IMemcacheClient).get(
32+ '%s:bugtask-flattener' % config.instance_name)
33+ self.start_at = watermark or 0
34+
35+ def findTaskIDs(self):
36+ return IMasterStore(BugTask).find(
37+ (BugTask.id,), BugTask.id >= self.start_at).order_by(BugTask.id)
38+
39+ def isDone(self):
40+ return self.findTaskIDs().is_empty()
41+
42+ def __call__(self, chunk_size):
43+ ids = [row[0] for row in self.findTaskIDs()[:chunk_size]]
44+ list(IMasterStore(BugTask).using(BugTask).find(
45+ SQL('bugtask_flatten(BugTask.id, false)'),
46+ BugTask.id.is_in(ids)))
47+
48+ self.start_at = ids[-1] + 1
49+ result = getUtility(IMemcacheClient).set(
50+ '%s:bugtask-flattener' % config.instance_name,
51+ self.start_at)
52+ if not result:
53+ self.log.warning('Failed to set start_at in memcache.')
54+
55+ transaction.commit()
56+
57+
58 class BaseDatabaseGarbageCollector(LaunchpadCronScript):
59 """Abstract base class to run a collection of TunableLoops."""
60 script_name = None # Script name for locking and database user. Override.
61@@ -1342,6 +1378,7 @@
62 UnusedSessionPruner,
63 DuplicateSessionPruner,
64 BugHeatUpdater,
65+ BugTaskFlattener,
66 ]
67 experimental_tunable_loops = []
68
69
70=== modified file 'lib/lp/scripts/tests/test_garbo.py'
71--- lib/lp/scripts/tests/test_garbo.py 2012-03-26 22:47:47 +0000
72+++ lib/lp/scripts/tests/test_garbo.py 2012-04-02 07:16:34 +0000
73@@ -42,6 +42,7 @@
74 BugNotification,
75 BugNotificationRecipient,
76 )
77+from lp.bugs.model.bugtask import BugTask
78 from lp.code.bzr import (
79 BranchFormat,
80 RepositoryFormat,
81@@ -1104,6 +1105,35 @@
82 self.assertEqual(whiteboard, spec.whiteboard)
83 self.assertEqual(0, spec.work_items.count())
84
85+ def test_BugTaskFlattener(self):
86+ # Private bugs without corresponding data in the access policy
87+ # schema get mirrored.
88+ switch_dbuser('testadmin')
89+ task = self.factory.makeBugTask()
90+ # Remove the existing mirrored data.
91+ IMasterStore(BugTask).execute(
92+ 'DELETE FROM BugTaskFlat WHERE bugtask = ?', (task.id,))
93+ transaction.commit()
94+ self.runHourly()
95+ # Check that there's a record again, and delete it.
96+ switch_dbuser('testadmin')
97+ self.assertEqual(
98+ (task.id,),
99+ IMasterStore(BugTask).execute(
100+ 'SELECT bugtask FROM BugTaskFlat WHERE bugtask = ?',
101+ (task.id,)).get_one())
102+ IMasterStore(BugTask).execute(
103+ 'DELETE FROM BugTaskFlat WHERE bugtask = ?', (task.id,))
104+ transaction.commit()
105+ self.runHourly()
106+ # A watermark is kept in memcache, so a second run doesn't
107+ # consider the same task.
108+ self.assertIs(
109+ None,
110+ IMasterStore(BugTask).execute(
111+ 'SELECT bugtask FROM BugTaskFlat WHERE bugtask = ?',
112+ (task.id,)).get_one())
113+
114
115 class TestGarboTasks(TestCaseWithFactory):
116 layer = LaunchpadZopelessLayer