bunqueue 2.8.53 → 2.8.54
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/dist/application/backgroundTasks.js +5 -1
- package/dist/application/cleanupTasks.js +1 -1
- package/dist/application/clientTracking.js +1 -1
- package/dist/application/contextFactory.js +1 -0
- package/dist/application/dlqManager.d.ts +7 -1
- package/dist/application/dlqManager.js +16 -5
- package/dist/application/lockManager.js +3 -3
- package/dist/application/operations/ack.js +2 -2
- package/dist/application/operations/ackHelpers.js +1 -1
- package/dist/application/operations/jobManagement.js +6 -4
- package/dist/application/operations/jobMoveOperations.js +3 -3
- package/dist/application/operations/jobStateTransitions.d.ts +1 -1
- package/dist/application/operations/jobStateTransitions.js +16 -2
- package/dist/application/queueManager.d.ts +22 -2
- package/dist/application/queueManager.js +48 -6
- package/dist/application/stallDetection.js +3 -3
- package/dist/client/flowJobDependencies.js +3 -1
- package/dist/client/flowJobMoveMethods.js +7 -2
- package/dist/client/jobConversion.d.ts +3 -3
- package/dist/client/jobConversion.js +10 -6
- package/dist/client/jobConversionHelpers.d.ts +1 -1
- package/dist/client/jobConversionHelpers.js +3 -6
- package/dist/client/jobConversionTypes.d.ts +4 -0
- package/dist/client/jobDeduplication.d.ts +6 -0
- package/dist/client/jobDeduplication.js +14 -0
- package/dist/client/queue/bullmqCompat.d.ts +18 -8
- package/dist/client/queue/bullmqCompat.js +39 -71
- package/dist/client/queue/deduplication.d.ts +2 -0
- package/dist/client/queue/deduplication.js +20 -11
- package/dist/client/queue/dlq.d.ts +11 -6
- package/dist/client/queue/dlq.js +47 -4
- package/dist/client/queue/dlqJobMethods.d.ts +9 -0
- package/dist/client/queue/dlqJobMethods.js +176 -0
- package/dist/client/queue/dlqOps.d.ts +2 -1
- package/dist/client/queue/dlqOps.js +2 -2
- package/dist/client/queue/jobMove.js +6 -1
- package/dist/client/queue/jobProxy.js +10 -5
- package/dist/client/queue/operations/add.js +17 -0
- package/dist/client/queue/operations/index.d.ts +1 -0
- package/dist/client/queue/operations/index.js +1 -0
- package/dist/client/queue/operations/management.d.ts +0 -4
- package/dist/client/queue/operations/management.js +33 -5
- package/dist/client/queue/operations/query.d.ts +1 -20
- package/dist/client/queue/operations/query.js +18 -53
- package/dist/client/queue/operations/queryStates.d.ts +13 -0
- package/dist/client/queue/operations/queryStates.js +32 -0
- package/dist/client/queue/operations/queryTcpPages.d.ts +14 -0
- package/dist/client/queue/operations/queryTcpPages.js +40 -0
- package/dist/client/queue/queue.d.ts +3 -0
- package/dist/client/queue/queue.js +23 -11
- package/dist/client/queue/rateLimit.d.ts +4 -4
- package/dist/client/queue/rateLimit.js +17 -8
- package/dist/client/queue/workers.d.ts +12 -0
- package/dist/client/queue/workers.js +20 -2
- package/dist/client/queueGroup.d.ts +11 -0
- package/dist/client/queueGroup.js +28 -1
- package/dist/client/sandboxed/worker.js +1 -1
- package/dist/client/worker/processor.js +1 -1
- package/dist/client/worker/processorHandlers.d.ts +1 -5
- package/dist/client/worker/processorHandlers.js +5 -7
- package/dist/client/worker/worker.d.ts +1 -1
- package/dist/client/worker/worker.js +37 -13
- package/dist/domain/queue/limiterManager.d.ts +11 -0
- package/dist/domain/queue/limiterManager.js +28 -0
- package/dist/domain/queue/shard.d.ts +10 -2
- package/dist/domain/queue/shard.js +23 -4
- package/dist/domain/queue/uniqueKeyManager.d.ts +3 -1
- package/dist/domain/queue/uniqueKeyManager.js +9 -1
- package/dist/domain/types/command.d.ts +34 -1
- package/dist/domain/types/queue.d.ts +5 -0
- package/dist/domain/types/queue.js +13 -0
- package/dist/infrastructure/persistence/sqlite.d.ts +4 -0
- package/dist/infrastructure/persistence/sqlite.js +16 -0
- package/dist/infrastructure/server/handlerRoutes.js +14 -1
- package/dist/infrastructure/server/handlers/dlq.d.ts +3 -0
- package/dist/infrastructure/server/handlers/dlq.js +18 -4
- package/dist/infrastructure/server/handlers/introspection.d.ts +19 -0
- package/dist/infrastructure/server/handlers/introspection.js +21 -0
- package/dist/infrastructure/server/handlers/query.js +1 -1
- package/package.json +1 -1
|
@@ -22,12 +22,15 @@ import { getSharedManager } from './manager';
|
|
|
22
22
|
*/
|
|
23
23
|
export class QueueGroup {
|
|
24
24
|
prefix;
|
|
25
|
+
queues = new Map();
|
|
25
26
|
constructor(namespace) {
|
|
26
27
|
this.prefix = namespace.endsWith(':') ? namespace : `${namespace}:`;
|
|
27
28
|
}
|
|
28
29
|
/** Get a queue within this group */
|
|
29
30
|
getQueue(name, opts) {
|
|
30
|
-
|
|
31
|
+
const queue = new Queue(this.prefix + name, opts);
|
|
32
|
+
this.queues.set(name, queue);
|
|
33
|
+
return queue;
|
|
31
34
|
}
|
|
32
35
|
/** Create a worker for a queue in this group */
|
|
33
36
|
getWorker(name, processor, opts) {
|
|
@@ -77,4 +80,28 @@ export class QueueGroup {
|
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
82
|
}
|
|
83
|
+
/** List queues created through this group, including remote queues. */
|
|
84
|
+
async listQueuesAsync() {
|
|
85
|
+
const names = new Set(this.queues.keys());
|
|
86
|
+
for (const name of this.listQueues())
|
|
87
|
+
names.add(name);
|
|
88
|
+
return [...names].sort();
|
|
89
|
+
}
|
|
90
|
+
/** Pause every queue created through this group. */
|
|
91
|
+
async pauseAllAsync() {
|
|
92
|
+
await Promise.all([...this.queues.values()].map((queue) => queue.pauseAsync()));
|
|
93
|
+
}
|
|
94
|
+
/** Resume every queue created through this group. */
|
|
95
|
+
async resumeAllAsync() {
|
|
96
|
+
await Promise.all([...this.queues.values()].map((queue) => queue.resumeAsync()));
|
|
97
|
+
}
|
|
98
|
+
/** Drain waiting jobs from every queue and return the aggregate count. */
|
|
99
|
+
async drainAllAsync() {
|
|
100
|
+
const counts = await Promise.all([...this.queues.values()].map((queue) => queue.drainAsync()));
|
|
101
|
+
return counts.reduce((total, count) => total + count, 0);
|
|
102
|
+
}
|
|
103
|
+
/** Remove all data from every queue created through this group. */
|
|
104
|
+
async obliterateAllAsync() {
|
|
105
|
+
await Promise.all([...this.queues.values()].map((queue) => queue.obliterateAsync()));
|
|
106
|
+
}
|
|
80
107
|
}
|
|
@@ -550,7 +550,7 @@ export class SandboxedWorker extends EventEmitter {
|
|
|
550
550
|
discard: createDiscardHandler(embedded, tcp),
|
|
551
551
|
getDependencies: createGetDependenciesHandler(embedded, tcp, domainJob),
|
|
552
552
|
getDependenciesCount: createGetDependenciesCountHandler(embedded, tcp, domainJob),
|
|
553
|
-
removeDeduplicationKey: createRemoveDeduplicationKeyHandler(),
|
|
553
|
+
removeDeduplicationKey: createRemoveDeduplicationKeyHandler(embedded, tcp),
|
|
554
554
|
moveToCompleted,
|
|
555
555
|
moveToFailed,
|
|
556
556
|
});
|
|
@@ -55,7 +55,7 @@ export async function processJob(internalJob, config) {
|
|
|
55
55
|
discard: createDiscardHandler(embedded, tcp),
|
|
56
56
|
getDependencies: createGetDependenciesHandler(embedded, tcp, internalJob),
|
|
57
57
|
getDependenciesCount: createGetDependenciesCountHandler(embedded, tcp, internalJob),
|
|
58
|
-
removeDeduplicationKey: createRemoveDeduplicationKeyHandler(),
|
|
58
|
+
removeDeduplicationKey: createRemoveDeduplicationKeyHandler(embedded, tcp),
|
|
59
59
|
token: token ?? undefined,
|
|
60
60
|
});
|
|
61
61
|
jobHolder.current = job;
|
|
@@ -58,8 +58,4 @@ export declare function createDiscardHandler(embedded: boolean, tcp: TcpConnecti
|
|
|
58
58
|
*/
|
|
59
59
|
export declare function createGetDependenciesHandler(embedded: boolean, tcp: TcpConnection | null, internalJob: InternalJob): (_id: string) => Promise<JobDependencies>;
|
|
60
60
|
export declare function createGetDependenciesCountHandler(embedded: boolean, tcp: TcpConnection | null, internalJob: InternalJob): (_id: string) => Promise<JobDependenciesCount>;
|
|
61
|
-
|
|
62
|
-
* removeDeduplicationKey — no server primitive; throw explicit error so callers
|
|
63
|
-
* learn this isn't supported rather than silently getting `false`.
|
|
64
|
-
*/
|
|
65
|
-
export declare function createRemoveDeduplicationKeyHandler(): (_id: string) => Promise<boolean>;
|
|
61
|
+
export declare function createRemoveDeduplicationKeyHandler(embedded: boolean, tcp: TcpConnection | null): (id: string) => Promise<boolean>;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { jobId } from '../../domain/types/job';
|
|
6
6
|
import { getSharedManager } from '../manager';
|
|
7
7
|
import { UnrecoverableError } from '../errors';
|
|
8
|
+
import { removeJobDeduplicationKey } from '../jobDeduplication';
|
|
8
9
|
export function createProgressHandler(embedded, tcp, emitter, jobHolder) {
|
|
9
10
|
return async (id, progress, message) => {
|
|
10
11
|
if (embedded) {
|
|
@@ -306,7 +307,8 @@ export function createMoveToWaitingChildrenHandler(embedded, tcp) {
|
|
|
306
307
|
}
|
|
307
308
|
if (!tcp)
|
|
308
309
|
return false;
|
|
309
|
-
|
|
310
|
+
const response = await tcp.send({ cmd: 'MoveToWaitingChildren', id });
|
|
311
|
+
return response.ok === true;
|
|
310
312
|
};
|
|
311
313
|
}
|
|
312
314
|
export function createWaitUntilFinishedHandler(embedded, tcp) {
|
|
@@ -391,10 +393,6 @@ export function createGetDependenciesCountHandler(embedded, tcp, internalJob) {
|
|
|
391
393
|
};
|
|
392
394
|
};
|
|
393
395
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
* learn this isn't supported rather than silently getting `false`.
|
|
397
|
-
*/
|
|
398
|
-
export function createRemoveDeduplicationKeyHandler() {
|
|
399
|
-
return () => Promise.reject(new Error('removeDeduplicationKey is not implemented — no server primitive available'));
|
|
396
|
+
export function createRemoveDeduplicationKeyHandler(embedded, tcp) {
|
|
397
|
+
return (id) => removeJobDeduplicationKey(id, embedded, tcp);
|
|
400
398
|
}
|
|
@@ -152,7 +152,7 @@ export declare class Worker<T = unknown, R = unknown> extends EventEmitter {
|
|
|
152
152
|
private handlePullError;
|
|
153
153
|
/** Register this worker with the server (TCP only, fire-and-forget) */
|
|
154
154
|
private registerWithServer;
|
|
155
|
-
/** Start periodic worker-level heartbeat (separate from job heartbeat) */
|
|
155
|
+
/** Start periodic worker-level heartbeat (separate from job heartbeat). */
|
|
156
156
|
private startWorkerHeartbeat;
|
|
157
157
|
private getHeartbeatDeps;
|
|
158
158
|
private getPullConfig;
|
|
@@ -183,8 +183,17 @@ export class Worker extends EventEmitter {
|
|
|
183
183
|
if (this.embedded && !this.stalledUnsubscribe && !this.opts.skipStalledCheck) {
|
|
184
184
|
this.subscribeToStalledEvents();
|
|
185
185
|
}
|
|
186
|
-
// Register worker
|
|
187
|
-
if (
|
|
186
|
+
// Register the worker in the selected broker runtime.
|
|
187
|
+
if (this.embedded && !this.registered) {
|
|
188
|
+
getSharedManager().registerWorker(this.queueKey, [this.queueKey], this.opts.concurrency, {
|
|
189
|
+
workerId: this.workerId,
|
|
190
|
+
hostname: hostname(),
|
|
191
|
+
pid: process.pid,
|
|
192
|
+
startedAt: this.startedAt,
|
|
193
|
+
});
|
|
194
|
+
this.registered = true;
|
|
195
|
+
}
|
|
196
|
+
else if (this.tcp && !this.registered) {
|
|
188
197
|
this.registerWithServer();
|
|
189
198
|
}
|
|
190
199
|
if (this.opts.heartbeatInterval > 0 && !this.opts.skipLockRenewal) {
|
|
@@ -199,10 +208,10 @@ export class Worker extends EventEmitter {
|
|
|
199
208
|
else {
|
|
200
209
|
const deps = this.getHeartbeatDeps();
|
|
201
210
|
this.heartbeatTimer = startHeartbeat(deps, this.opts.heartbeatInterval);
|
|
202
|
-
// Worker-level heartbeat (separate from job heartbeat)
|
|
203
|
-
this.startWorkerHeartbeat();
|
|
204
211
|
}
|
|
205
212
|
}
|
|
213
|
+
if (this.opts.heartbeatInterval > 0)
|
|
214
|
+
this.startWorkerHeartbeat();
|
|
206
215
|
this.poll();
|
|
207
216
|
}
|
|
208
217
|
/** Subscribe to stalled events from QueueManager (BullMQ v5 compatible) */
|
|
@@ -485,13 +494,18 @@ export class Worker extends EventEmitter {
|
|
|
485
494
|
await this.ackBatcher.flush();
|
|
486
495
|
await this.ackBatcher.waitForInFlight();
|
|
487
496
|
this.ackBatcher.stop();
|
|
488
|
-
// Unregister
|
|
489
|
-
if (
|
|
490
|
-
|
|
491
|
-
|
|
497
|
+
// Unregister before closing the transport/runtime.
|
|
498
|
+
if (this.registered) {
|
|
499
|
+
if (this.embedded) {
|
|
500
|
+
getSharedManager().unregisterWorker(this.workerId);
|
|
492
501
|
}
|
|
493
|
-
|
|
494
|
-
|
|
502
|
+
else if (this.tcp) {
|
|
503
|
+
try {
|
|
504
|
+
await this.tcp.send({ cmd: 'UnregisterWorker', workerId: this.workerId });
|
|
505
|
+
}
|
|
506
|
+
catch {
|
|
507
|
+
// Best-effort unregister — server will cleanup via stale timeout
|
|
508
|
+
}
|
|
495
509
|
}
|
|
496
510
|
this.registered = false;
|
|
497
511
|
}
|
|
@@ -852,12 +866,22 @@ export class Worker extends EventEmitter {
|
|
|
852
866
|
this.emit('error', Object.assign(error, { context: 'worker-register' }));
|
|
853
867
|
});
|
|
854
868
|
}
|
|
855
|
-
/** Start periodic worker-level heartbeat (separate from job heartbeat) */
|
|
869
|
+
/** Start periodic worker-level heartbeat (separate from job heartbeat). */
|
|
856
870
|
startWorkerHeartbeat() {
|
|
857
|
-
if (this.workerHeartbeatTimer
|
|
871
|
+
if (this.workerHeartbeatTimer)
|
|
858
872
|
return;
|
|
859
873
|
this.workerHeartbeatTimer = setInterval(() => {
|
|
860
|
-
if (!this.
|
|
874
|
+
if (!this.registered)
|
|
875
|
+
return;
|
|
876
|
+
if (this.embedded) {
|
|
877
|
+
getSharedManager().workerManager.heartbeat(this.workerId, {
|
|
878
|
+
activeJobs: this.activeJobs,
|
|
879
|
+
processed: this.processedCount,
|
|
880
|
+
failed: this.failedCount,
|
|
881
|
+
});
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
if (!this.tcp)
|
|
861
885
|
return;
|
|
862
886
|
void this.tcp
|
|
863
887
|
.send({
|
|
@@ -38,6 +38,13 @@ export declare class LimiterManager {
|
|
|
38
38
|
expireRateLimitIfNeeded(queue: string): void;
|
|
39
39
|
/** Try to acquire rate limit token */
|
|
40
40
|
tryAcquireRateLimit(queue: string): boolean;
|
|
41
|
+
/** Read the active rate-limit configuration after applying lazy expiry. */
|
|
42
|
+
getRateLimit(queue: string): {
|
|
43
|
+
max: number;
|
|
44
|
+
duration: number;
|
|
45
|
+
} | null;
|
|
46
|
+
/** Read the remaining temporary-limit TTL or token-bucket cooldown. */
|
|
47
|
+
getRateLimitTtl(queue: string, maxJobs?: number): number;
|
|
41
48
|
/** Set concurrency limit for queue */
|
|
42
49
|
setConcurrency(queue: string, limit: number): void;
|
|
43
50
|
/** Clear concurrency limit */
|
|
@@ -46,6 +53,10 @@ export declare class LimiterManager {
|
|
|
46
53
|
tryAcquireConcurrency(queue: string): boolean;
|
|
47
54
|
/** Release concurrency slot */
|
|
48
55
|
releaseConcurrency(queue: string): void;
|
|
56
|
+
/** Read the configured global concurrency limit. */
|
|
57
|
+
getConcurrency(queue: string): number | null;
|
|
58
|
+
/** Whether all configured global concurrency slots are currently occupied. */
|
|
59
|
+
isConcurrencyMaxed(queue: string): boolean;
|
|
49
60
|
/** Get all queue names with state */
|
|
50
61
|
getQueueNames(): string[];
|
|
51
62
|
/** Delete queue data */
|
|
@@ -78,6 +78,25 @@ export class LimiterManager {
|
|
|
78
78
|
const limiter = this.rateLimiters.get(queue);
|
|
79
79
|
return !limiter || limiter.tryAcquire();
|
|
80
80
|
}
|
|
81
|
+
/** Read the active rate-limit configuration after applying lazy expiry. */
|
|
82
|
+
getRateLimit(queue) {
|
|
83
|
+
this.expireRateLimitIfNeeded(queue);
|
|
84
|
+
const state = this.queueState.get(queue);
|
|
85
|
+
if (state?.rateLimit === null || state?.rateLimit === undefined)
|
|
86
|
+
return null;
|
|
87
|
+
return { max: state.rateLimit, duration: state.rateLimitDuration ?? 1000 };
|
|
88
|
+
}
|
|
89
|
+
/** Read the remaining temporary-limit TTL or token-bucket cooldown. */
|
|
90
|
+
getRateLimitTtl(queue, maxJobs) {
|
|
91
|
+
this.expireRateLimitIfNeeded(queue);
|
|
92
|
+
const state = this.queueState.get(queue);
|
|
93
|
+
if (state?.rateLimit === null || state?.rateLimit === undefined)
|
|
94
|
+
return -2;
|
|
95
|
+
if (state.rateLimitExpiresAt !== null) {
|
|
96
|
+
return Math.max(0, state.rateLimitExpiresAt - Date.now());
|
|
97
|
+
}
|
|
98
|
+
return this.rateLimiters.get(queue)?.getTtl(maxJobs) ?? -2;
|
|
99
|
+
}
|
|
81
100
|
// ============ Concurrency Limiting ============
|
|
82
101
|
/** Set concurrency limit for queue */
|
|
83
102
|
setConcurrency(queue, limit) {
|
|
@@ -107,6 +126,15 @@ export class LimiterManager {
|
|
|
107
126
|
releaseConcurrency(queue) {
|
|
108
127
|
this.concurrencyLimiters.get(queue)?.release();
|
|
109
128
|
}
|
|
129
|
+
/** Read the configured global concurrency limit. */
|
|
130
|
+
getConcurrency(queue) {
|
|
131
|
+
return this.queueState.get(queue)?.concurrencyLimit ?? null;
|
|
132
|
+
}
|
|
133
|
+
/** Whether all configured global concurrency slots are currently occupied. */
|
|
134
|
+
isConcurrencyMaxed(queue) {
|
|
135
|
+
const limiter = this.concurrencyLimiters.get(queue);
|
|
136
|
+
return limiter ? limiter.getActive() >= limiter.getLimit() : false;
|
|
137
|
+
}
|
|
110
138
|
// ============ Queue Management ============
|
|
111
139
|
/** Get all queue names with state */
|
|
112
140
|
getQueueNames() {
|
|
@@ -63,7 +63,8 @@ export declare class Shard {
|
|
|
63
63
|
registerUniqueKey(queue: string, key: string, jobId: JobId): void;
|
|
64
64
|
registerUniqueKeyWithTtl(queue: string, key: string, jobId: JobId, ttl?: number): void;
|
|
65
65
|
extendUniqueKeyTtl(queue: string, key: string, ttl: number): boolean;
|
|
66
|
-
releaseUniqueKey(queue: string, key: string):
|
|
66
|
+
releaseUniqueKey(queue: string, key: string): boolean;
|
|
67
|
+
releaseUniqueKeyIfOwned(queue: string, key: string, ownerId: JobId): boolean;
|
|
67
68
|
cleanExpiredUniqueKeys(): number;
|
|
68
69
|
get uniqueKeys(): Map<string, Map<string, UniqueKeyEntry>>;
|
|
69
70
|
isGroupActive(queue: string, groupId: string): boolean;
|
|
@@ -73,13 +74,20 @@ export declare class Shard {
|
|
|
73
74
|
clearRateLimit(queue: string): void;
|
|
74
75
|
expireRateLimitIfNeeded(queue: string): void;
|
|
75
76
|
tryAcquireRateLimit(queue: string): boolean;
|
|
77
|
+
getRateLimit(queue: string): {
|
|
78
|
+
max: number;
|
|
79
|
+
duration: number;
|
|
80
|
+
} | null;
|
|
81
|
+
getRateLimitTtl(queue: string, maxJobs?: number): number;
|
|
76
82
|
setConcurrency(queue: string, limit: number): void;
|
|
77
83
|
clearConcurrency(queue: string): void;
|
|
78
84
|
tryAcquireConcurrency(queue: string): boolean;
|
|
79
85
|
releaseConcurrency(queue: string): void;
|
|
86
|
+
getConcurrency(queue: string): number | null;
|
|
87
|
+
isConcurrencyMaxed(queue: string): boolean;
|
|
80
88
|
get queueState(): Map<string, QueueState>;
|
|
81
89
|
clearQueueLimiters(queue: string): void;
|
|
82
|
-
releaseJobResources(queue: string, uniqueKey: string | null, groupId: string | null): void;
|
|
90
|
+
releaseJobResources(queue: string, uniqueKey: string | null, groupId: string | null, ownerId?: JobId): void;
|
|
83
91
|
get waitingDeps(): Map<JobId, Job>;
|
|
84
92
|
get dependencyIndex(): Map<JobId, Set<JobId>>;
|
|
85
93
|
get waitingChildren(): Map<JobId, Job>;
|
|
@@ -114,7 +114,10 @@ export class Shard {
|
|
|
114
114
|
return this.uniqueKeyManager.extendTtl(queue, key, ttl);
|
|
115
115
|
}
|
|
116
116
|
releaseUniqueKey(queue, key) {
|
|
117
|
-
this.uniqueKeyManager.release(queue, key);
|
|
117
|
+
return this.uniqueKeyManager.release(queue, key);
|
|
118
|
+
}
|
|
119
|
+
releaseUniqueKeyIfOwned(queue, key, ownerId) {
|
|
120
|
+
return this.uniqueKeyManager.releaseIfOwned(queue, key, ownerId);
|
|
118
121
|
}
|
|
119
122
|
cleanExpiredUniqueKeys() {
|
|
120
123
|
return this.uniqueKeyManager.cleanExpired();
|
|
@@ -150,6 +153,12 @@ export class Shard {
|
|
|
150
153
|
tryAcquireRateLimit(queue) {
|
|
151
154
|
return this.limiterManager.tryAcquireRateLimit(queue);
|
|
152
155
|
}
|
|
156
|
+
getRateLimit(queue) {
|
|
157
|
+
return this.limiterManager.getRateLimit(queue);
|
|
158
|
+
}
|
|
159
|
+
getRateLimitTtl(queue, maxJobs) {
|
|
160
|
+
return this.limiterManager.getRateLimitTtl(queue, maxJobs);
|
|
161
|
+
}
|
|
153
162
|
setConcurrency(queue, limit) {
|
|
154
163
|
this.limiterManager.setConcurrency(queue, limit);
|
|
155
164
|
}
|
|
@@ -162,6 +171,12 @@ export class Shard {
|
|
|
162
171
|
releaseConcurrency(queue) {
|
|
163
172
|
this.limiterManager.releaseConcurrency(queue);
|
|
164
173
|
}
|
|
174
|
+
getConcurrency(queue) {
|
|
175
|
+
return this.limiterManager.getConcurrency(queue);
|
|
176
|
+
}
|
|
177
|
+
isConcurrencyMaxed(queue) {
|
|
178
|
+
return this.limiterManager.isConcurrencyMaxed(queue);
|
|
179
|
+
}
|
|
165
180
|
get queueState() {
|
|
166
181
|
return this.limiterManager.getStateMap();
|
|
167
182
|
}
|
|
@@ -169,9 +184,13 @@ export class Shard {
|
|
|
169
184
|
this.limiterManager.deleteQueue(queue);
|
|
170
185
|
}
|
|
171
186
|
// ============ Resource Release ============
|
|
172
|
-
releaseJobResources(queue, uniqueKey, groupId) {
|
|
173
|
-
if (uniqueKey)
|
|
174
|
-
|
|
187
|
+
releaseJobResources(queue, uniqueKey, groupId, ownerId) {
|
|
188
|
+
if (uniqueKey) {
|
|
189
|
+
if (ownerId)
|
|
190
|
+
this.releaseUniqueKeyIfOwned(queue, uniqueKey, ownerId);
|
|
191
|
+
else
|
|
192
|
+
this.releaseUniqueKey(queue, uniqueKey);
|
|
193
|
+
}
|
|
175
194
|
if (groupId)
|
|
176
195
|
this.releaseGroup(queue, groupId);
|
|
177
196
|
this.releaseConcurrency(queue);
|
|
@@ -21,7 +21,9 @@ export declare class UniqueKeyManager {
|
|
|
21
21
|
/** Extend TTL for an existing unique key */
|
|
22
22
|
extendTtl(queue: string, key: string, ttl: number): boolean;
|
|
23
23
|
/** Release unique key */
|
|
24
|
-
release(queue: string, key: string):
|
|
24
|
+
release(queue: string, key: string): boolean;
|
|
25
|
+
/** Release a key only when it is still owned by the given job. */
|
|
26
|
+
releaseIfOwned(queue: string, key: string, ownerId: JobId): boolean;
|
|
25
27
|
/** Clean expired unique keys (call periodically) */
|
|
26
28
|
cleanExpired(): number;
|
|
27
29
|
/** Clear all keys for a queue */
|
|
@@ -59,7 +59,15 @@ export class UniqueKeyManager {
|
|
|
59
59
|
}
|
|
60
60
|
/** Release unique key */
|
|
61
61
|
release(queue, key) {
|
|
62
|
-
this.keys.get(queue)?.delete(key);
|
|
62
|
+
return this.keys.get(queue)?.delete(key) ?? false;
|
|
63
|
+
}
|
|
64
|
+
/** Release a key only when it is still owned by the given job. */
|
|
65
|
+
releaseIfOwned(queue, key, ownerId) {
|
|
66
|
+
const queueKeys = this.keys.get(queue);
|
|
67
|
+
const entry = queueKeys?.get(key);
|
|
68
|
+
if (!entry || entry.jobId !== ownerId)
|
|
69
|
+
return false;
|
|
70
|
+
return queueKeys?.delete(key) ?? false;
|
|
63
71
|
}
|
|
64
72
|
/** Clean expired unique keys (call periodically) */
|
|
65
73
|
cleanExpired() {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type { JobInput, JobState } from './job';
|
|
6
6
|
import type { AtomicFlowJobInput } from './flow';
|
|
7
7
|
import type { CronJobOptions } from './cron';
|
|
8
|
+
import type { DlqFilter } from './dlq';
|
|
8
9
|
/** Base command interface */
|
|
9
10
|
interface BaseCommand {
|
|
10
11
|
readonly cmd: string;
|
|
@@ -143,6 +144,7 @@ export interface GetJobsCommand extends BaseCommand {
|
|
|
143
144
|
readonly state?: JobState | JobState[];
|
|
144
145
|
readonly limit?: number;
|
|
145
146
|
readonly offset?: number;
|
|
147
|
+
readonly asc?: boolean;
|
|
146
148
|
}
|
|
147
149
|
export interface GetJobCountsCommand extends BaseCommand {
|
|
148
150
|
readonly cmd: 'GetJobCounts';
|
|
@@ -238,6 +240,11 @@ export interface DlqCommand extends BaseCommand {
|
|
|
238
240
|
readonly cmd: 'Dlq';
|
|
239
241
|
readonly queue: string;
|
|
240
242
|
readonly count?: number;
|
|
243
|
+
readonly filter?: DlqFilter;
|
|
244
|
+
}
|
|
245
|
+
export interface GetDlqStatsCommand extends BaseCommand {
|
|
246
|
+
readonly cmd: 'GetDlqStats';
|
|
247
|
+
readonly queue: string;
|
|
241
248
|
}
|
|
242
249
|
export interface RetryDlqCommand extends BaseCommand {
|
|
243
250
|
readonly cmd: 'RetryDlq';
|
|
@@ -245,6 +252,7 @@ export interface RetryDlqCommand extends BaseCommand {
|
|
|
245
252
|
readonly jobId?: string;
|
|
246
253
|
/** Cap the number of DLQ entries retried (omit = retry all). #111-class. */
|
|
247
254
|
readonly count?: number;
|
|
255
|
+
readonly filter?: DlqFilter;
|
|
248
256
|
}
|
|
249
257
|
export interface PurgeDlqCommand extends BaseCommand {
|
|
250
258
|
readonly cmd: 'PurgeDlq';
|
|
@@ -254,6 +262,31 @@ export interface RetryCompletedCommand extends BaseCommand {
|
|
|
254
262
|
readonly cmd: 'RetryCompleted';
|
|
255
263
|
readonly queue: string;
|
|
256
264
|
readonly id?: string;
|
|
265
|
+
readonly count?: number;
|
|
266
|
+
readonly timestamp?: number;
|
|
267
|
+
}
|
|
268
|
+
export interface GetQueueLimitsCommand extends BaseCommand {
|
|
269
|
+
readonly cmd: 'GetQueueLimits';
|
|
270
|
+
readonly queue: string;
|
|
271
|
+
readonly maxJobs?: number;
|
|
272
|
+
}
|
|
273
|
+
export interface GetDeduplicationJobIdCommand extends BaseCommand {
|
|
274
|
+
readonly cmd: 'GetDeduplicationJobId';
|
|
275
|
+
readonly queue: string;
|
|
276
|
+
readonly deduplicationId: string;
|
|
277
|
+
}
|
|
278
|
+
export interface RemoveDeduplicationKeyCommand extends BaseCommand {
|
|
279
|
+
readonly cmd: 'RemoveDeduplicationKey';
|
|
280
|
+
readonly queue: string;
|
|
281
|
+
readonly deduplicationId: string;
|
|
282
|
+
}
|
|
283
|
+
export interface RemoveJobDeduplicationKeyCommand extends BaseCommand {
|
|
284
|
+
readonly cmd: 'RemoveJobDeduplicationKey';
|
|
285
|
+
readonly id: string;
|
|
286
|
+
}
|
|
287
|
+
export interface MoveToWaitingChildrenCommand extends BaseCommand {
|
|
288
|
+
readonly cmd: 'MoveToWaitingChildren';
|
|
289
|
+
readonly id: string;
|
|
257
290
|
}
|
|
258
291
|
export interface RateLimitCommand extends BaseCommand {
|
|
259
292
|
readonly cmd: 'RateLimit';
|
|
@@ -516,7 +549,7 @@ export interface HelloCommand extends BaseCommand {
|
|
|
516
549
|
readonly capabilities?: 'pipelining'[];
|
|
517
550
|
}
|
|
518
551
|
/** Union of all commands */
|
|
519
|
-
export type Command = PushCommand | PushBatchCommand | PushFlowCommand | PullCommand | PullBatchCommand | AckCommand | AckBatchCommand | FailCommand | GetJobCommand | GetStateCommand | GetResultCommand | GetJobsCommand | GetJobCountsCommand | GetCountsPerPriorityCommand | GetJobByCustomIdCommand | CountCommand | GetProgressCommand | CancelCommand | ProgressCommand | UpdateCommand | ChangePriorityCommand | PromoteCommand | WaitJobCommand | MoveToDelayedCommand | DiscardCommand | PauseCommand | ResumeCommand | IsPausedCommand | DrainCommand | ObliterateCommand | ListQueuesCommand | CleanCommand | DlqCommand | RetryDlqCommand | PurgeDlqCommand | RetryCompletedCommand | RateLimitCommand | SetConcurrencyCommand | RateLimitClearCommand | ClearConcurrencyCommand | SetStallConfigCommand | GetStallConfigCommand | SetDlqConfigCommand | GetDlqConfigCommand | CronCommand | CronDeleteCommand | CronListCommand | AddLogCommand | GetLogsCommand | HeartbeatCommand | JobHeartbeatCommand | JobHeartbeatBatchCommand | PingCommand | RegisterWorkerCommand | UnregisterWorkerCommand | ListWorkersCommand | AddWebhookCommand | RemoveWebhookCommand | ListWebhooksCommand | StatsCommand | MetricsCommand | PrometheusCommand | CronGetCommand | GetChildrenValuesCommand | StorageStatusCommand | ClearLogsCommand | ExtendLockCommand | ExtendLocksCommand | ChangeDelayCommand | SetWebhookEnabledCommand | CompactMemoryCommand | UpdateParentCommand | MoveToWaitCommand | PromoteJobsCommand | DashboardOverviewCommand | DashboardQueuesCommand | DashboardQueueCommand | AuthCommand | GetFailedChildrenValuesCommand | GetIgnoredChildrenFailuresCommand | RemoveChildDependencyCommand | RemoveUnprocessedChildrenCommand | HelloCommand;
|
|
552
|
+
export type Command = PushCommand | PushBatchCommand | PushFlowCommand | PullCommand | PullBatchCommand | AckCommand | AckBatchCommand | FailCommand | GetJobCommand | GetStateCommand | GetResultCommand | GetJobsCommand | GetJobCountsCommand | GetCountsPerPriorityCommand | GetJobByCustomIdCommand | CountCommand | GetProgressCommand | CancelCommand | ProgressCommand | UpdateCommand | ChangePriorityCommand | PromoteCommand | WaitJobCommand | MoveToDelayedCommand | DiscardCommand | PauseCommand | ResumeCommand | IsPausedCommand | DrainCommand | ObliterateCommand | ListQueuesCommand | CleanCommand | DlqCommand | GetDlqStatsCommand | RetryDlqCommand | PurgeDlqCommand | RetryCompletedCommand | GetQueueLimitsCommand | GetDeduplicationJobIdCommand | RemoveDeduplicationKeyCommand | RemoveJobDeduplicationKeyCommand | MoveToWaitingChildrenCommand | RateLimitCommand | SetConcurrencyCommand | RateLimitClearCommand | ClearConcurrencyCommand | SetStallConfigCommand | GetStallConfigCommand | SetDlqConfigCommand | GetDlqConfigCommand | CronCommand | CronDeleteCommand | CronListCommand | AddLogCommand | GetLogsCommand | HeartbeatCommand | JobHeartbeatCommand | JobHeartbeatBatchCommand | PingCommand | RegisterWorkerCommand | UnregisterWorkerCommand | ListWorkersCommand | AddWebhookCommand | RemoveWebhookCommand | ListWebhooksCommand | StatsCommand | MetricsCommand | PrometheusCommand | CronGetCommand | GetChildrenValuesCommand | StorageStatusCommand | ClearLogsCommand | ExtendLockCommand | ExtendLocksCommand | ChangeDelayCommand | SetWebhookEnabledCommand | CompactMemoryCommand | UpdateParentCommand | MoveToWaitCommand | PromoteJobsCommand | DashboardOverviewCommand | DashboardQueuesCommand | DashboardQueueCommand | AuthCommand | GetFailedChildrenValuesCommand | GetIgnoredChildrenFailuresCommand | RemoveChildDependencyCommand | RemoveUnprocessedChildrenCommand | HelloCommand;
|
|
520
553
|
/** Extract command type */
|
|
521
554
|
export type CommandType = Command['cmd'];
|
|
522
555
|
export {};
|
|
@@ -29,6 +29,11 @@ export declare class RateLimiter {
|
|
|
29
29
|
private refill;
|
|
30
30
|
/** Get current token count */
|
|
31
31
|
getTokens(): number;
|
|
32
|
+
/**
|
|
33
|
+
* Milliseconds until the bucket can admit another job. When `maxJobs` is
|
|
34
|
+
* provided, report 0 until at least that many tokens have been consumed.
|
|
35
|
+
*/
|
|
36
|
+
getTtl(maxJobs?: number): number;
|
|
32
37
|
}
|
|
33
38
|
/** Concurrency limiter */
|
|
34
39
|
export declare class ConcurrencyLimiter {
|
|
@@ -48,6 +48,19 @@ export class RateLimiter {
|
|
|
48
48
|
this.refill();
|
|
49
49
|
return this.tokens;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Milliseconds until the bucket can admit another job. When `maxJobs` is
|
|
53
|
+
* provided, report 0 until at least that many tokens have been consumed.
|
|
54
|
+
*/
|
|
55
|
+
getTtl(maxJobs) {
|
|
56
|
+
const tokens = this.getTokens();
|
|
57
|
+
const consumed = this.capacity - tokens;
|
|
58
|
+
if (maxJobs !== undefined && consumed < maxJobs)
|
|
59
|
+
return 0;
|
|
60
|
+
if (tokens >= 1 || this.refillRate <= 0)
|
|
61
|
+
return 0;
|
|
62
|
+
return Math.ceil(((1 - tokens) / this.refillRate) * 1000);
|
|
63
|
+
}
|
|
51
64
|
}
|
|
52
65
|
/** Concurrency limiter */
|
|
53
66
|
export class ConcurrencyLimiter {
|
|
@@ -97,6 +97,8 @@ export declare class SqliteStorage {
|
|
|
97
97
|
*/
|
|
98
98
|
private flushIfBuffered;
|
|
99
99
|
markActive(jobId: JobId, startedAt: number, timeline?: JobTimelineEntry[]): void;
|
|
100
|
+
/** Persist a manually parked parent so recovery does not requeue it. */
|
|
101
|
+
markWaitingChildren(jobId: JobId, timeline?: JobTimelineEntry[]): void;
|
|
100
102
|
markCompleted(jobId: JobId, completedAt: number, timeline?: JobTimelineEntry[]): void;
|
|
101
103
|
markFailed(job: Job, error: string | null): void;
|
|
102
104
|
/** Save DLQ entry with full metadata */
|
|
@@ -122,6 +124,8 @@ export declare class SqliteStorage {
|
|
|
122
124
|
deleteJob(jobId: JobId): void;
|
|
123
125
|
/** Update a job's data blob (e.g. after adding __parentId) */
|
|
124
126
|
updateJobData(jobId: JobId, data: unknown): void;
|
|
127
|
+
/** Clear a released deduplication key so recovery cannot resurrect it. */
|
|
128
|
+
clearJobUniqueKey(jobId: JobId): void;
|
|
125
129
|
/** Persist priority ordering, including its LIFO tie-break, across recovery. */
|
|
126
130
|
updateJobPriority(jobId: JobId, priority: number, lifo: boolean): void;
|
|
127
131
|
/** Persist progress and its heartbeat as one active-job mutation. */
|
|
@@ -289,6 +289,15 @@ export class SqliteStorage {
|
|
|
289
289
|
.run('active', startedAt, timeline && timeline.length > 0 ? pack(timeline) : null, jobId);
|
|
290
290
|
});
|
|
291
291
|
}
|
|
292
|
+
/** Persist a manually parked parent so recovery does not requeue it. */
|
|
293
|
+
markWaitingChildren(jobId, timeline) {
|
|
294
|
+
this.flushIfBuffered(jobId);
|
|
295
|
+
this.safeWrite(() => {
|
|
296
|
+
this.db
|
|
297
|
+
.prepare('UPDATE jobs SET state = ?, started_at = NULL, timeline = ? WHERE id = ?')
|
|
298
|
+
.run('waiting-children', timeline && timeline.length > 0 ? pack(timeline) : null, jobId);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
292
301
|
markCompleted(jobId, completedAt, timeline) {
|
|
293
302
|
this.flushIfBuffered(jobId);
|
|
294
303
|
this.safeWrite(() => {
|
|
@@ -432,6 +441,13 @@ export class SqliteStorage {
|
|
|
432
441
|
this.db.prepare('UPDATE jobs SET data = ? WHERE id = ?').run(pack(data), jobId);
|
|
433
442
|
});
|
|
434
443
|
}
|
|
444
|
+
/** Clear a released deduplication key so recovery cannot resurrect it. */
|
|
445
|
+
clearJobUniqueKey(jobId) {
|
|
446
|
+
this.flushIfBuffered(jobId);
|
|
447
|
+
this.safeWrite(() => {
|
|
448
|
+
this.db.prepare('UPDATE jobs SET unique_key = NULL WHERE id = ?').run(jobId);
|
|
449
|
+
});
|
|
450
|
+
}
|
|
435
451
|
/** Persist priority ordering, including its LIFO tie-break, across recovery. */
|
|
436
452
|
updateJobPriority(jobId, priority, lifo) {
|
|
437
453
|
this.flushIfBuffered(jobId);
|
|
@@ -6,7 +6,8 @@ import { handlePush, handlePushBatch, handlePull, handlePullBatch, handleAck, ha
|
|
|
6
6
|
import { handlePushFlow } from './handlers/flow';
|
|
7
7
|
import { handleGetJob, handleGetState, handleGetResult, handleGetJobCounts, handleGetCountsPerPriority, handleGetJobByCustomId, handleGetJobs, handleGetChildrenValues, } from './handlers/query';
|
|
8
8
|
import { handleCancel, handleProgress, handleGetProgress, handlePause, handleResume, handleDrain, handleStats, handleMetrics, handleStorageStatus, } from './handlers/management';
|
|
9
|
-
import { handleDlq, handleRetryDlq, handlePurgeDlq, handleRetryCompleted } from './handlers/dlq';
|
|
9
|
+
import { handleDlq, handleGetDlqStats, handleRetryDlq, handlePurgeDlq, handleRetryCompleted, } from './handlers/dlq';
|
|
10
|
+
import { handleGetQueueLimits, handleGetDeduplicationJobId, handleRemoveDeduplicationKey, handleRemoveJobDeduplicationKey, handleMoveToWaitingChildren, } from './handlers/introspection';
|
|
10
11
|
import { handleCron, handleCronGet, handleCronDelete, handleCronList } from './handlers/cron';
|
|
11
12
|
import { handleUpdate, handleUpdateParent, handleChangePriority, handlePromote, handleMoveToDelayed, handleDiscard, handleWaitJob, handleIsPaused, handleObliterate, handleListQueues, handleClean, handleCount, handleRateLimit, handleRateLimitClear, handleSetConcurrency, handleClearConcurrency, handleChangeDelay, handleMoveToWait, handlePromoteJobs, handleSetStallConfig, handleGetStallConfig, handleSetDlqConfig, handleGetDlqConfig, handleGetFailedChildrenValues, handleGetIgnoredChildrenFailures, handleRemoveChildDependency, handleRemoveUnprocessedChildren, } from './handlers/advanced';
|
|
12
13
|
import { handleDashboardOverview, handleDashboardQueues, handleDashboardQueue, } from './handlers/dashboard';
|
|
@@ -59,6 +60,10 @@ export async function routeQueryCommand(cmd, ctx, reqId) {
|
|
|
59
60
|
return handleGetProgress(cmd, ctx, reqId);
|
|
60
61
|
case 'GetChildrenValues':
|
|
61
62
|
return handleGetChildrenValues(cmd, ctx, reqId);
|
|
63
|
+
case 'GetQueueLimits':
|
|
64
|
+
return handleGetQueueLimits(cmd, ctx, reqId);
|
|
65
|
+
case 'GetDeduplicationJobId':
|
|
66
|
+
return handleGetDeduplicationJobId(cmd, ctx, reqId);
|
|
62
67
|
default:
|
|
63
68
|
return null;
|
|
64
69
|
}
|
|
@@ -103,6 +108,12 @@ export async function routeManagementCommand(cmd, ctx, reqId) {
|
|
|
103
108
|
return handleRemoveChildDependency(cmd, ctx, reqId);
|
|
104
109
|
case 'RemoveUnprocessedChildren':
|
|
105
110
|
return handleRemoveUnprocessedChildren(cmd, ctx, reqId);
|
|
111
|
+
case 'RemoveDeduplicationKey':
|
|
112
|
+
return handleRemoveDeduplicationKey(cmd, ctx, reqId);
|
|
113
|
+
case 'RemoveJobDeduplicationKey':
|
|
114
|
+
return handleRemoveJobDeduplicationKey(cmd, ctx, reqId);
|
|
115
|
+
case 'MoveToWaitingChildren':
|
|
116
|
+
return handleMoveToWaitingChildren(cmd, ctx, reqId);
|
|
106
117
|
default:
|
|
107
118
|
return null;
|
|
108
119
|
}
|
|
@@ -135,6 +146,8 @@ export function routeDlqCommand(cmd, ctx, reqId) {
|
|
|
135
146
|
switch (cmd.cmd) {
|
|
136
147
|
case 'Dlq':
|
|
137
148
|
return handleDlq(cmd, ctx, reqId);
|
|
149
|
+
case 'GetDlqStats':
|
|
150
|
+
return handleGetDlqStats(cmd, ctx, reqId);
|
|
138
151
|
case 'RetryDlq':
|
|
139
152
|
return handleRetryDlq(cmd, ctx, reqId);
|
|
140
153
|
case 'PurgeDlq':
|
|
@@ -9,6 +9,9 @@ import type { HandlerContext } from '../types';
|
|
|
9
9
|
export declare function handleDlq(cmd: Extract<Command, {
|
|
10
10
|
cmd: 'Dlq';
|
|
11
11
|
}>, ctx: HandlerContext, reqId?: string): Response;
|
|
12
|
+
export declare function handleGetDlqStats(cmd: Extract<Command, {
|
|
13
|
+
cmd: 'GetDlqStats';
|
|
14
|
+
}>, ctx: HandlerContext, reqId?: string): Response;
|
|
12
15
|
/** Handle RetryDlq command - retry DLQ jobs */
|
|
13
16
|
export declare function handleRetryDlq(cmd: Extract<Command, {
|
|
14
17
|
cmd: 'RetryDlq';
|
|
@@ -6,14 +6,25 @@ import * as resp from '../../../domain/types/response';
|
|
|
6
6
|
import { jobId } from '../../../domain/types/job';
|
|
7
7
|
/** Handle Dlq command - get DLQ jobs */
|
|
8
8
|
export function handleDlq(cmd, ctx, reqId) {
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const entries = ctx.queueManager.getDlqEntries(cmd.queue, cmd.filter);
|
|
10
|
+
const selected = cmd.count === undefined ? entries : entries.slice(0, Math.max(0, cmd.count));
|
|
11
|
+
return {
|
|
12
|
+
ok: true,
|
|
13
|
+
jobs: selected.map((entry) => entry.job),
|
|
14
|
+
entries: selected,
|
|
15
|
+
reqId,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function handleGetDlqStats(cmd, ctx, reqId) {
|
|
19
|
+
return resp.data({ stats: ctx.queueManager.getDlqStats(cmd.queue) }, reqId);
|
|
11
20
|
}
|
|
12
21
|
/** Handle RetryDlq command - retry DLQ jobs */
|
|
13
22
|
export function handleRetryDlq(cmd, ctx, reqId) {
|
|
14
23
|
const jid = cmd.jobId ? jobId(cmd.jobId) : undefined;
|
|
15
24
|
// #111-class: honour the caller's `count` cap instead of retrying the whole DLQ.
|
|
16
|
-
const count =
|
|
25
|
+
const count = cmd.filter
|
|
26
|
+
? ctx.queueManager.retryDlqByFilter(cmd.queue, cmd.filter)
|
|
27
|
+
: ctx.queueManager.retryDlq(cmd.queue, jid, cmd.count);
|
|
17
28
|
if (count > 0) {
|
|
18
29
|
const event = jid ? 'dlq:retried' : 'dlq:retry-all';
|
|
19
30
|
const data = { queue: cmd.queue, count };
|
|
@@ -33,6 +44,9 @@ export function handlePurgeDlq(cmd, ctx, reqId) {
|
|
|
33
44
|
/** Handle RetryCompleted command - retry completed jobs */
|
|
34
45
|
export function handleRetryCompleted(cmd, ctx, reqId) {
|
|
35
46
|
const jid = cmd.id ? jobId(cmd.id) : undefined;
|
|
36
|
-
const count = ctx.queueManager.retryCompleted(cmd.queue, jid
|
|
47
|
+
const count = ctx.queueManager.retryCompleted(cmd.queue, jid, {
|
|
48
|
+
limit: cmd.count,
|
|
49
|
+
timestamp: cmd.timestamp,
|
|
50
|
+
});
|
|
37
51
|
return { ok: true, count, reqId };
|
|
38
52
|
}
|