bunqueue v2.8.19
blog · release
bunqueue v2.8.19
Fixes 2 bugs. A job that was processed successfully could be recorded as failed when its lock token expired while the handler was running (src/application/queueManager.ts): when lockDuration elapsed without renewal (e.g.
To install bunqueue v2.8.19
Section titled “To install bunqueue v2.8.19”bun add bunqueue@2.8.19npm install bunqueue@2.8.19To upgrade bunqueue
Section titled “To upgrade bunqueue”bun add bunqueue@latestBugfixes
Section titled “Bugfixes”- A job that was processed successfully could be recorded as
failedwhen its lock token expired while the handler was running (src/application/queueManager.ts): whenlockDurationelapsed without renewal (e.g. a half-open TCP storm forcing a worker rebuild on a fresh connection), the handler still finished, but the completion ACK carried the now-expired token. The server rejected it (Invalid or expired lock token), the clientAckBatcherburned its transient retries against this permanent error and dropped the completion, and the job re-pulled → stalled → landed infaileddespite having been processed correctly every time (observed ~350 jobs and 695×acks loston one production queue). The ACK paths (ack,ackBatch,ackBatchWithResults) now apply a grace window: a completion is accepted when the job is still inprocessing, the lock entry’s token still matches the presenting worker, and the lock belongs to the current processing instance (lock.createdAt >= job.startedAt). The third condition is a re-lease guard: the stall path requeues a job without deleting its lock (the lingering lock is load-bearing, the Worker dedups re-pulls viaactiveJobIds, and the lock preserves the original owner’s recovery path), so if another worker re-pulls the job itsstartedAtis reset to a newer time than the lingering lock’screatedAt, the guard denies the grace, and the timed-out worker’s late ACK is rejected, preventing a double-completion. In the genuine case (same worker finishing just after its own lock expired, no re-pull) the completion is recorded instead of being lost to a stall. At-least-once delivery already protected the data; this fixes the queue’s accounting (success recorded as success).
Bugfixes
Section titled “Bugfixes”- A deliberately paused queue silently resumed itself after a server restart, and rate-limit / concurrency overrides reset to defaults (
src/application/queueManager.ts,src/application/backgroundTasks.ts,src/infrastructure/persistence/): thepaused/rateLimit/concurrencyLimitstate lived only inLimiterManager’s in-memory Map. The schema declared aqueue_statetable for exactly this, but nothing read or wrote it, so any restart reset operator intent with no error or warning (a correctness/safety bug: a queue paused for maintenance, or to stop a misbehaving consumer, quietly resumed and processed jobs). The already-declared table is now wired:pause/resume/setRateLimit/clearRateLimit/setConcurrency/clearConcurrencywrite through toqueue_state(UPSERT; an all-default state deletes the row instead of persisting a placeholder),obliteratedrops the row, andrecover()loadsqueue_stateon boot and applies it to the owning shard. Control-state now survives restarts/upgrades/crashes.
Thanks to 1 contributor!
Section titled “Thanks to 1 contributor!”← bunqueue v2.8.18 · Release on GitHub · Commits since v2.8.18 · Full changelog · All posts