1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
# Copyright 2010 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Code for the BugWatch scheduler."""
__metaclass__ = type
__all__ = [
'BugWatchScheduler',
'MAX_SAMPLE_SIZE',
]
import transaction
from canonical.database.sqlbase import sqlvalues
from canonical.launchpad.interfaces.lpstorm import IMasterStore
from canonical.launchpad.utilities.looptuner import TunableLoop
from lp.bugs.interfaces.bugwatch import BUG_WATCH_ACTIVITY_SUCCESS_STATUSES
from lp.bugs.model.bugwatch import BugWatch
# The maximum additional delay in days that a watch may have placed upon
# it.
MAX_DELAY_DAYS = 6
# The maximum number of BugWatchActivity entries we want to examine.
MAX_SAMPLE_SIZE = 5
def get_delay_coefficient(max_delay_days, max_sample_size):
return float(max_delay_days) / float(max_sample_size)
class BugWatchScheduler(TunableLoop):
"""An `ITunableLoop` for scheduling BugWatches."""
maximum_chunk_size = 1000
def __init__(self, log, abort_time=None, max_delay_days=None,
max_sample_size=None):
super(BugWatchScheduler, self).__init__(log, abort_time)
self.transaction = transaction
self.store = IMasterStore(BugWatch)
if max_delay_days is None:
max_delay_days = MAX_DELAY_DAYS
if max_sample_size is None:
max_sample_size = MAX_SAMPLE_SIZE
self.max_sample_size = max_sample_size
self.delay_coefficient = get_delay_coefficient(
max_delay_days, max_sample_size)
def __call__(self, chunk_size):
"""Run the loop."""
# XXX 2010-03-25 gmb bug=198767:
# We cast chunk_size to an integer to ensure that we're not
# trying to slice using floats or anything similarly
# foolish. We shouldn't have to do this.
chunk_size = int(chunk_size)
query = """
UPDATE BugWatch
SET next_check =
COALESCE(
lastchecked + interval '1 day',
now() AT TIME ZONE 'UTC') +
(interval '1 day' * (%s * recent_failure_count))
FROM (
SELECT bug_watch.id,
(SELECT COUNT(*)
FROM (SELECT 1
FROM bugwatchactivity
WHERE bugwatchactivity.bug_watch = bug_watch.id
AND bugwatchactivity.result NOT IN (%s)
ORDER BY bugwatchactivity.id DESC
LIMIT %s) AS recent_failures
) AS recent_failure_count
FROM BugWatch AS bug_watch
WHERE bug_watch.next_check IS NULL
LIMIT %s
) AS counts
WHERE BugWatch.id = counts.id
""" % sqlvalues(
self.delay_coefficient, BUG_WATCH_ACTIVITY_SUCCESS_STATUSES,
self.max_sample_size, chunk_size)
self.transaction.begin()
result = self.store.execute(query)
self.log.debug("Scheduled %s watches" % result.rowcount)
self.transaction.commit()
def isDone(self):
"""Return True when there are no more watches to schedule."""
return self.store.find(
BugWatch, BugWatch.next_check == None).is_empty()
|