Skip to content

Fix scheduler thread stuck after system clock jumps backward (#1508)#2929

Merged
lahma merged 3 commits into
3.xfrom
system-time
Mar 29, 2026
Merged

Fix scheduler thread stuck after system clock jumps backward (#1508)#2929
lahma merged 3 commits into
3.xfrom
system-time

Conversation

@lahma

@lahma lahma commented Mar 29, 2026

Copy link
Copy Markdown
Member

Summary

  • Cap the trigger-wait timeout in QuartzSchedulerThread at idleWaitTime (30s default) so the thread recovers from system clock backward jumps within one idle-wait cycle
  • Under normal operation the cap never activates since triggers are acquired at most idleWaitTime into the future — zero performance impact

Fixes #1508

Details

When the system clock jumps backward (NTP correction, manual change, VM migration), timeUntilTrigger in the trigger-wait loop becomes arbitrarily large (hours/days). The schedulingChangeSignal.WaitAsync(timeUntilTrigger, ...) call would block for the entire duration with nothing to signal it, causing triggers to stop firing.

The outer while loop already recomputes timeUntilTrigger from the current clock after each wait, so capping the wait is safe and sufficient.

Test plan

  • New SystemTimeClockJumpTest validates trigger fires after clock-backward jump using SystemTime.UtcNow mocking
  • Existing HighCpuSchedulerTest and ShutdownTriggerReleaseTest pass (no regression)
  • Solution builds with zero warnings

🤖 Generated with Claude Code

When the system clock jumps backward, the trigger-wait loop in
QuartzSchedulerThread could block for the duration of the clock jump
(potentially hours) because schedulingChangeSignal.WaitAsync used
timeUntilTrigger as an unbounded timeout. Cap the wait at idleWaitTime
(30s default) so the thread wakes up periodically and recomputes from
the current clock. Under normal operation the cap never activates since
triggers are acquired at most idleWaitTime into the future.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@lahma
lahma requested a review from Copilot March 29, 2026 08:23
@lahma lahma added the port-main Requires porting to main branch label Mar 29, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Caps the scheduler thread’s trigger-wait timeout to ensure QuartzSchedulerThread reliably recovers and resumes firing triggers after a system clock jump backward (fix for #1508), and adds a unit test intended to validate the regression.

Changes:

  • Cap schedulingChangeSignal.WaitAsync(...) timeout to idleWaitTime while waiting for the next trigger fire time.
  • Add SystemTimeClockJumpTest to exercise recovery behavior after a backward clock jump.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Quartz/Core/QuartzSchedulerThread.cs Bounds the trigger wait duration to avoid extended sleeps when the system clock moves backward.
src/Quartz.Tests.Unit/Core/SystemTimeClockJumpTest.cs Adds a regression test for the backward clock jump scenario (needs adjustment to reliably exercise acquisition/wait path).

Comment thread src/Quartz.Tests.Unit/Core/SystemTimeClockJumpTest.cs Outdated
lahma and others added 2 commits March 29, 2026 11:28
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@sonarqubecloud

Copy link
Copy Markdown

@lahma lahma removed the port-main Requires porting to main branch label Mar 29, 2026
@lahma
lahma merged commit e6cef25 into 3.x Mar 29, 2026
14 of 15 checks passed
@lahma
lahma deleted the system-time branch March 29, 2026 08:40
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.

2 participants