From 700c804368a9327f343b2616c9e475b110ebd8db Mon Sep 17 00:00:00 2001 From: Bryan Date: Tue, 25 Jul 2023 16:49:47 +0800 Subject: [PATCH] Fix Issue #388: Celery Beat scheduled tasks may be executed repeatedly (#660) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: The problem that Celery Beat scheduled tasks may be executed repeatedly * fix: 增加 TypeError 捕获 * fix: Add tests for `test_sync_not_saves_last_run_at_while_schedule_changed` --- django_celery_beat/schedulers.py | 4 ++-- t/unit/test_schedulers.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/django_celery_beat/schedulers.py b/django_celery_beat/schedulers.py index 2c1b5773..846b97a9 100644 --- a/django_celery_beat/schedulers.py +++ b/django_celery_beat/schedulers.py @@ -300,9 +300,9 @@ def sync(self): while self._dirty: name = self._dirty.pop() try: - self.schedule[name].save() + self._schedule[name].save() _tried.add(name) - except (KeyError, ObjectDoesNotExist): + except (KeyError, TypeError, ObjectDoesNotExist): _failed.add(name) except DatabaseError as exc: logger.exception('Database error while sync: %r', exc) diff --git a/t/unit/test_schedulers.py b/t/unit/test_schedulers.py index f44d88cb..dbafe666 100644 --- a/t/unit/test_schedulers.py +++ b/t/unit/test_schedulers.py @@ -444,6 +444,28 @@ def test_reserve(self): assert self.s.flushed == 1 assert self.m2.name in self.s._dirty + def test_sync_not_saves_last_run_at_while_schedule_changed(self): + # Update e1 last_run_at and add to dirty + e1 = self.s.schedule[self.m2.name] + time.sleep(3) + e1.model.last_run_at = e1._default_now() + self.s._dirty.add(e1.model.name) + + # Record e1 pre sync last_run_at + e1_pre_sync_last_run_at = e1.model.last_run_at + + # Set schedule_changed() == True + self.s._last_timestamp = monotonic() + # Do sync + self.s.sync() + + # Record e1 post sync last_run_at + e1_m = PeriodicTask.objects.get(pk=e1.model.pk) + e1_post_sync_last_run_at = e1_m.last_run_at + + # Check + assert e1_pre_sync_last_run_at == e1_post_sync_last_run_at + def test_sync_saves_last_run_at(self): e1 = self.s.schedule[self.m2.name] last_run = e1.last_run_at