@bymax-one/nest-queue 1.2.10 → 1.2.12
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 +77 -1
- package/README.md +28 -20
- package/dist/server/index.cjs +43 -502
- package/dist/server/index.d.cts +29 -10
- package/dist/server/index.d.ts +29 -10
- package/dist/server/index.mjs +44 -503
- package/package.json +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,80 @@ versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
## [1.2.12] — 2026-11-09
|
|
15
|
+
|
|
16
|
+
No action required. The public API, the declaration files and the peer ranges are unchanged
|
|
17
|
+
(`check-api-surface` reports no difference against 1.2.11); the runtime bundles (`index.mjs`,
|
|
18
|
+
`index.cjs`) differ in code, measured with `diff -r` of a fresh build against the unpacked 1.2.11
|
|
19
|
+
tarball. The package also ships a corrected README.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **An outage on a connection the library opens no longer prints `[ioredis] Unhandled error event`
|
|
24
|
+
to stderr.** In Mode B the owned connection listened for `error` only while it waited for
|
|
25
|
+
`ready`, so until BullMQ built something on it (BullMQ adds its own listener to a client it is
|
|
26
|
+
handed) a later outage reached ioredis with no listener, and ioredis wrote one line per
|
|
27
|
+
reconnect attempt to raw stderr, outside the structured logger. The owned connection now keeps a
|
|
28
|
+
listener for its whole life that logs through the module's logger. The same holds for the
|
|
29
|
+
duplicate opened for each `QueueEvents` (BullMQ listens on its own copy, not on this one) and for
|
|
30
|
+
the probe the Mode A check duplicates. Every error is logged as it arrives, with no throttling.
|
|
31
|
+
A client you pass in Mode A is yours and the library adds no listener to it.
|
|
32
|
+
|
|
33
|
+
### Documentation
|
|
34
|
+
|
|
35
|
+
- **The README's Mode A example now compiles and runs.** It imported `CACHE_REDIS_CLIENT` from
|
|
36
|
+
`@bymax-one/nest-cache`, a name that package does not export. The example now injects
|
|
37
|
+
`ConnectionManager` and passes `cache.getClient()`, and says that `getClient()` is typed
|
|
38
|
+
`Redis | Cluster` while Mode A takes a standalone `Redis`.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## [1.2.11] — 2026-11-06
|
|
43
|
+
|
|
44
|
+
Action required only if you extend or replace `QueueLifecycle`, or rely on the order in which the
|
|
45
|
+
library closes its resources: the producer side now closes after the HTTP server stops, see the Apply
|
|
46
|
+
note. Healthy use is unchanged. The public API and the peer ranges are unchanged; the declaration
|
|
47
|
+
files differ because `QueueLifecycle` now also implements `OnApplicationShutdown` (and in JSDoc).
|
|
48
|
+
Measured with `diff -r` of a fresh build against the unpacked 1.2.10 tarball, the runtime bundles
|
|
49
|
+
(`index.mjs`, `index.cjs`) differ in code.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- **A request still being served when the application closed could not enqueue.** NestJS runs every
|
|
54
|
+
`onModuleDestroy` hook before it stops the HTTP server, and `QueueLifecycle` closed the flow
|
|
55
|
+
producer, the cached queues and the library-owned Redis connection in that hook. A request that
|
|
56
|
+
enqueued in the window between the two (a request in flight during `app.close()`, or a
|
|
57
|
+
`SIGTERM` with shutdown hooks enabled) failed with `Connection is closed.` in Mode B. Those three
|
|
58
|
+
resources are now released in `onApplicationShutdown`, which NestJS runs after the server has
|
|
59
|
+
stopped. Consumption still stops first: workers drain within `shutdown.drainTimeoutMs`, then
|
|
60
|
+
queue-events and the connections opened for workers and queue-events close, all in
|
|
61
|
+
`onModuleDestroy`. `shutdown.drainOnShutdown` now runs in `onApplicationShutdown` too, right before
|
|
62
|
+
the queues close, so a job enqueued late is drained with the rest. The log line `Queue shutdown
|
|
63
|
+
complete in Nms (forced N worker(s))` is now written once, at the end, and its time covers both
|
|
64
|
+
hooks. In Mode A the consumer's client is untouched, as before.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- **The shipped bundles no longer carry comments.** `dist/server/index.mjs` and `index.cjs` are
|
|
69
|
+
smaller; the declaration files keep their JSDoc, and the comment removal does not change the
|
|
70
|
+
runtime behaviour of the bundles.
|
|
71
|
+
|
|
72
|
+
### Documentation
|
|
73
|
+
|
|
74
|
+
- **The README describes the two shutdown hooks** and when each resource closes.
|
|
75
|
+
|
|
76
|
+
#### Apply to a derived backend
|
|
77
|
+
|
|
78
|
+
- If you subclass or wrap `QueueLifecycle`, or call `onModuleDestroy()` yourself to tear the library
|
|
79
|
+
down in a test, also call `onApplicationShutdown()` afterwards: the flow producer, the cached
|
|
80
|
+
queues and the library-owned connection are no longer closed by `onModuleDestroy()`. Nest calls
|
|
81
|
+
both when the application closes.
|
|
82
|
+
- If you enqueue from a hook that runs after the library's `onModuleDestroy` (for example your own
|
|
83
|
+
`beforeApplicationShutdown`), the enqueue now succeeds instead of failing with
|
|
84
|
+
`Connection is closed.`; if you worked around that failure, the workaround can go.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
14
88
|
## [1.2.10] — 2026-11-01
|
|
15
89
|
|
|
16
90
|
Action required only if code relies on the status of a failed schedule: `upsertJobScheduler` no
|
|
@@ -821,7 +895,9 @@ v6 peer range.
|
|
|
821
895
|
|
|
822
896
|
---
|
|
823
897
|
|
|
824
|
-
[Unreleased]: https://github.com/bymaxone/nest-queue/compare/v1.2.
|
|
898
|
+
[Unreleased]: https://github.com/bymaxone/nest-queue/compare/v1.2.12...HEAD
|
|
899
|
+
[1.2.12]: https://github.com/bymaxone/nest-queue/compare/v1.2.11...v1.2.12
|
|
900
|
+
[1.2.11]: https://github.com/bymaxone/nest-queue/compare/v1.2.10...v1.2.11
|
|
825
901
|
[1.2.10]: https://github.com/bymaxone/nest-queue/compare/v1.2.9...v1.2.10
|
|
826
902
|
[1.2.9]: https://github.com/bymaxone/nest-queue/compare/v1.2.8...v1.2.9
|
|
827
903
|
[1.2.8]: https://github.com/bymaxone/nest-queue/compare/v1.2.7...v1.2.8
|
package/README.md
CHANGED
|
@@ -235,20 +235,20 @@ export class EmailModule {}
|
|
|
235
235
|
|
|
236
236
|
Every option passed to `forRoot` / `forRootAsync`. Only `connection` is required.
|
|
237
237
|
|
|
238
|
-
| Option | Type | Default | Description
|
|
239
|
-
| -------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
240
|
-
| `connection` | `{ client } \| { url, options? } \| { options }` | — | **Required.** Mode A supplies an existing `ioredis` client; Mode B lets the library open its own. The arms are mutually exclusive.
|
|
241
|
-
| `prefix` | `string` | `'bull'` | Redis key namespace. Applied to the `Queue`, `Worker`, `QueueEvents` **and** `FlowProducer` — see [Architecture](#-architecture).
|
|
242
|
-
| `defaultJobOptions` | `JobsOptions` | `attempts: 3` · exponential backoff `2000` ms · `removeOnComplete: { age: 24 h, count: 1000 }` · `removeOnFail: { age: 7 d, count: 5000 }` | Merged into every job enqueued through the service; per-job options win. The bounded `removeOn*` caps are what keep Redis from growing without limit under a failure storm.
|
|
243
|
-
| `queueOptions` | `Partial<QueueOptions>` | `{}` | Extra BullMQ `Queue` options. `connection`, `defaultJobOptions` and `prefix` are owned by the library and cannot be set here.
|
|
244
|
-
| `flows.enabled` | `boolean` | `false` | `FlowService` is always provided; while disabled, every method except `close()` throws `FLOW_DISABLED`.
|
|
245
|
-
| `metrics.enabled` | `boolean` | `false` | `MetricsService` is always provided; while disabled, every method throws `METRICS_DISABLED`.
|
|
246
|
-
| `metrics.cacheTtlMs` | `number` | `5_000` | TTL of the metrics snapshot cache. `0` disables caching. Must be `>= 0`.
|
|
247
|
-
| `telemetry` | `Telemetry` | — | A BullMQ `Telemetry` instance (typically `new BullMQOtel(...)` from the optional `bullmq-otel` peer), attached to every `Queue` and `Worker`.
|
|
248
|
-
| `shutdown.drainTimeoutMs` | `number` | `30_000` | Upper bound on the graceful drain before workers are force-closed. Must be `> 0`.
|
|
249
|
-
| `shutdown.drainOnShutdown` | `boolean` | `false` | Development and test only:
|
|
250
|
-
| `connectionReadyTimeoutMs` | `number` | `10_000` | Mode B only: how long to wait for Redis `ready` before failing with `CONNECTION_TIMEOUT`.
|
|
251
|
-
| `isGlobal` | `boolean` | `true` | Register the module globally, so `QueueService` is injectable without re-importing.
|
|
238
|
+
| Option | Type | Default | Description |
|
|
239
|
+
| -------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
240
|
+
| `connection` | `{ client } \| { url, options? } \| { options }` | — | **Required.** Mode A supplies an existing `ioredis` client; Mode B lets the library open its own. The arms are mutually exclusive. |
|
|
241
|
+
| `prefix` | `string` | `'bull'` | Redis key namespace. Applied to the `Queue`, `Worker`, `QueueEvents` **and** `FlowProducer` — see [Architecture](#-architecture). |
|
|
242
|
+
| `defaultJobOptions` | `JobsOptions` | `attempts: 3` · exponential backoff `2000` ms · `removeOnComplete: { age: 24 h, count: 1000 }` · `removeOnFail: { age: 7 d, count: 5000 }` | Merged into every job enqueued through the service; per-job options win. The bounded `removeOn*` caps are what keep Redis from growing without limit under a failure storm. |
|
|
243
|
+
| `queueOptions` | `Partial<QueueOptions>` | `{}` | Extra BullMQ `Queue` options. `connection`, `defaultJobOptions` and `prefix` are owned by the library and cannot be set here. |
|
|
244
|
+
| `flows.enabled` | `boolean` | `false` | `FlowService` is always provided; while disabled, every method except `close()` throws `FLOW_DISABLED`. |
|
|
245
|
+
| `metrics.enabled` | `boolean` | `false` | `MetricsService` is always provided; while disabled, every method throws `METRICS_DISABLED`. |
|
|
246
|
+
| `metrics.cacheTtlMs` | `number` | `5_000` | TTL of the metrics snapshot cache. `0` disables caching. Must be `>= 0`. |
|
|
247
|
+
| `telemetry` | `Telemetry` | — | A BullMQ `Telemetry` instance (typically `new BullMQOtel(...)` from the optional `bullmq-otel` peer), attached to every `Queue` and `Worker`. |
|
|
248
|
+
| `shutdown.drainTimeoutMs` | `number` | `30_000` | Upper bound on the graceful drain before workers are force-closed. Must be `> 0`. |
|
|
249
|
+
| `shutdown.drainOnShutdown` | `boolean` | `false` | Development and test only: once the HTTP server has stopped, call `drain()` on every cached queue, which **removes** its waiting jobs (delayed and active jobs are left alone). Leave it `false` in production. |
|
|
250
|
+
| `connectionReadyTimeoutMs` | `number` | `10_000` | Mode B only: how long to wait for Redis `ready` before failing with `CONNECTION_TIMEOUT`. |
|
|
251
|
+
| `isGlobal` | `boolean` | `true` | Register the module globally, so `QueueService` is injectable without re-importing. |
|
|
252
252
|
|
|
253
253
|
> [!WARNING]
|
|
254
254
|
> **`shutdown.drainOnShutdown` deletes jobs.** When it is `true`, shutdown calls `drain()` on every
|
|
@@ -270,18 +270,22 @@ Worker and QueueEvents connections are duplicated from this client with
|
|
|
270
270
|
|
|
271
271
|
```typescript
|
|
272
272
|
import { BymaxQueueModule } from '@bymax-one/nest-queue'
|
|
273
|
-
import {
|
|
273
|
+
import { ConnectionManager } from '@bymax-one/nest-cache'
|
|
274
274
|
import type { Redis } from 'ioredis'
|
|
275
275
|
|
|
276
276
|
BymaxQueueModule.forRootAsync({
|
|
277
|
-
inject: [
|
|
278
|
-
useFactory: (
|
|
279
|
-
connection: { client:
|
|
277
|
+
inject: [ConnectionManager],
|
|
278
|
+
useFactory: (cache: ConnectionManager) => ({
|
|
279
|
+
connection: { client: cache.getClient() as Redis },
|
|
280
280
|
isGlobal: true,
|
|
281
281
|
}),
|
|
282
282
|
})
|
|
283
283
|
```
|
|
284
284
|
|
|
285
|
+
`BymaxCacheModule` is global by default and provides `ConnectionManager`; `getClient()` returns the shared
|
|
286
|
+
client. It is typed `Redis | Cluster`, and Mode A takes a standalone `Redis`, so run
|
|
287
|
+
`@bymax-one/nest-cache` in standalone mode.
|
|
288
|
+
|
|
285
289
|
Mode A is the recommended setup when you already have `@bymax-one/nest-cache` in the app.
|
|
286
290
|
One client is shared; the lib duplicates it per role and applies the correct retry policy
|
|
287
291
|
without touching your cache connection.
|
|
@@ -667,7 +671,7 @@ BymaxQueueModule.forRoot({
|
|
|
667
671
|
|
|
668
672
|
## 🛑 Graceful Shutdown
|
|
669
673
|
|
|
670
|
-
`QueueLifecycle` runs automatically when NestJS closes the application context, through its `onModuleDestroy`
|
|
674
|
+
`QueueLifecycle` runs automatically when NestJS closes the application context, through its `onModuleDestroy` and `onApplicationShutdown` hooks (call `app.enableShutdownHooks()` so that a termination signal closes the context).
|
|
671
675
|
It implements a **bounded drain protocol** with at-least-once semantics:
|
|
672
676
|
|
|
673
677
|
1. All workers are closed with a grace period (`drainTimeoutMs`, default 30 s).
|
|
@@ -675,7 +679,11 @@ It implements a **bounded drain protocol** with at-least-once semantics:
|
|
|
675
679
|
timeout fires — whichever comes first.
|
|
676
680
|
3. On timeout, the worker is force-closed (`worker.close(true)`), preserving stalled
|
|
677
681
|
jobs for reprocessing by the next instance.
|
|
678
|
-
4.
|
|
682
|
+
4. Queue-events and the connections the library opened for workers and queue-events are closed.
|
|
683
|
+
5. Once NestJS has stopped the HTTP server (`onApplicationShutdown`), the flow producer, the
|
|
684
|
+
cached queues and, in Mode B, the library's own Redis connection are closed. NestJS runs
|
|
685
|
+
`onModuleDestroy` before it stops the HTTP server, so a request that is still being served can
|
|
686
|
+
enqueue a job after step 4; those resources stay open until the server is down.
|
|
679
687
|
|
|
680
688
|
**Handlers must be idempotent.** Use `jobId`, `deduplication`, or application-level
|
|
681
689
|
idempotency keys to guard against reprocessing a job that was partially handled before
|