bunqueue 2.8.34 → 2.8.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/dist/application/backgroundTasks.js +37 -7
- package/dist/application/contextFactory.js +5 -0
- package/dist/application/dlqManager.d.ts +6 -0
- package/dist/application/dlqManager.js +35 -14
- package/dist/application/lockManager.js +22 -8
- package/dist/application/operations/customId.d.ts +31 -0
- package/dist/application/operations/customId.js +59 -0
- package/dist/application/operations/jobClaim.d.ts +17 -0
- package/dist/application/operations/jobClaim.js +20 -0
- package/dist/application/operations/jobManagement.d.ts +4 -5
- package/dist/application/operations/jobManagement.js +15 -116
- package/dist/application/operations/jobMoveOperations.d.ts +9 -0
- package/dist/application/operations/jobMoveOperations.js +94 -0
- package/dist/application/operations/jobStateTransitions.js +3 -0
- package/dist/application/operations/pull.js +5 -0
- package/dist/application/operations/push.js +8 -103
- package/dist/application/operations/pushInsert.d.ts +12 -0
- package/dist/application/operations/pushInsert.js +25 -0
- package/dist/application/operations/pushLocks.d.ts +14 -0
- package/dist/application/operations/pushLocks.js +43 -0
- package/dist/application/operations/queueControl.d.ts +1 -1
- package/dist/application/operations/queueControl.js +1 -1
- package/dist/application/queueManager.d.ts +2 -2
- package/dist/application/queueManager.js +30 -10
- package/dist/application/stallDetection.js +17 -8
- package/dist/client/queue/dlq.d.ts +23 -1
- package/dist/client/queue/dlq.js +75 -0
- package/dist/client/queue/helpers.js +2 -0
- package/dist/client/queue/jobProxy.d.ts +1 -1
- package/dist/client/queue/operations/control.d.ts +17 -0
- package/dist/client/queue/operations/control.js +41 -0
- package/dist/client/queue/operations/counts.d.ts +1 -0
- package/dist/client/queue/operations/counts.js +3 -0
- package/dist/client/queue/queue.d.ts +13 -0
- package/dist/client/queue/queue.js +39 -0
- package/dist/client/queue/rateLimit.d.ts +18 -3
- package/dist/client/queue/rateLimit.js +45 -10
- package/dist/client/queue/stall.d.ts +2 -0
- package/dist/client/queue/stall.js +12 -0
- package/dist/domain/queue/dependencyTracker.d.ts +5 -0
- package/dist/domain/queue/dependencyTracker.js +20 -0
- package/dist/domain/queue/limiterManager.d.ts +14 -2
- package/dist/domain/queue/limiterManager.js +32 -5
- package/dist/domain/queue/shard.d.ts +3 -2
- package/dist/domain/queue/shard.js +11 -2
- package/dist/domain/types/command.d.ts +4 -0
- package/dist/domain/types/queue.d.ts +4 -0
- package/dist/domain/types/queue.js +2 -0
- package/dist/infrastructure/persistence/schema.d.ts +2 -2
- package/dist/infrastructure/persistence/schema.js +38 -2
- package/dist/infrastructure/persistence/sqlite.d.ts +20 -1
- package/dist/infrastructure/persistence/sqlite.js +62 -6
- package/dist/infrastructure/persistence/sqliteBatch.js +8 -6
- package/dist/infrastructure/persistence/sqliteSerializer.d.ts +6 -0
- package/dist/infrastructure/persistence/sqliteSerializer.js +9 -1
- package/dist/infrastructure/persistence/statements.d.ts +8 -1
- package/dist/infrastructure/persistence/statements.js +7 -5
- package/dist/infrastructure/scheduler/cronScheduler.d.ts +5 -0
- package/dist/infrastructure/scheduler/cronScheduler.js +39 -13
- package/dist/infrastructure/server/handlers/advanced.js +11 -2
- package/dist/infrastructure/server/httpRouteQueueConfig.js +2 -0
- package/package.json +4 -1
|
@@ -283,6 +283,35 @@ export class CronScheduler {
|
|
|
283
283
|
else {
|
|
284
284
|
newNextRun = executionTime;
|
|
285
285
|
}
|
|
286
|
+
// Skip decision comes BEFORE the executions increment: a skipped fire
|
|
287
|
+
// pushes no job, so it must not consume the maxLimit budget (a cron
|
|
288
|
+
// with maxLimit=N must deliver N jobs, not N-minus-skips — worst case
|
|
289
|
+
// skipIfNoWorker used to burn the whole budget with zero deliveries).
|
|
290
|
+
// The schedule still advances so the heap doesn't hot-loop on it.
|
|
291
|
+
const skipReason = this.getSkipReason(cron, now);
|
|
292
|
+
if (skipReason) {
|
|
293
|
+
if (this.persistCron) {
|
|
294
|
+
try {
|
|
295
|
+
this.persistCron(cron.name, cron.executions, newNextRun);
|
|
296
|
+
}
|
|
297
|
+
catch (persistErr) {
|
|
298
|
+
// Benign here: nothing was pushed, in-memory advance is enough;
|
|
299
|
+
// the next successful persist catches the row up.
|
|
300
|
+
cronLog.error('Failed to persist cron nextRun on skip', {
|
|
301
|
+
name: cron.name,
|
|
302
|
+
error: String(persistErr),
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
cron.nextRun = newNextRun;
|
|
307
|
+
this.dashboardEmit?.('cron:skipped', {
|
|
308
|
+
name: cron.name,
|
|
309
|
+
queue: cron.queue,
|
|
310
|
+
reason: skipReason,
|
|
311
|
+
});
|
|
312
|
+
toReinsert.push(entry);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
286
315
|
// PERSIST STATE FIRST (before pushing job to prevent duplicates)
|
|
287
316
|
if (this.persistCron) {
|
|
288
317
|
try {
|
|
@@ -339,27 +368,24 @@ export class CronScheduler {
|
|
|
339
368
|
this.scheduleNext();
|
|
340
369
|
}
|
|
341
370
|
/** Fire a cron job with overlap and worker detection */
|
|
342
|
-
|
|
371
|
+
/**
|
|
372
|
+
* Reasons a due cron must not push right now. Evaluated BEFORE the
|
|
373
|
+
* executions increment in tick(), so skips never consume maxLimit budget.
|
|
374
|
+
*/
|
|
375
|
+
getSkipReason(cron, now) {
|
|
343
376
|
// skipIfNoWorker: skip if no workers registered for this queue
|
|
344
377
|
if (cron.skipIfNoWorker && this.hasWorkers && !this.hasWorkers(cron.queue)) {
|
|
345
|
-
|
|
346
|
-
name: cron.name,
|
|
347
|
-
queue: cron.queue,
|
|
348
|
-
reason: 'no-worker',
|
|
349
|
-
});
|
|
350
|
-
return;
|
|
378
|
+
return 'no-worker';
|
|
351
379
|
}
|
|
352
380
|
// Overlap detection: skip if last fire was within repeatEvery window
|
|
353
381
|
const lastFire = this.lastFiredAt.get(cron.name);
|
|
354
382
|
const interval = cron.repeatEvery ?? 60000;
|
|
355
383
|
if (lastFire && now - lastFire < interval * 0.8) {
|
|
356
|
-
|
|
357
|
-
name: cron.name,
|
|
358
|
-
queue: cron.queue,
|
|
359
|
-
reason: 'overlap',
|
|
360
|
-
});
|
|
361
|
-
return;
|
|
384
|
+
return 'overlap';
|
|
362
385
|
}
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
async fireCronJob(cron, now) {
|
|
363
389
|
// preventOverlap: auto-set uniqueKey to block pushes while previous job is active
|
|
364
390
|
const effectiveUniqueKey = cron.uniqueKey ?? (cron.preventOverlap ? `cron:${cron.name}` : undefined);
|
|
365
391
|
const opts = cron.jobOptions;
|
|
@@ -172,8 +172,17 @@ export function handleRateLimit(cmd, ctx, reqId) {
|
|
|
172
172
|
const limit = toFiniteNumber(cmd.limit);
|
|
173
173
|
if (limit === undefined)
|
|
174
174
|
return resp.error('limit must be a finite number', reqId);
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
// Optional window/TTL: invalid values degrade to the defaults (1s window,
|
|
176
|
+
// permanent limit) inside the limiter rather than failing the command.
|
|
177
|
+
const duration = toFiniteNumber(cmd.duration);
|
|
178
|
+
const ttl = toFiniteNumber(cmd.ttl);
|
|
179
|
+
ctx.queueManager.setRateLimit(cmd.queue, limit, duration, ttl);
|
|
180
|
+
ctx.queueManager.emitDashboardEvent('ratelimit:set', {
|
|
181
|
+
queue: cmd.queue,
|
|
182
|
+
max: limit,
|
|
183
|
+
...(duration !== undefined && { duration }),
|
|
184
|
+
...(ttl !== undefined && { ttl }),
|
|
185
|
+
});
|
|
177
186
|
return resp.ok(undefined, reqId);
|
|
178
187
|
}
|
|
179
188
|
/** Handle RateLimitClear command */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bunqueue",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.36",
|
|
4
4
|
"description": "High-performance job queue for Bun & AI agents. SQLite persistence, cron scheduling, priorities, retries, DLQ, webhooks, native MCP server. Zero external infrastructure.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -57,7 +57,9 @@
|
|
|
57
57
|
"build": "bun build --compile --minify src/main.ts --outfile dist/bunqueue",
|
|
58
58
|
"build:lib": "tsc -p tsconfig.build.json",
|
|
59
59
|
"test": "BUNQUEUE_EMBEDDED=1 bun test",
|
|
60
|
+
"test:model": "BUNQUEUE_EMBEDDED=1 bun test test/model-based/queue-model.test.ts",
|
|
60
61
|
"test:sandbox": "bun run scripts/test-sandbox.ts",
|
|
62
|
+
"test:sandbox:sdk": "bun run scripts/test-sdk-sandbox.ts",
|
|
61
63
|
"bench": "bun run bench/throughput.ts && bun run bench/latency.ts",
|
|
62
64
|
"bench:throughput": "bun run bench/throughput.ts",
|
|
63
65
|
"bench:latency": "bun run bench/latency.ts",
|
|
@@ -82,6 +84,7 @@
|
|
|
82
84
|
"@types/bun": "^1.3.9",
|
|
83
85
|
"bullmq": "^5.79.3",
|
|
84
86
|
"elysia": "^1.4.25",
|
|
87
|
+
"fast-check": "^4.9.0",
|
|
85
88
|
"ioredis": "^5.11.1",
|
|
86
89
|
"typescript": "^5.9.3",
|
|
87
90
|
"zod": "^4.3.6"
|