Skip to content

Reschedule dynamic recurring tasks when they are updated - #774

Closed
wintan1418 wants to merge 1 commit into
rails:mainfrom
wintan1418:pick-up-dynamic-recurring-task-updates
Closed

Reschedule dynamic recurring tasks when they are updated#774
wintan1418 wants to merge 1 commit into
rails:mainfrom
wintan1418:pick-up-dynamic-recurring-task-updates

Conversation

@wintan1418

@wintan1418 wintan1418 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Fixes #738

Problem

When dynamic recurring tasks are enabled, the scheduler's polling cycle (Scheduler::RecurringSchedule#reschedule_dynamic_tasks) only handles two cases:

  • schedule_created_dynamic_tasks — rows whose key isn't scheduled yet
  • unschedule_deleted_dynamic_tasks — scheduled entries whose key is no longer in the DB

A task whose key exists in both places falls through both checks, so any update to its schedule, class_name, arguments, queue_name, etc. is silently ignored: the scheduler keeps firing against the pre-update definition until the process restarts.

task = SolidQueue.schedule_recurring_task("import", class: "ImportJob", schedule: "0 4 * * *")
# scheduler picks it up on the next poll

task.update!(schedule: "0 8 * * *")
# expected: fires at 08:00 from now on
# actual:   keeps firing at 04:00 until the scheduler restarts

Fix

Keep the previously loaded snapshot of dynamic tasks across each polling cycle and compare every reloaded task's updated_at against it. Any task that changed gets its in-memory scheduled entry cancelled and rescheduled from the fresh record, mirroring what a delete + create with a polling cycle in between would have done.

Testing

Added a regression test that starts a scheduler with a dynamic task on an hourly cadence, updates it to every second while the scheduler is running, and asserts jobs get enqueued without a restart. The test fails on main and passes with this change.

@wintan1418
wintan1418 force-pushed the pick-up-dynamic-recurring-task-updates branch 3 times, most recently from 81bdc96 to f5bab54 Compare July 30, 2026 11:47
The scheduler's polling cycle only handled created and deleted dynamic
tasks. A task whose schedule, class, arguments or any other attribute
changed fell through both checks, so the scheduler kept firing on the
old definition until the process was restarted.

Keep the previously loaded snapshot of dynamic tasks and compare each
reloaded task's updated_at against it, cancelling and rescheduling any
task that changed.

Fixes rails#738
@wintan1418
wintan1418 force-pushed the pick-up-dynamic-recurring-task-updates branch from f5bab54 to c2e5d68 Compare July 30, 2026 11:59
@rosa

rosa commented Jul 30, 2026

Copy link
Copy Markdown
Member

Hey @wintan1418, thanks for this, but this is expected behaviour. Check this comment: #739 (comment)

@wintan1418

Copy link
Copy Markdown
Contributor Author

Thanks @rosa — makes sense, and sorry I missed the earlier discussion on #739. Closing this one. Since it's tripped a few people now, I've opened #777 adding a short note to the README's dynamic tasks section spelling out that in-place updates aren't picked up and that unschedule + schedule again is the way to update a task.

rosa pushed a commit that referenced this pull request Jul 30, 2026
The scheduler only detects dynamic tasks being created and deleted, so
updating a SolidQueue::RecurringTask record in place isn't picked up
until a restart. This is by design: the documented way to update a task
is to unschedule it and schedule it again.

This wasn't stated in the README and has caused some confusion (#738,
and the closed #739 and #774), so spell it out in the dynamic tasks
section.
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.

Updates to dynamic recurring tasks aren't picked up until the scheduler restarts

2 participants