You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(stability): close 8 audit + destruction-test bugs (RED→GREEN repros)
End-to-end audit + adversarial destruction test of v2.8.13. The data plane was already bulletproof (exactly-once through a SIGKILL flood, zero corruption, lossless crash recovery, bad-input isolation); these are feature-conditional control-plane / resource-hygiene defects. Each fix ships a committed reproduction test.
- concurrency slot leak on lock expiry -> setConcurrency(N) queue wedged to 0 (lockManager: releaseJobResources on requeue/DLQ)
- dependency children orphaned for undefined-result (across restart) and removeOnComplete parents (depCompletions set + state='completed' recovery)
- addBulk/PUSHB ignored durable -> batch durable now written immediately in one transaction (push.ts, sqlite.insertJobsBatch)
- pool socket drop re-dispatched in-flight jobs (double exec) -> releaseClientJobs skips renewalCount>0; worker renews just-pulled locks immediately
- Worker.close() hang on group-limited buffered jobs -> requeue buffered, drain on activeJobs only, force-close pre-empts
- worker not re-registered after TCP reconnect -> tcpPool.onReconnect + worker re-register (visibility only)
- moveToWaitingChildren stranded job -> getJob/getJobByCustomId/cancelJob consult waitingChildren
- perQueueMetrics unbounded -> LRU-bounded + freed by obliterate
Verified: tsc clean; unit 5640/5641, TCP 59/59, embedded 272/273 (2 pre-existing timing flakes pass in isolation); 9 repro files (20 tests) green; 161/161 disconnect-related suites; skeptic PASS.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/src/content/docs/changelog.md
+15Lines changed: 15 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,6 +10,21 @@ head:
10
10
11
11
All notable changes to bunqueue are documented here.
12
12
13
+
## [2.8.14] - 2026-06-15
14
+
15
+
### Fixed — 8 stability bugs from an end-to-end audit + destruction test (each with a RED→GREEN reproduction test)
16
+
17
+
The data plane was already bulletproof under the destruction test (exactly-once held through a SIGKILL flood, zero corruption, lossless crash recovery, bad-input isolation). These fixes close feature-conditional defects in the control plane and resource hygiene. No change to data correctness or process stability for the default producer/consumer path.
18
+
19
+
-**Concurrency slot leak on lock expiry** (`lockManager.ts`): `requeueExpiredJob` / `handleMaxStallsExceeded` now call `shard.releaseJobResources()` before re-queue/DLQ, mirroring the stall-detection paths. Previously a queue with `setConcurrency(N)` permanently wedged (throughput → 0) after N lock expiries under worker churn.
20
+
-**Dependency children orphaned** (`ack.ts`, `ackHelpers.ts`, `dependencyProcessor.ts`, `push.ts`, `backgroundTasks.ts`, `sqlite.ts`): a child `dependsOn` a parent that returned `undefined` (across a restart) or had `removeOnComplete: true` was silently never run and dropped after 1h. Added a bounded `depCompletions` set for removeOnComplete parents and made dependency recovery recognize `state='completed'` rows (not only `job_results`). Fixes late-dependent ordering too.
21
+
-**`addBulk` / PUSHB ignored `durable`** (`push.ts`, `sqlite.ts`): durable batch jobs sat in the 10ms write buffer instead of being written immediately like a single durable push. `insertJobsBatch(jobs, durable)` now writes the durable subset to disk atomically (single transaction), bypassing the buffer.
22
+
-**Pool socket drop re-dispatched in-flight jobs** (`clientTracking.ts`, `worker.ts`): with `poolSize > 1`, dropping the connection that pulled a job re-queued a job a live worker was still running (double execution). `releaseClientJobs` now skips jobs whose lock was renewed (`renewalCount > 0`); the worker renews just-pulled locks immediately so the window cannot open.
23
+
-**`Worker.close()` hang on buffered jobs** (`worker.ts`): a graceful close with group-limited buffered jobs hung forever; `close(true)` could not pre-empt it. Buffered (pulled-but-unstarted) jobs are now requeued on close, the drain waits only on genuinely in-flight jobs, and a force close pre-empts an in-progress graceful close.
24
+
-**Worker not re-registered after a TCP reconnect** (`tcpPool.ts`, `worker.ts`): after a transient drop the worker vanished from `ListWorkers` / `getForQueue` while still consuming jobs. The pool now exposes `onReconnect()` and the worker re-registers on reconnect. (Visibility only — no data loss.)
25
+
-**`moveToWaitingChildren` stranded the job** (`queryOperations.ts`, `jobManagement.ts`): a job moved to waiting-children was invisible to `getJob` and uncancellable. `getJob` / `getJobByCustomId` / `cancelJob` now consult `waitingChildren`.
26
+
-**`perQueueMetrics` unbounded growth** (`queueManager.ts`, `cleanupTasks.ts`): the per-queue metrics map grew one permanent entry per distinct queue name and was not freed by `obliterate()`. It is now LRU-bounded and freed by `obliterate()`; cumulative counters survive a transient drain.
27
+
13
28
## [2.8.13] - 2026-06-15
14
29
15
30
### Fixed — explicit `job.moveToFailed(err)` now carries the stacktrace (#74 follow-up)
0 commit comments