Skip to content

Changelog

Version v0.15.0

What’s Changed

Breaking Changes

  • triggerJob/triggerJobs renamed to rescheduleJob/rescheduleJobs with an optional schedule parameter ({ at: Date } | { afterMs: number }). Omitting it reschedules to now (previous behavior). A past time clamps to now. Only pending jobs are reschedulable. Batch validation is atomic.
  • Error classes renamed: JobNotTriggerableError / JobsNotTriggerableError → JobNotReschedulableError / JobsNotReschedulableError. New batch error variants JobsNotFoundError and JobsNotReschedulableError for rescheduleJobs. The status property removed from the singular error; the batch variant exposes jobIds.
  • Observability event renamed: jobTriggered → jobRescheduled. Now also emitted on worker retry (not just client reschedules), making it the single signal for “scheduledAt changed.” jobAttemptFailed is now a pure failure event — rescheduledAt/rescheduledAfterMs removed. scheduleAfterMs dropped from jobCreated and jobRescheduled events.
  • StateProvider gains required transactionConcurrency field ("concurrent" | "serialized"). Custom provider authors must declare it.
  • Dashboard: POST /jobs/:id/trigger renamed to /reschedule; “Trigger now” button renamed to “Reschedule”.
  • OTEL: Counter queuert.job.triggered → queuert.job.rescheduled; log entry job_triggered → job_rescheduled.

Features

  • Blocker chain cap: A job can declare at most 100 blocker chains. startChain, startChains, and continueWith throw the new BlockerLimitExceededError when exceeded.
  • Dashboard UX improvements: Infinite scroll on list views (chains, jobs, chain detail), real links on cards (right-click/Cmd-click to open in new tab), and smart back navigation that preserves filters.

Bug Fixes

  • createProcessors type resolution: Previously resolved job type names from the local slice only; now resolves from all registered slices.
  • "incomplete" dedup scope: Multi-step chains that continued past the root job are now correctly matched. Previously, once the root completed, the chain was no longer matched even though it was still running.
  • Concurrent unblock race (@queuert/postgres, @queuert/sqlite): Row-level locks on blocked jobs during unblockJobs prevent a race where concurrent blocker completions could leave dependent jobs stranded in blocked status. job_blocker PK widened to support duplicate chain IDs.

Performance

  • Autovacuum tuning (@queuert/postgres): New migration pins autovacuum_vacuum_threshold = 5000 / scale_factor = 0 on job and job_blocker tables, replacing size-proportional 0.02. Gives predictable vacuum cadence regardless of table size.

Packages

All packages released at 0.15.0: queuert, @queuert/postgres, @queuert/sqlite, @queuert/redis, @queuert/nats, @queuert/otel, @queuert/dashboard.