Merge ~twom/launchpad:stats-code-import-stats into launchpad:master

Proposed by Tom Wardill
Status: Merged
Approved by: Tom Wardill
Approved revision: bb6163ca342dfad7fc29cc7cd4ac4feb5a91d73e
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~twom/launchpad:stats-code-import-stats
Merge into: launchpad:master
Diff against target: 160 lines (+91/-0)
2 files modified
lib/lp/services/statsd/numbercruncher.py (+27/-0)
lib/lp/services/statsd/tests/test_numbercruncher.py (+64/-0)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+397427@code.launchpad.net

Commit message

Add CodeImportJob stats to numbercruncher

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/lp/services/statsd/numbercruncher.py b/lib/lp/services/statsd/numbercruncher.py
index 3f1a6e1..b143927 100644
--- a/lib/lp/services/statsd/numbercruncher.py
+++ b/lib/lp/services/statsd/numbercruncher.py
@@ -8,8 +8,10 @@ from __future__ import absolute_import, print_function, unicode_literals
8__metaclass__ = type8__metaclass__ = type
9__all__ = ['NumberCruncher']9__all__ = ['NumberCruncher']
1010
11from datetime import datetime
11import logging12import logging
1213
14import pytz
13from storm.expr import (15from storm.expr import (
14 Count,16 Count,
15 Sum,17 Sum,
@@ -27,6 +29,8 @@ from zope.component import getUtility
27from lp.buildmaster.enums import BuilderCleanStatus29from lp.buildmaster.enums import BuilderCleanStatus
28from lp.buildmaster.interfaces.builder import IBuilderSet30from lp.buildmaster.interfaces.builder import IBuilderSet
29from lp.buildmaster.manager import PrefetchedBuilderFactory31from lp.buildmaster.manager import PrefetchedBuilderFactory
32from lp.code.enums import CodeImportJobState
33from lp.code.model.codeimportjob import CodeImportJob
30from lp.services.database.interfaces import IStore34from lp.services.database.interfaces import IStore
31from lp.services.librarian.model import LibraryFileContent35from lp.services.librarian.model import LibraryFileContent
32from lp.services.statsd.interfaces.statsd_client import IStatsdClient36from lp.services.statsd.interfaces.statsd_client import IStatsdClient
@@ -40,6 +44,7 @@ class NumberCruncher(service.Service):
40 QUEUE_INTERVAL = 6044 QUEUE_INTERVAL = 60
41 BUILDER_INTERVAL = 6045 BUILDER_INTERVAL = 60
42 LIBRARIAN_INTERVAL = 360046 LIBRARIAN_INTERVAL = 3600
47 CODE_IMPORT_INTERVAL = 60
4348
44 def __init__(self, clock=None, builder_factory=None):49 def __init__(self, clock=None, builder_factory=None):
45 if clock is None:50 if clock is None:
@@ -155,6 +160,27 @@ class NumberCruncher(service.Service):
155 self.logger.exception("Failure while updating librarian stats:")160 self.logger.exception("Failure while updating librarian stats:")
156 transaction.abort()161 transaction.abort()
157162
163 def updateCodeImportStats(self):
164 """Update stats about code imports.
165
166 This aborts the current transaction before returning.
167 """
168 try:
169 self.logger.debug("Update code import stats.")
170 store = IStore(CodeImportJob)
171 pending = store.find(
172 CodeImportJob,
173 CodeImportJob.state == CodeImportJobState.PENDING).count()
174 overdue = store.find(
175 CodeImportJob,
176 CodeImportJob.date_due < datetime.now(pytz.UTC)).count()
177 self._sendGauge("codeimport.pending", pending)
178 self._sendGauge("codeimport.overdue", overdue)
179 self.logger.debug("Code import stats update complete")
180 except Exception:
181 self.logger.exception("Failure while updating code import stats.")
182 transaction.abort()
183
158 def startService(self):184 def startService(self):
159 self.logger.info("Starting number-cruncher service.")185 self.logger.info("Starting number-cruncher service.")
160 self.loops = []186 self.loops = []
@@ -163,6 +189,7 @@ class NumberCruncher(service.Service):
163 (self.QUEUE_INTERVAL, self.updateBuilderQueues),189 (self.QUEUE_INTERVAL, self.updateBuilderQueues),
164 (self.BUILDER_INTERVAL, self.updateBuilderStats),190 (self.BUILDER_INTERVAL, self.updateBuilderStats),
165 (self.LIBRARIAN_INTERVAL, self.updateLibrarianStats),191 (self.LIBRARIAN_INTERVAL, self.updateLibrarianStats),
192 (self.CODE_IMPORT_INTERVAL, self.updateCodeImportStats),
166 ):193 ):
167 loop, stopping_deferred = self._startLoop(interval, callback)194 loop, stopping_deferred = self._startLoop(interval, callback)
168 self.loops.append(loop)195 self.loops.append(loop)
diff --git a/lib/lp/services/statsd/tests/test_numbercruncher.py b/lib/lp/services/statsd/tests/test_numbercruncher.py
index 41b6bd4..f2ea44e 100644
--- a/lib/lp/services/statsd/tests/test_numbercruncher.py
+++ b/lib/lp/services/statsd/tests/test_numbercruncher.py
@@ -17,12 +17,14 @@ from testtools.twistedsupport import AsynchronousDeferredRunTest
17import transaction17import transaction
18from twisted.internet import task18from twisted.internet import task
19from zope.component import getUtility19from zope.component import getUtility
20from zope.security.proxy import removeSecurityProxy
2021
21from lp.buildmaster.enums import BuilderCleanStatus22from lp.buildmaster.enums import BuilderCleanStatus
22from lp.buildmaster.interactor import BuilderSlave23from lp.buildmaster.interactor import BuilderSlave
23from lp.buildmaster.interfaces.processor import IProcessorSet24from lp.buildmaster.interfaces.processor import IProcessorSet
24from lp.buildmaster.model.buildqueue import BuildQueue25from lp.buildmaster.model.buildqueue import BuildQueue
25from lp.buildmaster.tests.mock_slaves import OkSlave26from lp.buildmaster.tests.mock_slaves import OkSlave
27from lp.code.enums import CodeImportJobState
26from lp.services.database.isolation import is_transaction_in_progress28from lp.services.database.isolation import is_transaction_in_progress
27from lp.services.database.policy import DatabaseBlockedPolicy29from lp.services.database.policy import DatabaseBlockedPolicy
28from lp.services.log.logger import BufferLogger30from lp.services.log.logger import BufferLogger
@@ -232,6 +234,57 @@ class TestNumberCruncher(StatsMixin, TestCaseWithFactory):
232 "Failure while updating librarian stats:",234 "Failure while updating librarian stats:",
233 cruncher.logger.getLogBuffer())235 cruncher.logger.getLogBuffer())
234236
237 def test_updateCodeImportStats(self):
238 clock = task.Clock()
239 cruncher = NumberCruncher(clock=clock)
240 cruncher.updateCodeImportStats()
241
242 self.assertFalse(is_transaction_in_progress())
243 self.assertEqual(2, self.stats_client.gauge.call_count)
244 self.assertThat(
245 [x[0] for x in self.stats_client.gauge.call_args_list],
246 MatchesListwise([
247 MatchesListwise([
248 Equals('codeimport.pending,env=test'),
249 Equals(1),
250 ]),
251 MatchesListwise([
252 Equals('codeimport.overdue,env=test'),
253 Equals(1),
254 ]),
255 ]))
256
257 job = removeSecurityProxy(self.factory.makeCodeImportJob())
258 job.state = CodeImportJobState.PENDING
259 self.stats_client.gauge.reset_mock()
260 cruncher.updateCodeImportStats()
261
262 self.assertEqual(2, self.stats_client.gauge.call_count)
263 self.assertThat(
264 [x[0] for x in self.stats_client.gauge.call_args_list],
265 MatchesListwise([
266 MatchesListwise([
267 Equals('codeimport.pending,env=test'),
268 Equals(2),
269 ]),
270 MatchesListwise([
271 Equals('codeimport.overdue,env=test'),
272 Equals(2),
273 ]),
274 ]))
275
276 def test_updateCodeImportStats_error(self):
277 clock = task.Clock()
278 cruncher = NumberCruncher(clock=clock)
279 cruncher.logger = BufferLogger()
280 with DatabaseBlockedPolicy():
281 cruncher.updateCodeImportStats()
282
283 self.assertFalse(is_transaction_in_progress())
284 self.assertIn(
285 "Failure while updating code import stats.",
286 cruncher.logger.getLogBuffer())
287
235 def test_startService_starts_update_queues_loop(self):288 def test_startService_starts_update_queues_loop(self):
236 clock = task.Clock()289 clock = task.Clock()
237 cruncher = NumberCruncher(clock=clock)290 cruncher = NumberCruncher(clock=clock)
@@ -264,3 +317,14 @@ class TestNumberCruncher(StatsMixin, TestCaseWithFactory):
264 advance = NumberCruncher.LIBRARIAN_INTERVAL + 1317 advance = NumberCruncher.LIBRARIAN_INTERVAL + 1
265 clock.advance(advance)318 clock.advance(advance)
266 self.assertNotEqual(0, cruncher.updateLibrarianStats.call_count)319 self.assertNotEqual(0, cruncher.updateLibrarianStats.call_count)
320
321 def test_startService_starts_update_code_import_loop(self):
322 clock = task.Clock()
323 cruncher = NumberCruncher(clock=clock)
324
325 cruncher.updateCodeImportStats = FakeMethod()
326
327 cruncher.startService()
328 advance = NumberCruncher.CODE_IMPORT_INTERVAL + 1
329 clock.advance(advance)
330 self.assertNotEqual(0, cruncher.updateCodeImportStats.call_count)

Subscribers

People subscribed via source and target branches

to status/vote changes: