Skip to content

Fix rescheduling recreated dynamic tasks - #793

Merged
rosa merged 2 commits into
rails:mainfrom
saidM:fix/reschedule-recreated-dynamic-tasks
Nov 3, 2026
Merged

rosa merged 2 commits into
rails:mainfrom
saidM:fix/reschedule-recreated-dynamic-tasks

Conversation

@saidM

@saidM saidM commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #792.

The scheduler reconciles dynamic tasks by key, so a task removed and recreated with the same key between polls currently looks unchanged.

This change tracks the database record associated with each scheduled dynamic task. When the same key points to a new record, the old timer is cancelled and the replacement is scheduled. It also prevents a callback from the previous task from restoring the old schedule.

Direct updates to an existing task record remain unchanged, as documented.

Testing

  • Added coverage for replacing a dynamic task with the same key.
  • Added coverage for direct updates remaining unchanged.
  • Ran the full SQLite test suite: 357 tests, 1,744 assertions.
  • Ran RuboCop.

@rosa
rosa force-pushed the fix/reschedule-recreated-dynamic-tasks branch from 2939e3d to f780057 Compare November 3, 2026 11:15
@rosa

rosa commented Nov 3, 2026

Copy link
Copy Markdown
Member

Thanks a lot for this, @saidM, and sorry it took us so long! The bug is real, and your fix was correct. I rebased it onto main and added one commit on top that keeps the same behaviour with less bookkeeping: the scheduler now tracks scheduled tasks by record id instead of by key. A task that's deleted and created again gets a new id, so the existing steps that schedule new tasks and unschedule deleted ones handle it, and a run only schedules the next one if its task is still in the schedule. That replaces the map from keys to ids and the active flag.

Your tests are unchanged apart from looking tasks up by id, and I froze time in the in-place update test so a run at the top of the hour can't land between its two lookups.

A task that's deleted and created again with the same key gets a new id,
so keying the in-memory schedule by id lets the existing steps that
schedule new tasks and unschedule deleted ones handle it, without a
second map from keys to ids or a separate active flag. A run only
schedules the next one if its task is still in the schedule, which also
covers a replaced task and a stopped scheduler. The database query for
dynamic tasks now runs before taking the lock, so it doesn't hold up a
task that's due.

Polling now only unschedules dynamic tasks. A static task whose row is
deleted keeps running until the scheduler restarts, when it reads the
configuration again.

Also freeze time in the test for tasks updated in place, so a run at the
top of the hour can't land between its two lookups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rosa
rosa force-pushed the fix/reschedule-recreated-dynamic-tasks branch from f780057 to 6a1d79e Compare November 3, 2026 16:57
@rosa
rosa merged commit 3129f39 into rails:main Nov 3, 2026
56 of 58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dynamic recurring-task reschedule is not detected when the same key is recreated before the next scheduler poll

2 participants