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.
Files changed (63) hide show
  1. package/README.md +14 -2
  2. package/dist/application/backgroundTasks.js +37 -7
  3. package/dist/application/contextFactory.js +5 -0
  4. package/dist/application/dlqManager.d.ts +6 -0
  5. package/dist/application/dlqManager.js +35 -14
  6. package/dist/application/lockManager.js +22 -8
  7. package/dist/application/operations/customId.d.ts +31 -0
  8. package/dist/application/operations/customId.js +59 -0
  9. package/dist/application/operations/jobClaim.d.ts +17 -0
  10. package/dist/application/operations/jobClaim.js +20 -0
  11. package/dist/application/operations/jobManagement.d.ts +4 -5
  12. package/dist/application/operations/jobManagement.js +15 -116
  13. package/dist/application/operations/jobMoveOperations.d.ts +9 -0
  14. package/dist/application/operations/jobMoveOperations.js +94 -0
  15. package/dist/application/operations/jobStateTransitions.js +3 -0
  16. package/dist/application/operations/pull.js +5 -0
  17. package/dist/application/operations/push.js +8 -103
  18. package/dist/application/operations/pushInsert.d.ts +12 -0
  19. package/dist/application/operations/pushInsert.js +25 -0
  20. package/dist/application/operations/pushLocks.d.ts +14 -0
  21. package/dist/application/operations/pushLocks.js +43 -0
  22. package/dist/application/operations/queueControl.d.ts +1 -1
  23. package/dist/application/operations/queueControl.js +1 -1
  24. package/dist/application/queueManager.d.ts +2 -2
  25. package/dist/application/queueManager.js +30 -10
  26. package/dist/application/stallDetection.js +17 -8
  27. package/dist/client/queue/dlq.d.ts +23 -1
  28. package/dist/client/queue/dlq.js +75 -0
  29. package/dist/client/queue/helpers.js +2 -0
  30. package/dist/client/queue/jobProxy.d.ts +1 -1
  31. package/dist/client/queue/operations/control.d.ts +17 -0
  32. package/dist/client/queue/operations/control.js +41 -0
  33. package/dist/client/queue/operations/counts.d.ts +1 -0
  34. package/dist/client/queue/operations/counts.js +3 -0
  35. package/dist/client/queue/queue.d.ts +13 -0
  36. package/dist/client/queue/queue.js +39 -0
  37. package/dist/client/queue/rateLimit.d.ts +18 -3
  38. package/dist/client/queue/rateLimit.js +45 -10
  39. package/dist/client/queue/stall.d.ts +2 -0
  40. package/dist/client/queue/stall.js +12 -0
  41. package/dist/domain/queue/dependencyTracker.d.ts +5 -0
  42. package/dist/domain/queue/dependencyTracker.js +20 -0
  43. package/dist/domain/queue/limiterManager.d.ts +14 -2
  44. package/dist/domain/queue/limiterManager.js +32 -5
  45. package/dist/domain/queue/shard.d.ts +3 -2
  46. package/dist/domain/queue/shard.js +11 -2
  47. package/dist/domain/types/command.d.ts +4 -0
  48. package/dist/domain/types/queue.d.ts +4 -0
  49. package/dist/domain/types/queue.js +2 -0
  50. package/dist/infrastructure/persistence/schema.d.ts +2 -2
  51. package/dist/infrastructure/persistence/schema.js +38 -2
  52. package/dist/infrastructure/persistence/sqlite.d.ts +20 -1
  53. package/dist/infrastructure/persistence/sqlite.js +62 -6
  54. package/dist/infrastructure/persistence/sqliteBatch.js +8 -6
  55. package/dist/infrastructure/persistence/sqliteSerializer.d.ts +6 -0
  56. package/dist/infrastructure/persistence/sqliteSerializer.js +9 -1
  57. package/dist/infrastructure/persistence/statements.d.ts +8 -1
  58. package/dist/infrastructure/persistence/statements.js +7 -5
  59. package/dist/infrastructure/scheduler/cronScheduler.d.ts +5 -0
  60. package/dist/infrastructure/scheduler/cronScheduler.js +39 -13
  61. package/dist/infrastructure/server/handlers/advanced.js +11 -2
  62. package/dist/infrastructure/server/httpRouteQueueConfig.js +2 -0
  63. package/package.json +4 -1
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { getSharedManager } from '../manager';
6
6
  import { jobId } from '../../domain/types/job';
7
+ import { createSimpleJob } from './jobProxy';
7
8
  import * as dlqOps from './dlqOps';
8
9
  /** Client-side cache for TCP mode */
9
10
  const tcpDlqConfigCache = new Map();
@@ -18,6 +19,18 @@ export function setDlqConfig(ctx, config) {
18
19
  void ctx.tcp.send({ cmd: 'SetDlqConfig', queue: ctx.name, config });
19
20
  }
20
21
  }
22
+ /** Set DLQ configuration and resolve once the server has applied it. */
23
+ export async function setDlqConfigAsync(ctx, config) {
24
+ if (ctx.embedded) {
25
+ dlqOps.setDlqConfig(ctx.name, config);
26
+ return;
27
+ }
28
+ if (!ctx.tcp)
29
+ return;
30
+ const current = tcpDlqConfigCache.get(ctx.name) ?? {};
31
+ tcpDlqConfigCache.set(ctx.name, { ...current, ...config });
32
+ await ctx.tcp.send({ cmd: 'SetDlqConfig', queue: ctx.name, config });
33
+ }
21
34
  /** Get DLQ configuration */
22
35
  export function getDlqConfig(ctx) {
23
36
  if (ctx.embedded)
@@ -45,6 +58,38 @@ export function getDlq(ctx, filter) {
45
58
  return [];
46
59
  return dlqOps.getDlqEntries(ctx.name, filter);
47
60
  }
61
+ /**
62
+ * Get the dead jobs in the DLQ, working over TCP too (wire command `Dlq`).
63
+ * Returns public Job objects; the wire does not carry DlqEntry metadata
64
+ * (reason, enteredAt, attempts) — that stays embedded-only via getDlq().
65
+ */
66
+ export async function getDlqJobsAsync(ctx, count) {
67
+ let raw;
68
+ if (ctx.embedded) {
69
+ raw = getSharedManager().getDlq(ctx.name, count);
70
+ }
71
+ else if (ctx.tcp) {
72
+ const response = await ctx.tcp.send({ cmd: 'Dlq', queue: ctx.name, count });
73
+ if (!response.ok)
74
+ return [];
75
+ raw = (response.jobs ?? []);
76
+ }
77
+ else {
78
+ return [];
79
+ }
80
+ return raw.map((job) => {
81
+ const name = job.data?.name ?? 'unknown';
82
+ return createSimpleJob(String(job.id), name, job.data, job.createdAt ?? Date.now(), {
83
+ queueName: ctx.name,
84
+ embedded: ctx.embedded,
85
+ tcp: ctx.tcp,
86
+ getJobState: ctx.getJobState,
87
+ removeAsync: ctx.removeAsync,
88
+ retryJob: ctx.retryJob,
89
+ getChildrenValues: ctx.getChildrenValues,
90
+ });
91
+ });
92
+ }
48
93
  /** Get DLQ stats */
49
94
  export function getDlqStats(ctx) {
50
95
  if (!ctx.embedded) {
@@ -67,6 +112,21 @@ export function retryDlq(ctx, id) {
67
112
  void ctx.tcp.send({ cmd: 'RetryDlq', queue: ctx.name, jobId: id });
68
113
  return 0;
69
114
  }
115
+ /**
116
+ * Retry DLQ entries and resolve with the retried count once the server has
117
+ * processed it. The fire-and-forget retryDlq() always returns 0 over TCP and
118
+ * discards the server's count.
119
+ */
120
+ export async function retryDlqAsync(ctx, id) {
121
+ if (ctx.embedded)
122
+ return dlqOps.retryDlqEmbedded(ctx.name, id);
123
+ if (!ctx.tcp)
124
+ return 0;
125
+ const response = await ctx.tcp.send({ cmd: 'RetryDlq', queue: ctx.name, jobId: id });
126
+ if (!response.ok)
127
+ return 0;
128
+ return (response.count ?? 0);
129
+ }
70
130
  /** Retry DLQ entries by filter */
71
131
  export function retryDlqByFilter(ctx, filter) {
72
132
  if (!ctx.embedded)
@@ -81,6 +141,21 @@ export function purgeDlq(ctx) {
81
141
  void ctx.tcp.send({ cmd: 'PurgeDlq', queue: ctx.name });
82
142
  return 0;
83
143
  }
144
+ /**
145
+ * Purge the DLQ and resolve with the purged count once the server has
146
+ * processed it. The fire-and-forget purgeDlq() always returns 0 over TCP;
147
+ * purge-then-assert-empty patterns need this variant.
148
+ */
149
+ export async function purgeDlqAsync(ctx) {
150
+ if (ctx.embedded)
151
+ return dlqOps.purgeDlqEmbedded(ctx.name);
152
+ if (!ctx.tcp)
153
+ return 0;
154
+ const response = await ctx.tcp.send({ cmd: 'PurgeDlq', queue: ctx.name });
155
+ if (!response.ok)
156
+ return 0;
157
+ return (response.count ?? 0);
158
+ }
84
159
  /** Retry completed job */
85
160
  export function retryCompleted(ctx, id) {
86
161
  if (ctx.embedded) {
@@ -19,6 +19,8 @@ export function getDlqContext(manager) {
19
19
  return {
20
20
  shards: getShards(manager),
21
21
  jobIndex: manager.getJobIndex(),
22
+ jobResults: manager.jobResults,
23
+ jobLogs: manager.jobLogs,
22
24
  // #110-class: without storage, retryDlqByFilter's deleteDlqEntry/insertJob
23
25
  // silently no-op — filtered retries were never persisted in embedded mode
24
26
  // (dlq rows resurrected the jobs into the DLQ on restart).
@@ -29,7 +29,7 @@ export interface JobReflectionMeta {
29
29
  }
30
30
  /** Create a full Job proxy with TCP methods */
31
31
  export declare function createJobProxy<T>(id: string, name: string, data: T, ctx: JobProxyContext, meta?: JobReflectionMeta): Job<T>;
32
- interface SimpleJobContext {
32
+ export interface SimpleJobContext {
33
33
  queueName: string;
34
34
  /** Execution mode — determines whether to use embedded manager or TCP */
35
35
  embedded?: boolean;
@@ -10,12 +10,29 @@ interface ControlContext {
10
10
  }
11
11
  /** Pause the queue */
12
12
  export declare function pause(ctx: ControlContext): void;
13
+ /** Pause the queue and resolve once the server has processed it. */
14
+ export declare function pauseAsync(ctx: ControlContext): Promise<void>;
13
15
  /** Resume the queue */
14
16
  export declare function resume(ctx: ControlContext): void;
17
+ /** Resume the queue and resolve once the server has processed it. */
18
+ export declare function resumeAsync(ctx: ControlContext): Promise<void>;
15
19
  /** Drain the queue (remove all waiting jobs) */
16
20
  export declare function drain(ctx: ControlContext): void;
21
+ /**
22
+ * Drain the queue and resolve with the number of removed jobs once the
23
+ * server has processed it. Same ordering guarantee as obliterateAsync():
24
+ * jobs added after resolution cannot be caught by a late-arriving drain.
25
+ */
26
+ export declare function drainAsync(ctx: ControlContext): Promise<number>;
17
27
  /** Obliterate the queue (remove all jobs and data) */
18
28
  export declare function obliterate(ctx: ControlContext): void;
29
+ /**
30
+ * Obliterate the queue and resolve once the server has processed it.
31
+ * The fire-and-forget obliterate() gives no ordering guarantee over a
32
+ * multi-connection pool: a PUSH sent right after it can be processed first
33
+ * and then wiped. Await this variant before enqueuing follow-up jobs.
34
+ */
35
+ export declare function obliterateAsync(ctx: ControlContext): Promise<void>;
19
36
  /** Check if queue is paused (sync, embedded only) */
20
37
  export declare function isPaused(ctx: ControlContext): boolean;
21
38
  /** Check if queue is paused (async, works with TCP) */
@@ -10,6 +10,13 @@ export function pause(ctx) {
10
10
  else if (ctx.tcp)
11
11
  void ctx.tcp.send({ cmd: 'Pause', queue: ctx.name });
12
12
  }
13
+ /** Pause the queue and resolve once the server has processed it. */
14
+ export async function pauseAsync(ctx) {
15
+ if (ctx.embedded)
16
+ getSharedManager().pause(ctx.name);
17
+ else if (ctx.tcp)
18
+ await ctx.tcp.send({ cmd: 'Pause', queue: ctx.name });
19
+ }
13
20
  /** Resume the queue */
14
21
  export function resume(ctx) {
15
22
  if (ctx.embedded)
@@ -17,6 +24,13 @@ export function resume(ctx) {
17
24
  else if (ctx.tcp)
18
25
  void ctx.tcp.send({ cmd: 'Resume', queue: ctx.name });
19
26
  }
27
+ /** Resume the queue and resolve once the server has processed it. */
28
+ export async function resumeAsync(ctx) {
29
+ if (ctx.embedded)
30
+ getSharedManager().resume(ctx.name);
31
+ else if (ctx.tcp)
32
+ await ctx.tcp.send({ cmd: 'Resume', queue: ctx.name });
33
+ }
20
34
  /** Drain the queue (remove all waiting jobs) */
21
35
  export function drain(ctx) {
22
36
  if (ctx.embedded)
@@ -24,6 +38,21 @@ export function drain(ctx) {
24
38
  else if (ctx.tcp)
25
39
  void ctx.tcp.send({ cmd: 'Drain', queue: ctx.name });
26
40
  }
41
+ /**
42
+ * Drain the queue and resolve with the number of removed jobs once the
43
+ * server has processed it. Same ordering guarantee as obliterateAsync():
44
+ * jobs added after resolution cannot be caught by a late-arriving drain.
45
+ */
46
+ export async function drainAsync(ctx) {
47
+ if (ctx.embedded)
48
+ return getSharedManager().drain(ctx.name);
49
+ if (!ctx.tcp)
50
+ return 0;
51
+ const response = await ctx.tcp.send({ cmd: 'Drain', queue: ctx.name });
52
+ if (!response.ok)
53
+ return 0;
54
+ return (response.count ?? 0);
55
+ }
27
56
  /** Obliterate the queue (remove all jobs and data) */
28
57
  export function obliterate(ctx) {
29
58
  if (ctx.embedded)
@@ -31,6 +60,18 @@ export function obliterate(ctx) {
31
60
  else if (ctx.tcp)
32
61
  void ctx.tcp.send({ cmd: 'Obliterate', queue: ctx.name });
33
62
  }
63
+ /**
64
+ * Obliterate the queue and resolve once the server has processed it.
65
+ * The fire-and-forget obliterate() gives no ordering guarantee over a
66
+ * multi-connection pool: a PUSH sent right after it can be processed first
67
+ * and then wiped. Await this variant before enqueuing follow-up jobs.
68
+ */
69
+ export async function obliterateAsync(ctx) {
70
+ if (ctx.embedded)
71
+ getSharedManager().obliterate(ctx.name);
72
+ else if (ctx.tcp)
73
+ await ctx.tcp.send({ cmd: 'Obliterate', queue: ctx.name });
74
+ }
34
75
  /** Check if queue is paused (sync, embedded only) */
35
76
  export function isPaused(ctx) {
36
77
  if (!ctx.embedded)
@@ -16,6 +16,7 @@ export interface JobCounts {
16
16
  failed: number;
17
17
  delayed: number;
18
18
  paused: number;
19
+ 'waiting-children': number;
19
20
  }
20
21
  /**
21
22
  * Get job counts.
@@ -30,6 +30,7 @@ export function getJobCounts(ctx) {
30
30
  failed: counts.failed,
31
31
  delayed: counts.delayed,
32
32
  paused: pv.paused,
33
+ 'waiting-children': counts['waiting-children'],
33
34
  };
34
35
  }
35
36
  /** Get job counts (async, works with TCP) */
@@ -47,6 +48,7 @@ export async function getJobCountsAsync(ctx) {
47
48
  failed: 0,
48
49
  delayed: 0,
49
50
  paused: 0,
51
+ 'waiting-children': 0,
50
52
  };
51
53
  }
52
54
  const counts = response.counts;
@@ -58,6 +60,7 @@ export async function getJobCountsAsync(ctx) {
58
60
  failed: counts?.failed ?? 0,
59
61
  delayed: counts?.delayed ?? 0,
60
62
  paused: counts?.paused ?? 0,
63
+ 'waiting-children': counts?.['waiting-children'] ?? 0,
61
64
  };
62
65
  }
63
66
  /** Get waiting job count */
@@ -74,9 +74,13 @@ export declare class Queue<T = unknown> {
74
74
  getCountsPerPriority(): Record<number, number>;
75
75
  getCountsPerPriorityAsync(): Promise<Record<number, number>>;
76
76
  pause(): void;
77
+ pauseAsync(): Promise<void>;
77
78
  resume(): void;
79
+ resumeAsync(): Promise<void>;
78
80
  drain(): void;
81
+ drainAsync(): Promise<number>;
79
82
  obliterate(): void;
83
+ obliterateAsync(): Promise<void>;
80
84
  isPaused(): boolean;
81
85
  isPausedAsync(): Promise<boolean>;
82
86
  waitUntilReady(): Promise<void>;
@@ -106,23 +110,32 @@ export declare class Queue<T = unknown> {
106
110
  changeJobPriority(id: string, opts: ChangePriorityOpts): Promise<void>;
107
111
  extendJobLock(id: string, token: string, duration: number): Promise<number>;
108
112
  setStallConfig(config: Partial<StallConfig>): void;
113
+ setStallConfigAsync(config: Partial<StallConfig>): Promise<void>;
109
114
  getStallConfig(): StallConfig;
110
115
  getStallConfigAsync(): Promise<StallConfig>;
111
116
  setDlqConfig(config: Partial<DlqConfig>): void;
117
+ setDlqConfigAsync(config: Partial<DlqConfig>): Promise<void>;
112
118
  getDlqConfig(): DlqConfig;
113
119
  getDlqConfigAsync(): Promise<DlqConfig>;
114
120
  getDlq(filter?: DlqFilter): DlqEntry<T>[];
121
+ getDlqJobsAsync(count?: number): Promise<Job<T>[]>;
115
122
  getDlqStats(): DlqStats;
116
123
  retryDlq(id?: string): number;
124
+ retryDlqAsync(id?: string): Promise<number>;
117
125
  retryDlqByFilter(filter: DlqFilter): number;
118
126
  purgeDlq(): number;
127
+ purgeDlqAsync(): Promise<number>;
119
128
  retryCompleted(id?: string): number;
120
129
  retryCompletedAsync(id?: string): Promise<number>;
121
130
  setGlobalConcurrency(concurrency: number): void;
131
+ setGlobalConcurrencyAsync(concurrency: number): Promise<void>;
122
132
  removeGlobalConcurrency(): void;
133
+ removeGlobalConcurrencyAsync(): Promise<void>;
123
134
  getGlobalConcurrency(): Promise<number | null>;
124
135
  setGlobalRateLimit(max: number, duration?: number): void;
136
+ setGlobalRateLimitAsync(max: number, duration?: number): Promise<void>;
125
137
  removeGlobalRateLimit(): void;
138
+ removeGlobalRateLimitAsync(): Promise<void>;
126
139
  getGlobalRateLimit(): Promise<{
127
140
  max: number;
128
141
  duration: number;
@@ -262,15 +262,27 @@ export class Queue {
262
262
  pause() {
263
263
  controlOps.pause(this.ctx);
264
264
  }
265
+ pauseAsync() {
266
+ return controlOps.pauseAsync(this.ctx);
267
+ }
265
268
  resume() {
266
269
  controlOps.resume(this.ctx);
267
270
  }
271
+ resumeAsync() {
272
+ return controlOps.resumeAsync(this.ctx);
273
+ }
268
274
  drain() {
269
275
  controlOps.drain(this.ctx);
270
276
  }
277
+ drainAsync() {
278
+ return controlOps.drainAsync(this.ctx);
279
+ }
271
280
  obliterate() {
272
281
  controlOps.obliterate(this.ctx);
273
282
  }
283
+ obliterateAsync() {
284
+ return controlOps.obliterateAsync(this.ctx);
285
+ }
274
286
  isPaused() {
275
287
  return controlOps.isPaused(this.ctx);
276
288
  }
@@ -333,6 +345,9 @@ export class Queue {
333
345
  setStallConfig(config) {
334
346
  stallOps.setStallConfig(this.ctx, config);
335
347
  }
348
+ setStallConfigAsync(config) {
349
+ return stallOps.setStallConfigAsync(this.ctx, config);
350
+ }
336
351
  getStallConfig() {
337
352
  return stallOps.getStallConfig(this.ctx);
338
353
  }
@@ -343,6 +358,9 @@ export class Queue {
343
358
  setDlqConfig(config) {
344
359
  dlqOps.setDlqConfig(this.ctx, config);
345
360
  }
361
+ setDlqConfigAsync(config) {
362
+ return dlqOps.setDlqConfigAsync(this.ctx, config);
363
+ }
346
364
  getDlqConfig() {
347
365
  return dlqOps.getDlqConfig(this.ctx);
348
366
  }
@@ -352,18 +370,27 @@ export class Queue {
352
370
  getDlq(filter) {
353
371
  return dlqOps.getDlq(this.ctx, filter);
354
372
  }
373
+ getDlqJobsAsync(count) {
374
+ return dlqOps.getDlqJobsAsync(this.queryCtx, count);
375
+ }
355
376
  getDlqStats() {
356
377
  return dlqOps.getDlqStats(this.ctx);
357
378
  }
358
379
  retryDlq(id) {
359
380
  return dlqOps.retryDlq(this.ctx, id);
360
381
  }
382
+ retryDlqAsync(id) {
383
+ return dlqOps.retryDlqAsync(this.ctx, id);
384
+ }
361
385
  retryDlqByFilter(filter) {
362
386
  return dlqOps.retryDlqByFilter(this.ctx, filter);
363
387
  }
364
388
  purgeDlq() {
365
389
  return dlqOps.purgeDlq(this.ctx);
366
390
  }
391
+ purgeDlqAsync() {
392
+ return dlqOps.purgeDlqAsync(this.ctx);
393
+ }
367
394
  retryCompleted(id) {
368
395
  return dlqOps.retryCompleted(this.ctx, id);
369
396
  }
@@ -374,18 +401,30 @@ export class Queue {
374
401
  setGlobalConcurrency(concurrency) {
375
402
  rateLimitOps.setGlobalConcurrency(this.ctx, concurrency);
376
403
  }
404
+ setGlobalConcurrencyAsync(concurrency) {
405
+ return rateLimitOps.setGlobalConcurrencyAsync(this.ctx, concurrency);
406
+ }
377
407
  removeGlobalConcurrency() {
378
408
  rateLimitOps.removeGlobalConcurrency(this.ctx);
379
409
  }
410
+ removeGlobalConcurrencyAsync() {
411
+ return rateLimitOps.removeGlobalConcurrencyAsync(this.ctx);
412
+ }
380
413
  getGlobalConcurrency() {
381
414
  return rateLimitOps.getGlobalConcurrency(this.ctx);
382
415
  }
383
416
  setGlobalRateLimit(max, duration) {
384
417
  rateLimitOps.setGlobalRateLimit(this.ctx, max, duration);
385
418
  }
419
+ setGlobalRateLimitAsync(max, duration) {
420
+ return rateLimitOps.setGlobalRateLimitAsync(this.ctx, max, duration);
421
+ }
386
422
  removeGlobalRateLimit() {
387
423
  rateLimitOps.removeGlobalRateLimit(this.ctx);
388
424
  }
425
+ removeGlobalRateLimitAsync() {
426
+ return rateLimitOps.removeGlobalRateLimitAsync(this.ctx);
427
+ }
389
428
  getGlobalRateLimit() {
390
429
  return rateLimitOps.getGlobalRateLimit(this.ctx);
391
430
  }
@@ -9,20 +9,35 @@ interface RateLimitContext {
9
9
  }
10
10
  /** Set global concurrency limit */
11
11
  export declare function setGlobalConcurrency(ctx: RateLimitContext, concurrency: number): void;
12
+ /** Set global concurrency limit and resolve once the server has applied it. */
13
+ export declare function setGlobalConcurrencyAsync(ctx: RateLimitContext, concurrency: number): Promise<void>;
12
14
  /** Remove global concurrency limit */
13
15
  export declare function removeGlobalConcurrency(ctx: RateLimitContext): void;
16
+ /** Remove global concurrency limit and resolve once the server has applied it. */
17
+ export declare function removeGlobalConcurrencyAsync(ctx: RateLimitContext): Promise<void>;
14
18
  /** Get global concurrency limit */
15
19
  export declare function getGlobalConcurrency(_ctx: RateLimitContext): Promise<number | null>;
16
- /** Set global rate limit */
17
- export declare function setGlobalRateLimit(ctx: RateLimitContext, max: number, _duration?: number): void;
20
+ /**
21
+ * Set global rate limit: `max` jobs per `duration` ms (default 1000ms).
22
+ * The window is honored in both embedded and TCP modes.
23
+ */
24
+ export declare function setGlobalRateLimit(ctx: RateLimitContext, max: number, duration?: number): void;
25
+ /** Set global rate limit and resolve once the server has applied it. */
26
+ export declare function setGlobalRateLimitAsync(ctx: RateLimitContext, max: number, duration?: number): Promise<void>;
18
27
  /** Remove global rate limit */
19
28
  export declare function removeGlobalRateLimit(ctx: RateLimitContext): void;
29
+ /** Remove global rate limit and resolve once the server has applied it. */
30
+ export declare function removeGlobalRateLimitAsync(ctx: RateLimitContext): Promise<void>;
20
31
  /** Get global rate limit */
21
32
  export declare function getGlobalRateLimit(_ctx: RateLimitContext): Promise<{
22
33
  max: number;
23
34
  duration: number;
24
35
  } | null>;
25
- /** Set temporary rate limit with expiration */
36
+ /**
37
+ * Set temporary rate limit with expiration. The expiry lives broker-side
38
+ * (RateLimit `ttl`), so it survives client exit and works identically in
39
+ * embedded and TCP modes — no client timer involved.
40
+ */
26
41
  export declare function rateLimit(ctx: RateLimitContext, expireTimeMs: number): Promise<void>;
27
42
  /** Get rate limit TTL */
28
43
  export declare function getRateLimitTtl(_ctx: RateLimitContext, _maxJobs?: number): Promise<number>;
@@ -11,6 +11,13 @@ export function setGlobalConcurrency(ctx, concurrency) {
11
11
  void ctx.tcp.send({ cmd: 'SetConcurrency', queue: ctx.name, limit: concurrency });
12
12
  }
13
13
  }
14
+ /** Set global concurrency limit and resolve once the server has applied it. */
15
+ export async function setGlobalConcurrencyAsync(ctx, concurrency) {
16
+ if (ctx.embedded)
17
+ getSharedManager().setConcurrency(ctx.name, concurrency);
18
+ else if (ctx.tcp)
19
+ await ctx.tcp.send({ cmd: 'SetConcurrency', queue: ctx.name, limit: concurrency });
20
+ }
14
21
  /** Remove global concurrency limit */
15
22
  export function removeGlobalConcurrency(ctx) {
16
23
  if (ctx.embedded) {
@@ -20,19 +27,36 @@ export function removeGlobalConcurrency(ctx) {
20
27
  void ctx.tcp.send({ cmd: 'ClearConcurrency', queue: ctx.name });
21
28
  }
22
29
  }
30
+ /** Remove global concurrency limit and resolve once the server has applied it. */
31
+ export async function removeGlobalConcurrencyAsync(ctx) {
32
+ if (ctx.embedded)
33
+ getSharedManager().clearConcurrency(ctx.name);
34
+ else if (ctx.tcp)
35
+ await ctx.tcp.send({ cmd: 'ClearConcurrency', queue: ctx.name });
36
+ }
23
37
  /** Get global concurrency limit */
24
38
  export function getGlobalConcurrency(_ctx) {
25
39
  return Promise.resolve(null);
26
40
  }
27
- /** Set global rate limit */
28
- export function setGlobalRateLimit(ctx, max, _duration) {
41
+ /**
42
+ * Set global rate limit: `max` jobs per `duration` ms (default 1000ms).
43
+ * The window is honored in both embedded and TCP modes.
44
+ */
45
+ export function setGlobalRateLimit(ctx, max, duration) {
29
46
  if (ctx.embedded) {
30
- getSharedManager().setRateLimit(ctx.name, max);
47
+ getSharedManager().setRateLimit(ctx.name, max, duration);
31
48
  }
32
49
  else if (ctx.tcp) {
33
- void ctx.tcp.send({ cmd: 'RateLimit', queue: ctx.name, limit: max });
50
+ void ctx.tcp.send({ cmd: 'RateLimit', queue: ctx.name, limit: max, duration });
34
51
  }
35
52
  }
53
+ /** Set global rate limit and resolve once the server has applied it. */
54
+ export async function setGlobalRateLimitAsync(ctx, max, duration) {
55
+ if (ctx.embedded)
56
+ getSharedManager().setRateLimit(ctx.name, max, duration);
57
+ else if (ctx.tcp)
58
+ await ctx.tcp.send({ cmd: 'RateLimit', queue: ctx.name, limit: max, duration });
59
+ }
36
60
  /** Remove global rate limit */
37
61
  export function removeGlobalRateLimit(ctx) {
38
62
  if (ctx.embedded) {
@@ -42,20 +66,31 @@ export function removeGlobalRateLimit(ctx) {
42
66
  void ctx.tcp.send({ cmd: 'RateLimitClear', queue: ctx.name });
43
67
  }
44
68
  }
69
+ /** Remove global rate limit and resolve once the server has applied it. */
70
+ export async function removeGlobalRateLimitAsync(ctx) {
71
+ if (ctx.embedded)
72
+ getSharedManager().clearRateLimit(ctx.name);
73
+ else if (ctx.tcp)
74
+ await ctx.tcp.send({ cmd: 'RateLimitClear', queue: ctx.name });
75
+ }
45
76
  /** Get global rate limit */
46
77
  export function getGlobalRateLimit(_ctx) {
47
78
  return Promise.resolve(null);
48
79
  }
49
- /** Set temporary rate limit with expiration */
80
+ /**
81
+ * Set temporary rate limit with expiration. The expiry lives broker-side
82
+ * (RateLimit `ttl`), so it survives client exit and works identically in
83
+ * embedded and TCP modes — no client timer involved.
84
+ */
50
85
  export async function rateLimit(ctx, expireTimeMs) {
86
+ if (!Number.isFinite(expireTimeMs) || expireTimeMs <= 0) {
87
+ throw new Error(`rateLimit: expireTimeMs must be a positive finite number, got ${expireTimeMs}`);
88
+ }
51
89
  if (ctx.embedded) {
52
- getSharedManager().setRateLimit(ctx.name, 1);
53
- setTimeout(() => {
54
- getSharedManager().clearRateLimit(ctx.name);
55
- }, expireTimeMs);
90
+ getSharedManager().setRateLimit(ctx.name, 1, undefined, expireTimeMs);
56
91
  }
57
92
  else if (ctx.tcp) {
58
- await ctx.tcp.send({ cmd: 'RateLimit', queue: ctx.name, limit: 1 });
93
+ await ctx.tcp.send({ cmd: 'RateLimit', queue: ctx.name, limit: 1, ttl: expireTimeMs });
59
94
  }
60
95
  }
61
96
  /** Get rate limit TTL */
@@ -11,6 +11,8 @@ interface StallContext {
11
11
  }
12
12
  /** Set stall detection configuration */
13
13
  export declare function setStallConfig(ctx: StallContext, config: Partial<StallConfig>): void;
14
+ /** Set stall detection configuration and resolve once the server has applied it. */
15
+ export declare function setStallConfigAsync(ctx: StallContext, config: Partial<StallConfig>): Promise<void>;
14
16
  /** Get stall detection configuration */
15
17
  export declare function getStallConfig(ctx: StallContext): StallConfig;
16
18
  /** Get stall detection configuration (async, works in TCP mode) */
@@ -23,6 +23,18 @@ export function setStallConfig(ctx, config) {
23
23
  void ctx.tcp.send({ cmd: 'SetStallConfig', queue: ctx.name, config });
24
24
  }
25
25
  }
26
+ /** Set stall detection configuration and resolve once the server has applied it. */
27
+ export async function setStallConfigAsync(ctx, config) {
28
+ if (ctx.embedded) {
29
+ dlqOps.setStallConfigEmbedded(ctx.name, config);
30
+ return;
31
+ }
32
+ if (!ctx.tcp)
33
+ return;
34
+ const current = tcpConfigCache.get(ctx.name) ?? { ...DEFAULT_STALL_CONFIG };
35
+ tcpConfigCache.set(ctx.name, { ...current, ...config });
36
+ await ctx.tcp.send({ cmd: 'SetStallConfig', queue: ctx.name, config });
37
+ }
26
38
  /** Get stall detection configuration */
27
39
  export function getStallConfig(ctx) {
28
40
  if (ctx.embedded) {
@@ -54,6 +54,11 @@ export declare class DependencyTracker {
54
54
  * Remove a parent job from waiting
55
55
  */
56
56
  removeWaitingParent(jobId: JobId): Job | undefined;
57
+ /**
58
+ * Remove every dependency-gated job owned by a queue.
59
+ * Returns the removed ids so the caller can purge global indexes and storage.
60
+ */
61
+ removeQueue(queue: string): JobId[];
57
62
  /**
58
63
  * Get a parent job waiting for children
59
64
  */
@@ -100,6 +100,26 @@ export class DependencyTracker {
100
100
  }
101
101
  return job;
102
102
  }
103
+ /**
104
+ * Remove every dependency-gated job owned by a queue.
105
+ * Returns the removed ids so the caller can purge global indexes and storage.
106
+ */
107
+ removeQueue(queue) {
108
+ const removed = [];
109
+ for (const [jobId, job] of this.waitingDeps) {
110
+ if (job.queue !== queue)
111
+ continue;
112
+ this.removeWaitingJob(jobId);
113
+ removed.push(jobId);
114
+ }
115
+ for (const [jobId, job] of this.waitingChildren) {
116
+ if (job.queue !== queue)
117
+ continue;
118
+ this.waitingChildren.delete(jobId);
119
+ removed.push(jobId);
120
+ }
121
+ return removed;
122
+ }
103
123
  /**
104
124
  * Get a parent job waiting for children
105
125
  */
@@ -20,10 +20,22 @@ export declare class LimiterManager {
20
20
  pause(name: string): void;
21
21
  /** Resume queue */
22
22
  resume(name: string): void;
23
- /** Set rate limit for queue */
24
- setRateLimit(queue: string, limit: number): void;
23
+ /**
24
+ * Set rate limit for queue.
25
+ * @param durationMs window the limit applies to (default 1000ms): `limit`
26
+ * tokens refill over `durationMs`. Non-finite or non-positive → default.
27
+ * @param ttlMs auto-expiry: the limit clears itself after this many ms
28
+ * (lazy, checked on acquire/read). Non-finite or non-positive → permanent.
29
+ */
30
+ setRateLimit(queue: string, limit: number, durationMs?: number, ttlMs?: number): void;
25
31
  /** Clear rate limit */
26
32
  clearRateLimit(queue: string): void;
33
+ /**
34
+ * Drop the rate limit if its TTL has elapsed. Lazy expiry: called from the
35
+ * acquire path and from limit reads, so no timer is needed and an expired
36
+ * limit can never throttle a pull.
37
+ */
38
+ expireRateLimitIfNeeded(queue: string): void;
27
39
  /** Try to acquire rate limit token */
28
40
  tryAcquireRateLimit(queue: string): boolean;
29
41
  /** Set concurrency limit for queue */