fix: cap is_due delay at start_time for one-off crontab tasks (#1044) - #1045
fix: cap is_due delay at start_time for one-off crontab tasks (#1044)#1045yanlong-pan wants to merge 1 commit into
Conversation
…#1044) For a one-off PeriodicTask whose CrontabSchedule pins day_of_month + month_of_year (and often day_of_week) to specific values, CrontabSchedule.due_start_time() returns the *next* matching calendar date -- typically one year in the future. ModelEntry.is_due() then returned this years-long delay, causing the task to fire ~5 minutes late (when the forced sync interval expires) instead of at start_time. Cap the start_time used for delay computation by min()-ing with the user-specified model.start_time when model.one_off is True. Recurring crontabs are unaffected -- day_of_month='*' makes the next match at most 24h away, and the post-celery#844 wake-at-next-match behavior is desirable for them. Fixes celery#1044
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1045 +/- ##
==========================================
+ Coverage 87.68% 87.70% +0.02%
==========================================
Files 32 32
Lines 1015 1017 +2
Branches 81 82 +1
==========================================
+ Hits 890 892 +2
Misses 107 107
Partials 18 18 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR fixes a scheduling regression for one-off crontab-backed PeriodicTasks with future start_time, ensuring they wake at start_time rather than the next matching fixed-date crontab occurrence.
Changes:
- Caps one-off task delay calculation at
model.start_timeinModelEntry.is_due(). - Adds regression coverage for fixed-date one-off crontab tasks.
- Adds coverage confirming recurring crontab behavior remains unchanged.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
django_celery_beat/schedulers.py |
Caps computed future start delay for one-off tasks so they do not sleep past start_time. |
t/unit/test_schedulers.py |
Adds tests for the one-off fixed-date crontab regression and recurring crontab behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
auvipy
left a comment
There was a problem hiding this comment.
so we are OK with partial changes for the regression?
|
Author of #844 here, just wanted to share some notes from digging into this. The core question I keep coming back to is whether The repro run_at = timezone.now() + timedelta(minutes=2) # e.g. 05:36:53.747
cron = CrontabSchedule(
minute=str(run_at.minute), # "36"
hour=str(run_at.hour), # "5"
day_of_month=str(run_at.day),
month_of_year=str(run_at.month),
day_of_week=str(run_at.isoweekday() % 7),
)
task = PeriodicTask(crontab=cron, start_time=run_at, one_off=True)The cron pattern matches at minute boundary (e.g. Whenever In 2.7.0 this didn't surface because the delay was just What #1045 actually does The patch does A question worth raising The original issue #843 that #844 was addressing wasn't about the task failing to fire — it was about heap-top blocking. In celery's scheduler event = H[0] # peek at top, don't pop
is_due, _ = self.is_due(entry)
if is_due:
heappop(H) # only pop when due
...
# if not due, the top stays, and entries below it are never evaluated this tickPre-#844, a crontab task with future With #1045, one-off crontab tasks with future Two ways to look at the policy call
|
For a one-off PeriodicTask whose CrontabSchedule pins day_of_month + month_of_year (and often day_of_week) to specific values, CrontabSchedule.due_start_time() returns the next matching calendar date -- typically one year in the future. ModelEntry.is_due() then returned this years-long delay, causing the task to fire ~5 minutes late (when the forced sync interval expires) instead of at start_time.
Cap the start_time used for delay computation by min()-ing with the user-specified model.start_time when model.one_off is True. Recurring crontabs are unaffected -- day_of_month='*' makes the next match at most 24h away, and the post-#844 wake-at-next-match behavior is desirable for them.
Fixes #1044