Merge lp:~rvb/maas/nonce-bug-1190986-tasks into lp:~maas-committers/maas/trunk

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: no longer in the source branch.
Merged at revision: 1703
Proposed branch: lp:~rvb/maas/nonce-bug-1190986-tasks
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 142 lines (+88/-1)
5 files modified
etc/celeryconfig.py (+17/-1)
etc/celeryconfig_common.py (+1/-0)
services/region-worker/run (+1/-0)
src/maasserver/tasks.py (+30/-0)
src/maasserver/tests/test_tasks.py (+39/-0)
To merge this branch: bzr merge lp:~rvb/maas/nonce-bug-1190986-tasks
Reviewer Review Type Date Requested Status
Gavin Panella (community) Approve
Review via email: mp+190571@code.launchpad.net

Commit message

Enable the master tasks. Add the cleanup_old_nonces task.

Description of the change

This requires the following changes in the packaging to work https://code.launchpad.net/~rvb/maas/packaging-master-django/+merge/190614.

(I've built a package from this branch and the packaging branch mentioned above and QAed this in the lab.)

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Looks good.

[1]

+    'cleanup-old-nonces': {
+        'task': 'maasserver.tasks.cleanup_old_nonces',
+        'schedule': timedelta(minutes=5),
+        'options': {'queue': WORKER_QUEUE_REGION},
+    },

Does Celery ensure that jobs don't pile up? If a run of this job takes
more than 5 minutes (I'm thinking of the first run, maybe), will
Celery enqueue the next job, or will it be automatically cancelled?
Or... does schedule mean the time *between* runs, rather than a rigid
from-the-clock schedule?

review: Approve
Revision history for this message
Julian Edwards (julian-edwards) wrote :

AFAIK they will pile up (we have seen this happen with all other jobs when something went wrong with Rabbit. I think 5 minutes is too often to run this anyway - how about once a day?

Revision history for this message
Raphaël Badin (rvb) wrote :

> Or... does schedule mean the time *between* runs, rather than a rigid
> from-the-clock schedule?

Like Julian said, celerybeat works like cron in this respect, it only fires off tasks at regular interval, nothing more intelligent than that.

Revision history for this message
Raphaël Badin (rvb) wrote :

> AFAIK they will pile up (we have seen this happen with all other jobs when something went wrong with Rabbit.
> I think 5 minutes is too often to run this anyway - how about once a day?

fwiw, the 5 minutes interval comes from the fact that nonces are only valid for 5 minutes (oauth enforces that) and the cleanup_old_nonces utility uses that… so running this utility every 5 minutes is the best we can do (now, your might want to argue doing something different but I'm explaining why I picked up 5 minutes in the first place). Considering that a nonce is created *every time* an authenticated request is made to that API, in the context of hyperscale, I think it's worth running this as often as possible.

Also:
- running cleanup_old_nonces when there is no nonces to clean up is instantaneous (well, obviously).
- running cleanup_old_nonces when there is 6M nonces to clean up takes ~10s on a modest canonistack instance.

Revision history for this message
Gavin Panella (allenap) wrote :

Julian's point about jobs piling up is concerning. If we can ensure that jobs don't pile up (is that a per-message, per-queue or per-exchange setting?) then I think that every 5 minutes is fine. If they're going to accumulate, then once a day seems okay. We've not had *anything* for over a year, so once a day seems more than adequate.

Revision history for this message
Raphaël Badin (rvb) wrote :
Revision history for this message
Julian Edwards (julian-edwards) wrote :

On 14/10/13 18:51, Gavin Panella wrote:
> We've not had *anything* for over a year, so once a day seems more than adequate.

That was precisely my reasoning for once a day.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'etc/celeryconfig.py'
2--- etc/celeryconfig.py 2013-10-07 09:12:40 +0000
3+++ etc/celeryconfig.py 2013-10-11 11:16:45 +0000
4@@ -17,11 +17,23 @@
5
6 __metaclass__ = type
7
8+from datetime import timedelta
9+
10 import celeryconfig_common
11 from maas import import_settings
12
13+# Region worker queue. Will be overridden by the customized setting in the
14+# local MAAS Celery config.
15+WORKER_QUEUE_REGION = None
16+CELERY_IMPORTS = None
17+
18 import_settings(celeryconfig_common)
19
20+CELERY_IMPORTS = CELERY_IMPORTS + (
21+ # Master tasks.
22+ "maasserver.tasks",
23+)
24+
25 try:
26 import maas_local_celeryconfig
27 except ImportError:
28@@ -29,6 +41,10 @@
29 else:
30 import_settings(maas_local_celeryconfig)
31
32-
33 CELERYBEAT_SCHEDULE = {
34+ 'cleanup-old-nonces': {
35+ 'task': 'maasserver.tasks.cleanup_old_nonces',
36+ 'schedule': timedelta(minutes=5),
37+ 'options': {'queue': WORKER_QUEUE_REGION},
38+ },
39 }
40
41=== modified file 'etc/celeryconfig_common.py'
42--- etc/celeryconfig_common.py 2013-10-07 09:12:40 +0000
43+++ etc/celeryconfig_common.py 2013-10-11 11:16:45 +0000
44@@ -56,6 +56,7 @@
45 CELERYBEAT_SCHEDULE_FILENAME = '/var/lib/maas/celerybeat-cluster-schedule'
46
47 WORKER_QUEUE_DNS = 'celery'
48+WORKER_QUEUE_REGION = 'celery'
49
50 # Each cluster should have its own queue created automatically by Celery.
51 CELERY_CREATE_MISSING_QUEUES = True
52
53=== modified file 'services/region-worker/run'
54--- services/region-worker/run 2013-01-02 16:57:53 +0000
55+++ services/region-worker/run 2013-10-11 11:16:45 +0000
56@@ -15,6 +15,7 @@
57 [ -z "${logdir:-}" ] || exec &>> "${logdir}/current"
58
59 export PYTHONPATH=etc/:src/
60+export DJANGO_SETTINGS_MODULE=maas.demo
61 script="$(readlink -f bin/celeryd)"
62 # XXX GavinPanella 2013-01-02, bug=1040529: celeryd does not shutdown
63 # correctly when signalled: processes are often left behind. However,
64
65=== added file 'src/maasserver/tasks.py'
66--- src/maasserver/tasks.py 1970-01-01 00:00:00 +0000
67+++ src/maasserver/tasks.py 2013-10-11 11:16:45 +0000
68@@ -0,0 +1,30 @@
69+# Copyright 2013 Canonical Ltd. This software is licensed under the
70+# GNU Affero General Public License version 3 (see the file LICENSE).
71+
72+"""Maasserver tasks that are run in Celery workers."""
73+
74+from __future__ import (
75+ absolute_import,
76+ print_function,
77+ unicode_literals,
78+ )
79+
80+str = None
81+
82+__metaclass__ = type
83+__all__ = [
84+ 'cleanup_old_nonces',
85+ ]
86+
87+
88+from celery.task import task
89+from maasserver import (
90+ logger,
91+ nonces_cleanup,
92+ )
93+
94+
95+@task
96+def cleanup_old_nonces(**kwargs):
97+ nb_nonces_deleted = nonces_cleanup.cleanup_old_nonces()
98+ logger.info("%d expired nonce(s) cleaned up." % nb_nonces_deleted)
99
100=== added file 'src/maasserver/tests/test_tasks.py'
101--- src/maasserver/tests/test_tasks.py 1970-01-01 00:00:00 +0000
102+++ src/maasserver/tests/test_tasks.py 2013-10-11 11:16:45 +0000
103@@ -0,0 +1,39 @@
104+# Copyright 2013 Canonical Ltd. This software is licensed under the
105+# GNU Affero General Public License version 3 (see the file LICENSE).
106+
107+"""Tests for Celery tasks."""
108+
109+from __future__ import (
110+ absolute_import,
111+ print_function,
112+ unicode_literals,
113+ )
114+
115+str = None
116+
117+__metaclass__ = type
118+__all__ = []
119+
120+from fixtures import FakeLogger
121+from maasserver import tasks
122+from maastesting.celery import CeleryFixture
123+from maastesting.testcase import MAASTestCase
124+from testresources import FixtureResource
125+from testtools.matchers import Contains
126+
127+
128+class TestCleanupOldNonces(MAASTestCase):
129+
130+ resources = (
131+ ("celery", FixtureResource(CeleryFixture())),
132+ )
133+
134+ def test_cleanup_old_nonces_calls_cleanup_old_nonces(self):
135+ logger = self.useFixture(FakeLogger('maasserver'))
136+ nb_cleanups = 3
137+ mock = self.patch(tasks, 'nonces_cleanup')
138+ mock.cleanup_old_nonces.return_value = nb_cleanups
139+ tasks.cleanup_old_nonces()
140+ mock.cleanup_old_nonces.assert_called_once_with()
141+ message = "%d expired nonce(s) cleaned up." % nb_cleanups
142+ self.assertThat(logger.output, Contains(message))