@bymax-one/nest-queue 1.2.10 → 1.2.11

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 CHANGED
@@ -11,6 +11,52 @@ versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
11
11
 
12
12
  ---
13
13
 
14
+ ## [1.2.11] — 2026-11-06
15
+
16
+ Action required only if you extend or replace `QueueLifecycle`, or rely on the order in which the
17
+ library closes its resources: the producer side now closes after the HTTP server stops, see the Apply
18
+ note. Healthy use is unchanged. The public API and the peer ranges are unchanged; the declaration
19
+ files differ because `QueueLifecycle` now also implements `OnApplicationShutdown` (and in JSDoc).
20
+ Measured with `diff -r` of a fresh build against the unpacked 1.2.10 tarball, the runtime bundles
21
+ (`index.mjs`, `index.cjs`) differ in code.
22
+
23
+ ### Fixed
24
+
25
+ - **A request still being served when the application closed could not enqueue.** NestJS runs every
26
+ `onModuleDestroy` hook before it stops the HTTP server, and `QueueLifecycle` closed the flow
27
+ producer, the cached queues and the library-owned Redis connection in that hook. A request that
28
+ enqueued in the window between the two (a request in flight during `app.close()`, or a
29
+ `SIGTERM` with shutdown hooks enabled) failed with `Connection is closed.` in Mode B. Those three
30
+ resources are now released in `onApplicationShutdown`, which NestJS runs after the server has
31
+ stopped. Consumption still stops first: workers drain within `shutdown.drainTimeoutMs`, then
32
+ queue-events and the connections opened for workers and queue-events close, all in
33
+ `onModuleDestroy`. `shutdown.drainOnShutdown` now runs in `onApplicationShutdown` too, right before
34
+ the queues close, so a job enqueued late is drained with the rest. The log line `Queue shutdown
35
+ complete in Nms (forced N worker(s))` is now written once, at the end, and its time covers both
36
+ hooks. In Mode A the consumer's client is untouched, as before.
37
+
38
+ ### Changed
39
+
40
+ - **The shipped bundles no longer carry comments.** `dist/server/index.mjs` and `index.cjs` are
41
+ smaller; the declaration files keep their JSDoc, and the comment removal does not change the
42
+ runtime behaviour of the bundles.
43
+
44
+ ### Documentation
45
+
46
+ - **The README describes the two shutdown hooks** and when each resource closes.
47
+
48
+ #### Apply to a derived backend
49
+
50
+ - If you subclass or wrap `QueueLifecycle`, or call `onModuleDestroy()` yourself to tear the library
51
+ down in a test, also call `onApplicationShutdown()` afterwards: the flow producer, the cached
52
+ queues and the library-owned connection are no longer closed by `onModuleDestroy()`. Nest calls
53
+ both when the application closes.
54
+ - If you enqueue from a hook that runs after the library's `onModuleDestroy` (for example your own
55
+ `beforeApplicationShutdown`), the enqueue now succeeds instead of failing with
56
+ `Connection is closed.`; if you worked around that failure, the workaround can go.
57
+
58
+ ---
59
+
14
60
  ## [1.2.10] — 2026-11-01
15
61
 
16
62
  Action required only if code relies on the status of a failed schedule: `upsertJobScheduler` no
@@ -821,7 +867,8 @@ v6 peer range.
821
867
 
822
868
  ---
823
869
 
824
- [Unreleased]: https://github.com/bymaxone/nest-queue/compare/v1.2.10...HEAD
870
+ [Unreleased]: https://github.com/bymaxone/nest-queue/compare/v1.2.11...HEAD
871
+ [1.2.11]: https://github.com/bymaxone/nest-queue/compare/v1.2.10...v1.2.11
825
872
  [1.2.10]: https://github.com/bymaxone/nest-queue/compare/v1.2.9...v1.2.10
826
873
  [1.2.9]: https://github.com/bymaxone/nest-queue/compare/v1.2.8...v1.2.9
827
874
  [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: after the workers close, 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. |
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
@@ -667,7 +667,7 @@ BymaxQueueModule.forRoot({
667
667
 
668
668
  ## 🛑 Graceful Shutdown
669
669
 
670
- `QueueLifecycle` runs automatically when NestJS closes the application context, through its `onModuleDestroy` hook (call `app.enableShutdownHooks()` so that a termination signal closes the context).
670
+ `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
671
  It implements a **bounded drain protocol** with at-least-once semantics:
672
672
 
673
673
  1. All workers are closed with a grace period (`drainTimeoutMs`, default 30 s).
@@ -675,7 +675,11 @@ It implements a **bounded drain protocol** with at-least-once semantics:
675
675
  timeout fires — whichever comes first.
676
676
  3. On timeout, the worker is force-closed (`worker.close(true)`), preserving stalled
677
677
  jobs for reprocessing by the next instance.
678
- 4. In Mode B, Redis connections are disconnected after all workers are closed.
678
+ 4. Queue-events and the connections the library opened for workers and queue-events are closed.
679
+ 5. Once NestJS has stopped the HTTP server (`onApplicationShutdown`), the flow producer, the
680
+ cached queues and, in Mode B, the library's own Redis connection are closed. NestJS runs
681
+ `onModuleDestroy` before it stops the HTTP server, so a request that is still being served can
682
+ enqueue a job after step 4; those resources stay open until the server is down.
679
683
 
680
684
  **Handlers must be idempotent.** Use `jobId`, `deduplication`, or application-level
681
685
  idempotency keys to guard against reprocessing a job that was partially handled before