bunqueue v2.7.19
blog · release
bunqueue v2.7.19
Fixes 13 bugs. Cloud snapshots leaked unredacted job data (security): BUNQUEUE_CLOUD_REDACT_FIELDS was applied only to the event stream, never to periodic snapshots, so raw job.data and DLQ jobData (potential PII/secrets) were sent to the…
To install bunqueue v2.7.19
Section titled “To install bunqueue v2.7.19”bun add bunqueue@2.7.19npm install bunqueue@2.7.19To upgrade bunqueue
Section titled “To upgrade bunqueue”bun add bunqueue@latestBugfixes
Section titled “Bugfixes”Happy-path behaviour was already solid; these harden bunqueue under failure, stress, attack, restart and long-running conditions. Each fix ships with a test/audit-*.test.ts that reproduced the bug (red) and now passes (green).
- Cloud snapshots leaked unredacted job data (security):
BUNQUEUE_CLOUD_REDACT_FIELDSwas applied only to the event stream, never to periodic snapshots, so rawjob.dataand DLQjobData(potential PII/secrets) were sent to the dashboard. Redaction (andincludeJobDatagating) is now threaded through the snapshot path via a sharedredacthelper. (cloud/snapshotHelpers.ts,snapshotCollector.ts,cloudAgent.ts, newcloud/redact.ts) - WriteBuffer critical loss was unrecoverable after restart: when a flush exhausted its 10 retries, lost jobs were only logged + kept in an in-memory cap; they are now persisted to the DLQ (direct DB write, no recursion into the failed buffer) so they survive a restart. (
persistence/sqlite.ts) - Corrupt
dependsOnblob ran jobs out of order: a MessagePack decode failure silently returned empty deps, so a job recovered with corrupt dependency metadata executed as if it had none. Corruption is now flagged with a collision-proofSymboland the job is routed to the DLQ on recovery instead of running. (persistence/sqliteSerializer.ts,application/backgroundTasks.ts) - Worker ACK batcher silently dropped ACKs on overflow: at the pending-ACK cap the oldest ~10% were discarded without being sent, leaving those jobs stuck
processingand requeued indefinitely. Overflow now applies backpressure (awaits a flush) instead of dropping. (worker/ackBatcher.ts) - TCP slowloris / per-connection memory exhaustion: a partial frame had no read timeout. A per-connection stall timer (armed only while a partial frame is buffered,
TCP_IDLE_TIMEOUT_MS, default 60s) now reaps stalled connections; single frames remain bounded bymaxFrameSize. Legitimate 4–64MB frames delivered across TCP segments are unaffected. (server/protocol.ts,server/tcp.ts) - TCP responses dropped under backpressure:
socket.write()short-writes were ignored anddrain()was a no-op. A per-socket write queue now buffers unwritten bytes (order-preserving), flushes ondrain(), and caps atTCP_MAX_WRITE_QUEUE_BYTES(default 64MB, drops the connection past the cap). (server/tcp.ts, newserver/socketWriteQueue.ts) - TCP client hung on a malformed frame (pipelining): only the legacy
currentCommandwas rejected; all in-flight pipelined commands hung until timeout. A malformed frame now rejects every in-flight command and force-reconnects. (client/tcp/client.ts) - Flow-failure tracking maps grew unbounded:
failedChildrenValues/ignoredChildrenFailureswere never cleared on normal parent completion or inshutdown()(only onobliterate). They are now released when the parent reaches a terminal state and cleared on shutdown. (application/queueManager.ts) forEachsaga compensation lost iteration context: compensate handlers couldn’t tell which item they were rolling back (__item/__indexweren’t restored). Each iteration’s item/index is now persisted on its step record and restored into the compensation context. (workflow/loops.ts,compensator.ts,types.ts)- Re-created cron silently skipped its first fire:
lastFiredAtwasn’t cleared onremove()/upsert, so a same-named cron hit stale overlap detection. It is now cleared on remove/upsert. (scheduler/cronScheduler.ts) - Interval cron drift:
repeatEverynextRunwas computed from execution time (now + interval), drifting on late runs; it is now anchored to the scheduled slot (fixed-rate). (scheduler/cronScheduler.ts) - S3 restore could corrupt/delete the live DB: restore wrote over the live database before validating, and a failed integrity check unlinked it. Restore is now atomic: write to temp → validate → rename; the live DB is never touched on failure. (
backup/s3BackupOperations.ts) - DLQ exceeded
maxEntriesafter restart:restoreEntry()skipped the evictionadd()performs; it now enforcesmaxEntries(oldest-first) on recovery. (domain/queue/dlqShard.ts)
Thanks to 1 contributor!
Section titled “Thanks to 1 contributor!”← bunqueue v2.7.18 · Release on GitHub · Commits since v2.7.18 · Full changelog · All posts