@bymax-one/nest-queue 1.2.13 → 1.2.15
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/CHANGELOG.md +117 -1
- package/README.md +106 -53
- package/dist/server/index.cjs +369 -100
- package/dist/server/index.d.cts +117 -32
- package/dist/server/index.d.ts +117 -32
- package/dist/server/index.mjs +369 -100
- package/package.json +1 -1
package/dist/server/index.d.cts
CHANGED
|
@@ -58,7 +58,10 @@ interface BymaxQueueModuleOptions {
|
|
|
58
58
|
connection: QueueConnectionConfig;
|
|
59
59
|
/** Default options applied to every job enqueued through the service. */
|
|
60
60
|
defaultJobOptions?: JobsOptions;
|
|
61
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* Prefix for all Redis keys, a namespace and not an access control: anything that can reach the
|
|
63
|
+
* Redis instance can read every prefix on it. Default: 'bull'.
|
|
64
|
+
*/
|
|
62
65
|
prefix?: string;
|
|
63
66
|
/** Default options applied to every Queue, merged with the job/prefix defaults. */
|
|
64
67
|
queueOptions?: Partial<Omit<QueueOptions, 'connection' | 'defaultJobOptions' | 'prefix'>>;
|
|
@@ -87,8 +90,28 @@ interface BymaxQueueModuleOptions {
|
|
|
87
90
|
* Default: true.
|
|
88
91
|
*/
|
|
89
92
|
isGlobal?: boolean;
|
|
90
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* Mode B only: the startup budget, in milliseconds, for Redis to reach `ready`. Connection errors
|
|
95
|
+
* while the client is still reconnecting do not end the wait; the budget does, with
|
|
96
|
+
* `CONNECTION_TIMEOUT`, and the client the library opened is closed. Default: 10_000.
|
|
97
|
+
*/
|
|
91
98
|
connectionReadyTimeoutMs?: number;
|
|
99
|
+
/**
|
|
100
|
+
* Mode B only: the deadline, in milliseconds, for one Redis command on the connection the library
|
|
101
|
+
* opened (ioredis `commandTimeout`). A command that gets no reply in time rejects instead of
|
|
102
|
+
* waiting out ioredis' own retry window. An `options.commandTimeout` in the connection config
|
|
103
|
+
* wins. Worker and QueueEvents connections never inherit it, because they block by design.
|
|
104
|
+
* Default: 5_000.
|
|
105
|
+
*/
|
|
106
|
+
commandTimeoutMs?: number;
|
|
107
|
+
/**
|
|
108
|
+
* The deadline, in milliseconds, for one `QueueService` call that reaches Redis (`enqueue`,
|
|
109
|
+
* `getJob`, `getMetrics`, the scheduler, pause and clean calls, and the rest) in either connection
|
|
110
|
+
* mode, including the wait for the client to be ready on a queue's first call. When it passes, the
|
|
111
|
+
* call rejects with `CONNECTION_TIMEOUT`; an `enqueue` may still complete in Redis afterwards, so
|
|
112
|
+
* retry with a stable `jobId` or `deduplication`. Default: 10_000.
|
|
113
|
+
*/
|
|
114
|
+
enqueueTimeoutMs?: number;
|
|
92
115
|
}
|
|
93
116
|
/** A factory that produces module options, for async configuration. */
|
|
94
117
|
interface BymaxQueueOptionsFactory {
|
|
@@ -236,6 +259,10 @@ interface ResolvedQueueOptions {
|
|
|
236
259
|
telemetry?: Telemetry;
|
|
237
260
|
/** Mode B ready timeout in milliseconds. */
|
|
238
261
|
connectionReadyTimeoutMs: number;
|
|
262
|
+
/** Mode B per-command deadline in milliseconds. */
|
|
263
|
+
commandTimeoutMs: number;
|
|
264
|
+
/** Deadline in milliseconds for one enqueue call. */
|
|
265
|
+
enqueueTimeoutMs: number;
|
|
239
266
|
}
|
|
240
267
|
|
|
241
268
|
/**
|
|
@@ -362,7 +389,9 @@ declare class ConnectionResolver {
|
|
|
362
389
|
/**
|
|
363
390
|
* Merge the consumer's options with the owned-connection policy
|
|
364
391
|
* (`lazyConnect: false`, so the client connects eagerly and `init` can wait
|
|
365
|
-
* for `ready
|
|
392
|
+
* for `ready`; a command deadline, so a frozen Redis fails a call in seconds; and no offline
|
|
393
|
+
* queue, so a call made during an outage fails at once instead of buffering without bound —
|
|
394
|
+
* each only where the consumer has not chosen a value) and narrow the result to the shape ioredis 6's constructor
|
|
366
395
|
* accepts. The runtime object is unchanged — the cast only drops the
|
|
367
396
|
* `undefined` that `RedisOptions.replyMapping` carries and the constructor
|
|
368
397
|
* rejects.
|
|
@@ -374,11 +403,26 @@ declare class ConnectionResolver {
|
|
|
374
403
|
getMode(): QueueConnectionMode;
|
|
375
404
|
/** Whether the library owns (and must close) the connection. */
|
|
376
405
|
isOwned(): boolean;
|
|
406
|
+
/**
|
|
407
|
+
* Settle when the client can take a command. While it is still connecting or reconnecting, every
|
|
408
|
+
* caller shares one wait on its `ready` event, so a health check that polls through a long outage
|
|
409
|
+
* adds one listener set to the client, not one per call, and gives it back when Redis returns. A
|
|
410
|
+
* lazy client (`wait`) connects on its first command and a finished one (`end`) never becomes
|
|
411
|
+
* ready, so both are left for the call itself.
|
|
412
|
+
*/
|
|
413
|
+
awaitReady(): Promise<void>;
|
|
377
414
|
/** Close the library-owned connection on shutdown; never touch a BYO client. */
|
|
378
415
|
teardown(): Promise<void>;
|
|
379
416
|
/** Validate and adopt a bring-your-own client for the Queue role. */
|
|
380
417
|
private initModeA;
|
|
381
|
-
/**
|
|
418
|
+
/**
|
|
419
|
+
* Resolve when the owned client reaches `ready`. A connection error while the client is still
|
|
420
|
+
* reconnecting does not end the wait — a Redis that restarts during a deploy refuses connections
|
|
421
|
+
* for a moment first — so the budget does. The two exceptions are an answer from the server that
|
|
422
|
+
* waiting cannot change (a refused credential) and a client that has stopped reconnecting; both
|
|
423
|
+
* reject at once. The rejection carries the last error's code and never its message, which can
|
|
424
|
+
* name the host.
|
|
425
|
+
*/
|
|
382
426
|
private waitReady;
|
|
383
427
|
}
|
|
384
428
|
|
|
@@ -412,13 +456,27 @@ declare class QueueService {
|
|
|
412
456
|
* @param overrides - Optional per-queue option overrides.
|
|
413
457
|
*/
|
|
414
458
|
getOrCreateQueue<TData = unknown, TResult = unknown>(queueName: string, overrides?: Partial<Omit<QueueOptions, 'connection' | 'prefix'>>): Queue<TData, TResult>;
|
|
459
|
+
/**
|
|
460
|
+
* Run one Redis call under the per-call deadline (`enqueueTimeoutMs`), reporting every way Redis
|
|
461
|
+
* can fail to answer as the documented `CONNECTION_TIMEOUT`: the deadline passing, an ioredis
|
|
462
|
+
* command timeout, or a call refused because the connection is down.
|
|
463
|
+
*
|
|
464
|
+
* The first call on a queue name waits for the client to be ready through ONE wait shared by all
|
|
465
|
+
* callers, instead of letting BullMQ start its own unbounded wait per call, so a caller that
|
|
466
|
+
* polls through an outage does not pile up listeners. The call runs only if it is still within
|
|
467
|
+
* its deadline when the client is ready; a caller that was told it timed out never has its call
|
|
468
|
+
* run later, which would turn its retry into a duplicate.
|
|
469
|
+
*/
|
|
470
|
+
private call;
|
|
471
|
+
/** Settle when a call on `queueName` may start: at once for a queue that has been used before. */
|
|
472
|
+
private afterReady;
|
|
415
473
|
/** Resolve the raw cached queue (default generics) for internal delegation. */
|
|
416
474
|
private queueFor;
|
|
417
475
|
/**
|
|
418
476
|
* Add a single job to a queue. The queue is created lazily if needed.
|
|
419
477
|
*
|
|
420
478
|
* Delivery is **at-least-once** — a handler may run more than once for the same
|
|
421
|
-
* job (worker crash, lock expiry, or a shutdown
|
|
479
|
+
* job (worker crash, lock expiry, or a shutdown that outlasts the drain budget). Make handlers
|
|
422
480
|
* idempotent: use an idempotency key on writes, prefer upserts over inserts, or
|
|
423
481
|
* keep an "already-processed" marker keyed by `job.id`.
|
|
424
482
|
*
|
|
@@ -492,7 +550,9 @@ declare class QueueService {
|
|
|
492
550
|
* @param queueName - Target queue.
|
|
493
551
|
* @param status - Status filter.
|
|
494
552
|
* @param start - Starting index (inclusive). Default: 0.
|
|
495
|
-
* @param end - Ending index (inclusive). Default: 50.
|
|
553
|
+
* @param end - Ending index (inclusive). Default: 50. The window may span at most
|
|
554
|
+
* `MAX_JOBS_PAGE_SIZE` (1000) jobs, and BullMQ's `-1` (everything) is refused.
|
|
555
|
+
* @throws {QueueException} `INVALID_OPTIONS` when the window is not a bounded range of integers.
|
|
496
556
|
*/
|
|
497
557
|
getJobs<TData = unknown, TResult = unknown>(queueName: string, status: JobStatus, start?: number, end?: number): Promise<TypedJob<TData, TResult>[]>;
|
|
498
558
|
/**
|
|
@@ -597,6 +657,7 @@ declare class QueueService {
|
|
|
597
657
|
* })
|
|
598
658
|
*/
|
|
599
659
|
declare class FlowService {
|
|
660
|
+
#private;
|
|
600
661
|
private readonly logger;
|
|
601
662
|
private readonly producer?;
|
|
602
663
|
constructor(connection: ConnectionResolver, enabled: boolean, prefix: string, telemetry?: Telemetry);
|
|
@@ -629,6 +690,8 @@ declare class FlowService {
|
|
|
629
690
|
* @throws {QueueException} `FLOW_DISABLED` (503) when flows are not enabled.
|
|
630
691
|
*/
|
|
631
692
|
addBulk(flows: readonly FlowJob[]): Promise<JobNode[]>;
|
|
693
|
+
/** The command deadline of the producer connection, read when a command has timed out. */
|
|
694
|
+
private commandTimeout;
|
|
632
695
|
/**
|
|
633
696
|
* Escape hatch returning the underlying `FlowProducer` for advanced use cases.
|
|
634
697
|
* Prefer {@link FlowService.add} / {@link FlowService.addBulk} when possible.
|
|
@@ -751,6 +814,14 @@ interface WorkerOptions {
|
|
|
751
814
|
lockDuration?: number;
|
|
752
815
|
/** Interval between stalled-job checks, in milliseconds. Default: 30_000. */
|
|
753
816
|
stalledInterval?: number;
|
|
817
|
+
/**
|
|
818
|
+
* Fail a job whose handler has not settled after this many milliseconds, so it retries under the
|
|
819
|
+
* job's `attempts` and `backoff` and its concurrency slot is freed. The handler's `signal` aborts
|
|
820
|
+
* at that moment; a handler that ignores it keeps running in the background. There is no default:
|
|
821
|
+
* without it a handler that never settles holds its slot indefinitely. Not available for
|
|
822
|
+
* sandboxed workers. Range: 1 to 2_147_483_647.
|
|
823
|
+
*/
|
|
824
|
+
jobTimeoutMs?: number;
|
|
754
825
|
}
|
|
755
826
|
|
|
756
827
|
/**
|
|
@@ -773,8 +844,13 @@ interface WorkerOptions {
|
|
|
773
844
|
interface ProgrammaticWorkerConfig<TData = unknown, TResult = unknown> {
|
|
774
845
|
/** BullMQ queue the worker should consume. */
|
|
775
846
|
queueName: string;
|
|
776
|
-
/**
|
|
777
|
-
|
|
847
|
+
/**
|
|
848
|
+
* Async function that processes a single job. Must be idempotent. Its optional second argument
|
|
849
|
+
* is the job's `AbortSignal`: it aborts when the job's `jobTimeoutMs` passes or when BullMQ
|
|
850
|
+
* cancels the job (`worker.cancelJob()`, `worker.cancelAllJobs()`). Closing the worker does not
|
|
851
|
+
* abort it. Pass it to whatever the handler awaits so the work stops with the job.
|
|
852
|
+
*/
|
|
853
|
+
handler: (job: Job<TData, TResult>, signal?: AbortSignal) => Promise<TResult>;
|
|
778
854
|
/** Optional worker tuning overrides. */
|
|
779
855
|
options?: WorkerOptions;
|
|
780
856
|
}
|
|
@@ -912,6 +988,14 @@ declare class WorkerRegistry {
|
|
|
912
988
|
* @throws {QueueException} `WORKER_REGISTRATION_FAILED` when values are out of range.
|
|
913
989
|
*/
|
|
914
990
|
private validateOptions;
|
|
991
|
+
/**
|
|
992
|
+
* Validate `jobTimeoutMs`, which must be a timer delay Node honours: above 2^31 - 1 ms it would
|
|
993
|
+
* fire after 1 ms and fail every job at once.
|
|
994
|
+
*
|
|
995
|
+
* @param opts - The caller-supplied options (may be undefined).
|
|
996
|
+
* @throws {QueueException} `WORKER_REGISTRATION_FAILED` when the value is out of range.
|
|
997
|
+
*/
|
|
998
|
+
private validateJobTimeout;
|
|
915
999
|
/**
|
|
916
1000
|
* Validate that `processorFile` is a safe, reachable artifact path. Accepts
|
|
917
1001
|
* absolute paths with `.js`/`.cjs`/`.mjs` extensions and `file:` URLs. Rejects
|
|
@@ -1020,9 +1104,9 @@ declare class QueueEventsRegistry {
|
|
|
1020
1104
|
* HTTP server has stopped, it releases what a request can still be using: the
|
|
1021
1105
|
* flow producer, the cached queues, and finally the Queue-role connection.
|
|
1022
1106
|
*
|
|
1023
|
-
* Delivery is at-least-once: when a worker exceeds the drain budget it is
|
|
1024
|
-
*
|
|
1025
|
-
* worker. Handlers must therefore be idempotent, and `lockDuration` should
|
|
1107
|
+
* Delivery is at-least-once: when a worker exceeds the drain budget it is stopped
|
|
1108
|
+
* without waiting for its job, whose lock then expires so the job becomes `stalled`
|
|
1109
|
+
* and is retried by another worker. Handlers must therefore be idempotent, and `lockDuration` should
|
|
1026
1110
|
* comfortably exceed the worst-case handler runtime so healthy long jobs are
|
|
1027
1111
|
* never treated as stalled.
|
|
1028
1112
|
* @layer server/lifecycle
|
|
@@ -1071,28 +1155,26 @@ declare class QueueLifecycle implements OnModuleDestroy, OnApplicationShutdown {
|
|
|
1071
1155
|
*/
|
|
1072
1156
|
onApplicationShutdown(): Promise<void>;
|
|
1073
1157
|
/**
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1158
|
+
* Stop every worker in parallel, each bounded by what is left of the shutdown budget. A worker
|
|
1159
|
+
* is paused first (it stops fetching and waits for its active jobs) and then closed. One that is
|
|
1160
|
+
* still busy when the budget ends is closed with `close(true)`: because `pause` does not start a
|
|
1161
|
+
* close, that is the worker's FIRST close, so it does not wait for the job and stops the lock
|
|
1162
|
+
* renewal that would otherwise keep running and keep the process alive. The job's lock then
|
|
1163
|
+
* expires, and it stalls and is retried elsewhere. Returns how many workers were stopped
|
|
1164
|
+
* without draining.
|
|
1076
1165
|
*
|
|
1077
|
-
* @
|
|
1166
|
+
* @param deadline - The epoch millisecond at which the shutdown budget ends.
|
|
1167
|
+
* @returns The count of workers stopped without draining.
|
|
1078
1168
|
*/
|
|
1079
1169
|
private closeWorkers;
|
|
1080
|
-
/**
|
|
1081
|
-
* Race a graceful `worker.close()` against a timer. `worker.close()` already
|
|
1082
|
-
* drains active jobs and takes no timeout argument, so the timer is the only
|
|
1083
|
-
* way to bound the wait. Rejects with a timeout error when the budget elapses.
|
|
1084
|
-
*
|
|
1085
|
-
* @param worker - The worker to close.
|
|
1086
|
-
* @param timeoutMs - The drain budget in milliseconds.
|
|
1087
|
-
*/
|
|
1088
|
-
private closeWorkerWithTimeout;
|
|
1089
|
-
/** Close every cached `QueueEvents`, swallowing individual failures. */
|
|
1170
|
+
/** Close every cached `QueueEvents` in parallel within the budget, swallowing failures. */
|
|
1090
1171
|
private closeQueueEvents;
|
|
1091
1172
|
/**
|
|
1092
|
-
* Close the duplicated worker/queue-events connections the library opened.
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1095
|
-
* client is never among
|
|
1173
|
+
* Close the duplicated worker/queue-events connections the library opened, in parallel. BullMQ
|
|
1174
|
+
* treats a passed-in client as shared and never closes it, so the library must release these
|
|
1175
|
+
* explicitly. A connection that does not quit within the budget is disconnected, which also
|
|
1176
|
+
* releases anything still blocked on it. In Mode A the consumer's shared client is never among
|
|
1177
|
+
* them.
|
|
1096
1178
|
*/
|
|
1097
1179
|
private closeDuplicatedConnections;
|
|
1098
1180
|
/**
|
|
@@ -1161,7 +1243,7 @@ declare function Processor(queueName: string, options?: WorkerOptions): ClassDec
|
|
|
1161
1243
|
* an entry to the accumulated metadata array.
|
|
1162
1244
|
*
|
|
1163
1245
|
* Delivery is at-least-once: a handler may run more than once for the same job
|
|
1164
|
-
* (worker crash, lock expiry, or a shutdown
|
|
1246
|
+
* (worker crash, lock expiry, or a shutdown that outlasts the drain budget). Handlers MUST be
|
|
1165
1247
|
* idempotent — use an idempotency key on writes, prefer upserts over inserts, or
|
|
1166
1248
|
* keep an "already-processed" marker keyed by `job.id`.
|
|
1167
1249
|
*
|
|
@@ -1213,7 +1295,7 @@ type WorkerEventName = 'completed' | 'failed' | 'progress' | 'active' | 'stalled
|
|
|
1213
1295
|
* overwriting each other.
|
|
1214
1296
|
*
|
|
1215
1297
|
* Delivery is at-least-once. A `'stalled'` job — for example after a shutdown
|
|
1216
|
-
*
|
|
1298
|
+
* that outlasts the drain budget, or a lock expiry — is re-run by another worker: this is the
|
|
1217
1299
|
* visible at-least-once path, so handlers must be idempotent. Set `lockDuration`
|
|
1218
1300
|
* comfortably above the worst-case handler runtime to avoid false stalls.
|
|
1219
1301
|
*
|
|
@@ -1408,13 +1490,16 @@ declare const DEFAULT_WORKER_CONCURRENCY: 2;
|
|
|
1408
1490
|
declare const MAX_WORKER_CONCURRENCY: 1000;
|
|
1409
1491
|
/**
|
|
1410
1492
|
* Default options applied to every job enqueued through the service. The
|
|
1411
|
-
* `removeOnFail.count` cap bounds Redis memory growth under failure storms.
|
|
1493
|
+
* `removeOnFail.count` cap bounds Redis memory growth under failure storms. The backoff jitter
|
|
1494
|
+
* spreads each retry over the upper half of its delay, so jobs that failed together (a Redis
|
|
1495
|
+
* outage, a downstream timeout) do not all retry in the same instant.
|
|
1412
1496
|
*/
|
|
1413
1497
|
declare const DEFAULT_JOB_OPTIONS: {
|
|
1414
1498
|
readonly attempts: 3;
|
|
1415
1499
|
readonly backoff: {
|
|
1416
1500
|
readonly type: "exponential";
|
|
1417
1501
|
readonly delay: 2000;
|
|
1502
|
+
readonly jitter: 0.5;
|
|
1418
1503
|
};
|
|
1419
1504
|
readonly removeOnComplete: {
|
|
1420
1505
|
readonly age: number;
|
|
@@ -1427,7 +1512,7 @@ declare const DEFAULT_JOB_OPTIONS: {
|
|
|
1427
1512
|
};
|
|
1428
1513
|
/** Default TTL (ms) for the metrics cache. */
|
|
1429
1514
|
declare const DEFAULT_METRICS_CACHE_TTL_MS: 5000;
|
|
1430
|
-
/** Default window (ms) for in-flight jobs to drain before
|
|
1515
|
+
/** Default window (ms) for in-flight jobs to drain before the worker is stopped on shutdown. */
|
|
1431
1516
|
declare const DEFAULT_SHUTDOWN_DRAIN_TIMEOUT_MS: 30000;
|
|
1432
1517
|
|
|
1433
1518
|
/**
|
package/dist/server/index.d.ts
CHANGED
|
@@ -58,7 +58,10 @@ interface BymaxQueueModuleOptions {
|
|
|
58
58
|
connection: QueueConnectionConfig;
|
|
59
59
|
/** Default options applied to every job enqueued through the service. */
|
|
60
60
|
defaultJobOptions?: JobsOptions;
|
|
61
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* Prefix for all Redis keys, a namespace and not an access control: anything that can reach the
|
|
63
|
+
* Redis instance can read every prefix on it. Default: 'bull'.
|
|
64
|
+
*/
|
|
62
65
|
prefix?: string;
|
|
63
66
|
/** Default options applied to every Queue, merged with the job/prefix defaults. */
|
|
64
67
|
queueOptions?: Partial<Omit<QueueOptions, 'connection' | 'defaultJobOptions' | 'prefix'>>;
|
|
@@ -87,8 +90,28 @@ interface BymaxQueueModuleOptions {
|
|
|
87
90
|
* Default: true.
|
|
88
91
|
*/
|
|
89
92
|
isGlobal?: boolean;
|
|
90
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* Mode B only: the startup budget, in milliseconds, for Redis to reach `ready`. Connection errors
|
|
95
|
+
* while the client is still reconnecting do not end the wait; the budget does, with
|
|
96
|
+
* `CONNECTION_TIMEOUT`, and the client the library opened is closed. Default: 10_000.
|
|
97
|
+
*/
|
|
91
98
|
connectionReadyTimeoutMs?: number;
|
|
99
|
+
/**
|
|
100
|
+
* Mode B only: the deadline, in milliseconds, for one Redis command on the connection the library
|
|
101
|
+
* opened (ioredis `commandTimeout`). A command that gets no reply in time rejects instead of
|
|
102
|
+
* waiting out ioredis' own retry window. An `options.commandTimeout` in the connection config
|
|
103
|
+
* wins. Worker and QueueEvents connections never inherit it, because they block by design.
|
|
104
|
+
* Default: 5_000.
|
|
105
|
+
*/
|
|
106
|
+
commandTimeoutMs?: number;
|
|
107
|
+
/**
|
|
108
|
+
* The deadline, in milliseconds, for one `QueueService` call that reaches Redis (`enqueue`,
|
|
109
|
+
* `getJob`, `getMetrics`, the scheduler, pause and clean calls, and the rest) in either connection
|
|
110
|
+
* mode, including the wait for the client to be ready on a queue's first call. When it passes, the
|
|
111
|
+
* call rejects with `CONNECTION_TIMEOUT`; an `enqueue` may still complete in Redis afterwards, so
|
|
112
|
+
* retry with a stable `jobId` or `deduplication`. Default: 10_000.
|
|
113
|
+
*/
|
|
114
|
+
enqueueTimeoutMs?: number;
|
|
92
115
|
}
|
|
93
116
|
/** A factory that produces module options, for async configuration. */
|
|
94
117
|
interface BymaxQueueOptionsFactory {
|
|
@@ -236,6 +259,10 @@ interface ResolvedQueueOptions {
|
|
|
236
259
|
telemetry?: Telemetry;
|
|
237
260
|
/** Mode B ready timeout in milliseconds. */
|
|
238
261
|
connectionReadyTimeoutMs: number;
|
|
262
|
+
/** Mode B per-command deadline in milliseconds. */
|
|
263
|
+
commandTimeoutMs: number;
|
|
264
|
+
/** Deadline in milliseconds for one enqueue call. */
|
|
265
|
+
enqueueTimeoutMs: number;
|
|
239
266
|
}
|
|
240
267
|
|
|
241
268
|
/**
|
|
@@ -362,7 +389,9 @@ declare class ConnectionResolver {
|
|
|
362
389
|
/**
|
|
363
390
|
* Merge the consumer's options with the owned-connection policy
|
|
364
391
|
* (`lazyConnect: false`, so the client connects eagerly and `init` can wait
|
|
365
|
-
* for `ready
|
|
392
|
+
* for `ready`; a command deadline, so a frozen Redis fails a call in seconds; and no offline
|
|
393
|
+
* queue, so a call made during an outage fails at once instead of buffering without bound —
|
|
394
|
+
* each only where the consumer has not chosen a value) and narrow the result to the shape ioredis 6's constructor
|
|
366
395
|
* accepts. The runtime object is unchanged — the cast only drops the
|
|
367
396
|
* `undefined` that `RedisOptions.replyMapping` carries and the constructor
|
|
368
397
|
* rejects.
|
|
@@ -374,11 +403,26 @@ declare class ConnectionResolver {
|
|
|
374
403
|
getMode(): QueueConnectionMode;
|
|
375
404
|
/** Whether the library owns (and must close) the connection. */
|
|
376
405
|
isOwned(): boolean;
|
|
406
|
+
/**
|
|
407
|
+
* Settle when the client can take a command. While it is still connecting or reconnecting, every
|
|
408
|
+
* caller shares one wait on its `ready` event, so a health check that polls through a long outage
|
|
409
|
+
* adds one listener set to the client, not one per call, and gives it back when Redis returns. A
|
|
410
|
+
* lazy client (`wait`) connects on its first command and a finished one (`end`) never becomes
|
|
411
|
+
* ready, so both are left for the call itself.
|
|
412
|
+
*/
|
|
413
|
+
awaitReady(): Promise<void>;
|
|
377
414
|
/** Close the library-owned connection on shutdown; never touch a BYO client. */
|
|
378
415
|
teardown(): Promise<void>;
|
|
379
416
|
/** Validate and adopt a bring-your-own client for the Queue role. */
|
|
380
417
|
private initModeA;
|
|
381
|
-
/**
|
|
418
|
+
/**
|
|
419
|
+
* Resolve when the owned client reaches `ready`. A connection error while the client is still
|
|
420
|
+
* reconnecting does not end the wait — a Redis that restarts during a deploy refuses connections
|
|
421
|
+
* for a moment first — so the budget does. The two exceptions are an answer from the server that
|
|
422
|
+
* waiting cannot change (a refused credential) and a client that has stopped reconnecting; both
|
|
423
|
+
* reject at once. The rejection carries the last error's code and never its message, which can
|
|
424
|
+
* name the host.
|
|
425
|
+
*/
|
|
382
426
|
private waitReady;
|
|
383
427
|
}
|
|
384
428
|
|
|
@@ -412,13 +456,27 @@ declare class QueueService {
|
|
|
412
456
|
* @param overrides - Optional per-queue option overrides.
|
|
413
457
|
*/
|
|
414
458
|
getOrCreateQueue<TData = unknown, TResult = unknown>(queueName: string, overrides?: Partial<Omit<QueueOptions, 'connection' | 'prefix'>>): Queue<TData, TResult>;
|
|
459
|
+
/**
|
|
460
|
+
* Run one Redis call under the per-call deadline (`enqueueTimeoutMs`), reporting every way Redis
|
|
461
|
+
* can fail to answer as the documented `CONNECTION_TIMEOUT`: the deadline passing, an ioredis
|
|
462
|
+
* command timeout, or a call refused because the connection is down.
|
|
463
|
+
*
|
|
464
|
+
* The first call on a queue name waits for the client to be ready through ONE wait shared by all
|
|
465
|
+
* callers, instead of letting BullMQ start its own unbounded wait per call, so a caller that
|
|
466
|
+
* polls through an outage does not pile up listeners. The call runs only if it is still within
|
|
467
|
+
* its deadline when the client is ready; a caller that was told it timed out never has its call
|
|
468
|
+
* run later, which would turn its retry into a duplicate.
|
|
469
|
+
*/
|
|
470
|
+
private call;
|
|
471
|
+
/** Settle when a call on `queueName` may start: at once for a queue that has been used before. */
|
|
472
|
+
private afterReady;
|
|
415
473
|
/** Resolve the raw cached queue (default generics) for internal delegation. */
|
|
416
474
|
private queueFor;
|
|
417
475
|
/**
|
|
418
476
|
* Add a single job to a queue. The queue is created lazily if needed.
|
|
419
477
|
*
|
|
420
478
|
* Delivery is **at-least-once** — a handler may run more than once for the same
|
|
421
|
-
* job (worker crash, lock expiry, or a shutdown
|
|
479
|
+
* job (worker crash, lock expiry, or a shutdown that outlasts the drain budget). Make handlers
|
|
422
480
|
* idempotent: use an idempotency key on writes, prefer upserts over inserts, or
|
|
423
481
|
* keep an "already-processed" marker keyed by `job.id`.
|
|
424
482
|
*
|
|
@@ -492,7 +550,9 @@ declare class QueueService {
|
|
|
492
550
|
* @param queueName - Target queue.
|
|
493
551
|
* @param status - Status filter.
|
|
494
552
|
* @param start - Starting index (inclusive). Default: 0.
|
|
495
|
-
* @param end - Ending index (inclusive). Default: 50.
|
|
553
|
+
* @param end - Ending index (inclusive). Default: 50. The window may span at most
|
|
554
|
+
* `MAX_JOBS_PAGE_SIZE` (1000) jobs, and BullMQ's `-1` (everything) is refused.
|
|
555
|
+
* @throws {QueueException} `INVALID_OPTIONS` when the window is not a bounded range of integers.
|
|
496
556
|
*/
|
|
497
557
|
getJobs<TData = unknown, TResult = unknown>(queueName: string, status: JobStatus, start?: number, end?: number): Promise<TypedJob<TData, TResult>[]>;
|
|
498
558
|
/**
|
|
@@ -597,6 +657,7 @@ declare class QueueService {
|
|
|
597
657
|
* })
|
|
598
658
|
*/
|
|
599
659
|
declare class FlowService {
|
|
660
|
+
#private;
|
|
600
661
|
private readonly logger;
|
|
601
662
|
private readonly producer?;
|
|
602
663
|
constructor(connection: ConnectionResolver, enabled: boolean, prefix: string, telemetry?: Telemetry);
|
|
@@ -629,6 +690,8 @@ declare class FlowService {
|
|
|
629
690
|
* @throws {QueueException} `FLOW_DISABLED` (503) when flows are not enabled.
|
|
630
691
|
*/
|
|
631
692
|
addBulk(flows: readonly FlowJob[]): Promise<JobNode[]>;
|
|
693
|
+
/** The command deadline of the producer connection, read when a command has timed out. */
|
|
694
|
+
private commandTimeout;
|
|
632
695
|
/**
|
|
633
696
|
* Escape hatch returning the underlying `FlowProducer` for advanced use cases.
|
|
634
697
|
* Prefer {@link FlowService.add} / {@link FlowService.addBulk} when possible.
|
|
@@ -751,6 +814,14 @@ interface WorkerOptions {
|
|
|
751
814
|
lockDuration?: number;
|
|
752
815
|
/** Interval between stalled-job checks, in milliseconds. Default: 30_000. */
|
|
753
816
|
stalledInterval?: number;
|
|
817
|
+
/**
|
|
818
|
+
* Fail a job whose handler has not settled after this many milliseconds, so it retries under the
|
|
819
|
+
* job's `attempts` and `backoff` and its concurrency slot is freed. The handler's `signal` aborts
|
|
820
|
+
* at that moment; a handler that ignores it keeps running in the background. There is no default:
|
|
821
|
+
* without it a handler that never settles holds its slot indefinitely. Not available for
|
|
822
|
+
* sandboxed workers. Range: 1 to 2_147_483_647.
|
|
823
|
+
*/
|
|
824
|
+
jobTimeoutMs?: number;
|
|
754
825
|
}
|
|
755
826
|
|
|
756
827
|
/**
|
|
@@ -773,8 +844,13 @@ interface WorkerOptions {
|
|
|
773
844
|
interface ProgrammaticWorkerConfig<TData = unknown, TResult = unknown> {
|
|
774
845
|
/** BullMQ queue the worker should consume. */
|
|
775
846
|
queueName: string;
|
|
776
|
-
/**
|
|
777
|
-
|
|
847
|
+
/**
|
|
848
|
+
* Async function that processes a single job. Must be idempotent. Its optional second argument
|
|
849
|
+
* is the job's `AbortSignal`: it aborts when the job's `jobTimeoutMs` passes or when BullMQ
|
|
850
|
+
* cancels the job (`worker.cancelJob()`, `worker.cancelAllJobs()`). Closing the worker does not
|
|
851
|
+
* abort it. Pass it to whatever the handler awaits so the work stops with the job.
|
|
852
|
+
*/
|
|
853
|
+
handler: (job: Job<TData, TResult>, signal?: AbortSignal) => Promise<TResult>;
|
|
778
854
|
/** Optional worker tuning overrides. */
|
|
779
855
|
options?: WorkerOptions;
|
|
780
856
|
}
|
|
@@ -912,6 +988,14 @@ declare class WorkerRegistry {
|
|
|
912
988
|
* @throws {QueueException} `WORKER_REGISTRATION_FAILED` when values are out of range.
|
|
913
989
|
*/
|
|
914
990
|
private validateOptions;
|
|
991
|
+
/**
|
|
992
|
+
* Validate `jobTimeoutMs`, which must be a timer delay Node honours: above 2^31 - 1 ms it would
|
|
993
|
+
* fire after 1 ms and fail every job at once.
|
|
994
|
+
*
|
|
995
|
+
* @param opts - The caller-supplied options (may be undefined).
|
|
996
|
+
* @throws {QueueException} `WORKER_REGISTRATION_FAILED` when the value is out of range.
|
|
997
|
+
*/
|
|
998
|
+
private validateJobTimeout;
|
|
915
999
|
/**
|
|
916
1000
|
* Validate that `processorFile` is a safe, reachable artifact path. Accepts
|
|
917
1001
|
* absolute paths with `.js`/`.cjs`/`.mjs` extensions and `file:` URLs. Rejects
|
|
@@ -1020,9 +1104,9 @@ declare class QueueEventsRegistry {
|
|
|
1020
1104
|
* HTTP server has stopped, it releases what a request can still be using: the
|
|
1021
1105
|
* flow producer, the cached queues, and finally the Queue-role connection.
|
|
1022
1106
|
*
|
|
1023
|
-
* Delivery is at-least-once: when a worker exceeds the drain budget it is
|
|
1024
|
-
*
|
|
1025
|
-
* worker. Handlers must therefore be idempotent, and `lockDuration` should
|
|
1107
|
+
* Delivery is at-least-once: when a worker exceeds the drain budget it is stopped
|
|
1108
|
+
* without waiting for its job, whose lock then expires so the job becomes `stalled`
|
|
1109
|
+
* and is retried by another worker. Handlers must therefore be idempotent, and `lockDuration` should
|
|
1026
1110
|
* comfortably exceed the worst-case handler runtime so healthy long jobs are
|
|
1027
1111
|
* never treated as stalled.
|
|
1028
1112
|
* @layer server/lifecycle
|
|
@@ -1071,28 +1155,26 @@ declare class QueueLifecycle implements OnModuleDestroy, OnApplicationShutdown {
|
|
|
1071
1155
|
*/
|
|
1072
1156
|
onApplicationShutdown(): Promise<void>;
|
|
1073
1157
|
/**
|
|
1074
|
-
*
|
|
1075
|
-
*
|
|
1158
|
+
* Stop every worker in parallel, each bounded by what is left of the shutdown budget. A worker
|
|
1159
|
+
* is paused first (it stops fetching and waits for its active jobs) and then closed. One that is
|
|
1160
|
+
* still busy when the budget ends is closed with `close(true)`: because `pause` does not start a
|
|
1161
|
+
* close, that is the worker's FIRST close, so it does not wait for the job and stops the lock
|
|
1162
|
+
* renewal that would otherwise keep running and keep the process alive. The job's lock then
|
|
1163
|
+
* expires, and it stalls and is retried elsewhere. Returns how many workers were stopped
|
|
1164
|
+
* without draining.
|
|
1076
1165
|
*
|
|
1077
|
-
* @
|
|
1166
|
+
* @param deadline - The epoch millisecond at which the shutdown budget ends.
|
|
1167
|
+
* @returns The count of workers stopped without draining.
|
|
1078
1168
|
*/
|
|
1079
1169
|
private closeWorkers;
|
|
1080
|
-
/**
|
|
1081
|
-
* Race a graceful `worker.close()` against a timer. `worker.close()` already
|
|
1082
|
-
* drains active jobs and takes no timeout argument, so the timer is the only
|
|
1083
|
-
* way to bound the wait. Rejects with a timeout error when the budget elapses.
|
|
1084
|
-
*
|
|
1085
|
-
* @param worker - The worker to close.
|
|
1086
|
-
* @param timeoutMs - The drain budget in milliseconds.
|
|
1087
|
-
*/
|
|
1088
|
-
private closeWorkerWithTimeout;
|
|
1089
|
-
/** Close every cached `QueueEvents`, swallowing individual failures. */
|
|
1170
|
+
/** Close every cached `QueueEvents` in parallel within the budget, swallowing failures. */
|
|
1090
1171
|
private closeQueueEvents;
|
|
1091
1172
|
/**
|
|
1092
|
-
* Close the duplicated worker/queue-events connections the library opened.
|
|
1093
|
-
*
|
|
1094
|
-
*
|
|
1095
|
-
* client is never among
|
|
1173
|
+
* Close the duplicated worker/queue-events connections the library opened, in parallel. BullMQ
|
|
1174
|
+
* treats a passed-in client as shared and never closes it, so the library must release these
|
|
1175
|
+
* explicitly. A connection that does not quit within the budget is disconnected, which also
|
|
1176
|
+
* releases anything still blocked on it. In Mode A the consumer's shared client is never among
|
|
1177
|
+
* them.
|
|
1096
1178
|
*/
|
|
1097
1179
|
private closeDuplicatedConnections;
|
|
1098
1180
|
/**
|
|
@@ -1161,7 +1243,7 @@ declare function Processor(queueName: string, options?: WorkerOptions): ClassDec
|
|
|
1161
1243
|
* an entry to the accumulated metadata array.
|
|
1162
1244
|
*
|
|
1163
1245
|
* Delivery is at-least-once: a handler may run more than once for the same job
|
|
1164
|
-
* (worker crash, lock expiry, or a shutdown
|
|
1246
|
+
* (worker crash, lock expiry, or a shutdown that outlasts the drain budget). Handlers MUST be
|
|
1165
1247
|
* idempotent — use an idempotency key on writes, prefer upserts over inserts, or
|
|
1166
1248
|
* keep an "already-processed" marker keyed by `job.id`.
|
|
1167
1249
|
*
|
|
@@ -1213,7 +1295,7 @@ type WorkerEventName = 'completed' | 'failed' | 'progress' | 'active' | 'stalled
|
|
|
1213
1295
|
* overwriting each other.
|
|
1214
1296
|
*
|
|
1215
1297
|
* Delivery is at-least-once. A `'stalled'` job — for example after a shutdown
|
|
1216
|
-
*
|
|
1298
|
+
* that outlasts the drain budget, or a lock expiry — is re-run by another worker: this is the
|
|
1217
1299
|
* visible at-least-once path, so handlers must be idempotent. Set `lockDuration`
|
|
1218
1300
|
* comfortably above the worst-case handler runtime to avoid false stalls.
|
|
1219
1301
|
*
|
|
@@ -1408,13 +1490,16 @@ declare const DEFAULT_WORKER_CONCURRENCY: 2;
|
|
|
1408
1490
|
declare const MAX_WORKER_CONCURRENCY: 1000;
|
|
1409
1491
|
/**
|
|
1410
1492
|
* Default options applied to every job enqueued through the service. The
|
|
1411
|
-
* `removeOnFail.count` cap bounds Redis memory growth under failure storms.
|
|
1493
|
+
* `removeOnFail.count` cap bounds Redis memory growth under failure storms. The backoff jitter
|
|
1494
|
+
* spreads each retry over the upper half of its delay, so jobs that failed together (a Redis
|
|
1495
|
+
* outage, a downstream timeout) do not all retry in the same instant.
|
|
1412
1496
|
*/
|
|
1413
1497
|
declare const DEFAULT_JOB_OPTIONS: {
|
|
1414
1498
|
readonly attempts: 3;
|
|
1415
1499
|
readonly backoff: {
|
|
1416
1500
|
readonly type: "exponential";
|
|
1417
1501
|
readonly delay: 2000;
|
|
1502
|
+
readonly jitter: 0.5;
|
|
1418
1503
|
};
|
|
1419
1504
|
readonly removeOnComplete: {
|
|
1420
1505
|
readonly age: number;
|
|
@@ -1427,7 +1512,7 @@ declare const DEFAULT_JOB_OPTIONS: {
|
|
|
1427
1512
|
};
|
|
1428
1513
|
/** Default TTL (ms) for the metrics cache. */
|
|
1429
1514
|
declare const DEFAULT_METRICS_CACHE_TTL_MS: 5000;
|
|
1430
|
-
/** Default window (ms) for in-flight jobs to drain before
|
|
1515
|
+
/** Default window (ms) for in-flight jobs to drain before the worker is stopped on shutdown. */
|
|
1431
1516
|
declare const DEFAULT_SHUTDOWN_DRAIN_TIMEOUT_MS: 30000;
|
|
1432
1517
|
|
|
1433
1518
|
/**
|