github quartznet/quartznet v3.22.8

latest releases: v4.6.2, v3.22.9, v4.6.1...
3 hours ago

Fixes for the 3.x line, including a regression in 3.22.7 that made TriggerJob slower the more triggers its job had stored. There is no public API change and no schema change.

Fixed

  • TriggerJob no longer slows down as its job's backlog grows (regression in 3.22.7). 3.22.7 gave a manual firing the execution group its job's triggers share (#4110), and found that group by loading every stored trigger of the job, three statements per trigger. A queued manual firing is a stored trigger too, so each call cost more than the one before. The group is now read with one statement. (#4170, from #4167)
  • A [DisallowConcurrentExecution] job's completion reads only the triggers that misfired (since 3.17.0). Each completion loaded every remaining trigger of the job under TRIGGER_ACCESS to apply the misfire policy to the late ones, so draining a backlog was quadratic in statements, and ScheduleJob and TriggerJob waited behind the lock. The completion now asks the database only for the job's misfired triggers, with the misfire handler's predicate. (#4168, from #4167)
  • A restart keeps a declared trigger's fire count and an individual pause. A start re-applies every AddTrigger and ScheduleJob registration and every scheduling file through RescheduleJob. That reset TimesTriggered, so a finite WithRepeatCount ran again from the beginning, and it resumed a trigger an operator had paused on its own. Both are now kept while the declared schedule is unchanged; a changed schedule starts fresh. An application's own RescheduleJob still ends an individual pause. (#4149, from #4128)

The #4167 workload on SQL Server: 400 queued triggers on one serial job with a 50 ms body, which would drain in 20 s with no scheduler overhead.

Version Drain time Misfire events TriggerJob median
3.16.1 58 s 0 58 ms
3.22.7 212 s 137 1.4 s
3.22.8 50 s 0 58 ms

Against a remote database every one of those statements is a round trip, so the difference grows with latency.

Behaviour changes

  • A serial job's completion no longer reports a late IgnoreMisfirePolicy trigger to ITriggerListener.TriggerMisfired, and no longer writes it back unchanged. Acquisition fires it, as the misfire handler and RAMJobStore already did. (#4168)
  • The completion no longer calls the protected virtual JobStoreSupport.GetTriggersForJob(conn, …) and UpdateMisfiredTrigger(conn, key, …). A subclass that overrode either to change what a completion does is bypassed there; ResumeTrigger still calls UpdateMisfiredTrigger. (#4168)

Thanks to @kostekk88 for the report and the repro in #4167.

What's Changed

Other changes

  • A start that re-applies declared triggers keeps their run state and an individual pause (3.x) by @lahma in #4149
  • A serial job's completion reads only the misfired triggers it lets go of, not the job's whole backlog (3.x) by @lahma in #4168
  • A manual firing reads the execution group its job's triggers share in one statement, not by loading every trigger of the job (3.x) by @lahma in #4170

Full Changelog: v3.22.7...v3.22.8

Don't miss a new quartznet release

NewReleases is sending notifications on new releases.