@fluojs/queue 1.0.1 → 1.0.2

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/README.ko.md CHANGED
@@ -92,7 +92,7 @@ QueueModule.forRoot({ clientName: 'jobs' })
92
92
 
93
93
  ### 부트스트랩 및 종료 수명 주기
94
94
 
95
- Queue는 애플리케이션 부트스트랩 중 worker를 탐색하고 Queue가 소유하는 BullMQ 리소스를 만들지만, BullMQ worker processor는 runtime이 전체 애플리케이션 bootstrap/readiness sequence 완료를 표시한 뒤에만 시작합니다. 다른 `onApplicationBootstrap()` hook에서 enqueue한 job은 Queue 서비스가 초기화된 뒤에는 받을 수 있으며, processor는 뒤에 실행되는 async bootstrap hook이나 애플리케이션 readiness보다 앞서 실행되지 않고 bootstrap-ready handoff 이후 실행됩니다.
95
+ Queue는 애플리케이션 부트스트랩 중 worker를 탐색하고 Queue가 소유하는 BullMQ 리소스를 만들지만, BullMQ worker processor는 runtime이 전체 애플리케이션 bootstrap/readiness sequence 완료를 표시한 뒤에만 시작합니다. 다른 `onApplicationBootstrap()` hook에서 enqueue한 job은 Queue 서비스가 초기화된 뒤에는 받을 수 있으며, processor는 뒤에 실행되는 async bootstrap hook이나 애플리케이션 readiness보다 앞서 실행되지 않고 bootstrap-ready handoff 이후 실행됩니다. Queue status는 해당 BullMQ processor가 실제로 시작될 때까지 degraded readiness를 보고합니다. Processor 시작에 실패하면 lifecycle이 `failed`로 이동하고, status snapshot은 worker를 ready로 숨기지 않고 실패를 노출합니다.
96
96
 
97
97
  애플리케이션 종료가 시작되면 Queue는 상태를 `stopping`으로 바꾸고 새 enqueue를 거부한 다음 Queue 소유 worker/queue/connection을 닫고 pending dead-letter write를 drain합니다. Worker 종료는 `workerShutdownTimeoutMs`로 bounded wait를 적용하므로 끝나지 않는 active processor가 애플리케이션 종료를 무기한 막을 수 없습니다. Timeout이 지나면 Queue는 로그를 남기고 BullMQ worker에 force-close를 요청한 뒤 나머지 리소스 정리를 계속합니다.
98
98
 
@@ -122,7 +122,7 @@ Job은 JSON으로 직렬화 가능한 plain object여야 합니다. Queue는 enq
122
122
  ### 핵심 구성 요소
123
123
  - `QueueModule`: 큐 기능을 위한 기본 모듈입니다.
124
124
  - `QueueModule.forRoot(options)`: 애플리케이션 수준 큐 등록을 구성합니다.
125
- - `QueueLifecycleService`: 작업을 큐에 추가(`enqueue(job)`)하기 위한 기본 서비스입니다.
125
+ - `QueueLifecycleService`: 작업을 큐에 추가하고 lifecycle/status snapshot을 생성(`enqueue(job)`, `createPlatformStatusSnapshot()`)하기 위한 기본 서비스입니다.
126
126
  - `@QueueWorker(JobClass, options?)`: 특정 작업을 처리할 핸들러를 지정하는 데코레이터입니다.
127
127
  - `QUEUE`: queue facade를 위한 호환성 주입 토큰입니다.
128
128
  - `createQueuePlatformStatusSnapshot(...)`: lifecycle/readiness diagnostics를 위한 status snapshot helper입니다.
@@ -136,9 +136,9 @@ Job은 JSON으로 직렬화 가능한 plain object여야 합니다. Queue는 enq
136
136
  - `QueueBackoffType`: 지원되는 retry backoff strategy 이름(`fixed`, `exponential`)입니다.
137
137
  - `QueueBackoffOptions`: 재시도 백오프 설정(`type`, `delayMs`)을 위한 타입입니다.
138
138
  - `QueueRateLimiterOptions`: worker 수준 distributed rate limiter 설정(`max`, `duration`)을 위한 타입입니다.
139
- - `QueueLifecycleState`: Queue status adapter가 보고하는 lifecycle state(`idle`, `starting`, `started`, `stopping`, `stopped`)입니다.
140
- - `QueueStatusAdapterInput`: `createQueuePlatformStatusSnapshot(...)`에 전달하는 normalized queue metrics 타입입니다.
141
- - `QueuePlatformStatusSnapshot`: status helper 반환하는 Queue 전용 readiness, health, ownership, detail snapshot 타입입니다.
139
+ - `QueueLifecycleState`: Queue status adapter가 보고하는 lifecycle state(`idle`, `starting`, `started`, `stopping`, `stopped`, `failed`)입니다.
140
+ - `QueueStatusAdapterInput`: `createQueuePlatformStatusSnapshot(...)`에 전달하는 normalized queue metrics worker-start diagnostics 타입입니다.
141
+ - `QueuePlatformStatusSnapshot`: status helper `QueueLifecycleService.createPlatformStatusSnapshot()`이 반환하는 Queue 전용 readiness, health, ownership, detail snapshot 타입입니다.
142
142
 
143
143
  `QueueModuleOptions`에는 `workerShutdownTimeoutMs`, `defaultDeadLetterMaxEntries` 같은 lifecycle 및 dead-letter retention 설정도 포함됩니다.
144
144
 
@@ -148,7 +148,7 @@ Job은 JSON으로 직렬화 가능한 plain object여야 합니다. Queue는 enq
148
148
  - `workerShutdownTimeoutMs`: 종료 중 active worker processor를 기다리는 최대 시간입니다. 시간이 지나면 BullMQ worker를 force-close합니다. 기본값은 `30_000`입니다.
149
149
  - `defaultDeadLetterMaxEntries`: job별로 유지할 dead-letter record의 최대 개수이며, trimming을 끄려면 `false`를 지정합니다. 기본값은 `1_000`입니다.
150
150
 
151
- `createQueuePlatformStatusSnapshot(...)`은 Queue가 `started`에 도달한 뒤에만 readiness를 `ready`로 보고합니다. `starting`은 degraded readiness, `stopping`/`stopped`는 not-ready로 보고합니다. Snapshot details에는 Redis dependency id, lifecycle state, ready/discovered worker 수, pending dead-letter write 수, dead-letter drain timeout, `workerShutdownTimeoutMs`가 포함됩니다.
151
+ `QueueLifecycleService.createPlatformStatusSnapshot()`은 `createQueuePlatformStatusSnapshot(...)`과 같은 공개 snapshot 계약을 사용합니다. Queue가 `started`에 도달하고 탐색된 모든 BullMQ worker processor가 시작된 뒤에만 readiness를 `ready`로 보고합니다. Processor가 아직 pending인 `started` resource와 `starting`은 degraded readiness, `stopping`/`stopped`는 not-ready, worker-start failure는 `workerStartFailures`와 `lastWorkerStartFailure` details를 포함해 not-ready/unhealthy로 보고합니다. Snapshot details에는 Redis dependency id, lifecycle state, ready/discovered worker 수, pending dead-letter write 수, dead-letter drain timeout, `workerShutdownTimeoutMs`가 포함됩니다.
152
152
 
153
153
  singleton `@QueueWorker()` provider/controller만 등록됩니다. request/transient worker는 discovery 중 건너뜁니다.
154
154
 
package/README.md CHANGED
@@ -92,7 +92,7 @@ QueueModule.forRoot({ clientName: 'jobs' })
92
92
 
93
93
  ### Bootstrap and Shutdown Lifecycle
94
94
 
95
- Queue discovers workers and creates queue-owned BullMQ resources during application bootstrap, but BullMQ worker processors are started only after the runtime marks the full application bootstrap/readiness sequence complete. Jobs enqueued by other `onApplicationBootstrap()` hooks can be accepted once the Queue service is initialized, and their processors run after the bootstrap-ready handoff instead of racing ahead of later async bootstrap hooks or application readiness.
95
+ Queue discovers workers and creates queue-owned BullMQ resources during application bootstrap, but BullMQ worker processors are started only after the runtime marks the full application bootstrap/readiness sequence complete. Jobs enqueued by other `onApplicationBootstrap()` hooks can be accepted once the Queue service is initialized, and their processors run after the bootstrap-ready handoff instead of racing ahead of later async bootstrap hooks or application readiness. Queue status reports degraded readiness until those BullMQ processors have actually started; if a processor fails to start, the lifecycle moves to `failed` and status snapshots expose the failure instead of reporting the workers as ready.
96
96
 
97
97
  Application shutdown marks Queue as `stopping`, rejects new enqueue attempts, closes queue-owned workers/queues/connections, and drains pending dead-letter writes. Worker shutdown is bounded by `workerShutdownTimeoutMs` so an active processor that never settles cannot block application shutdown indefinitely. When the timeout elapses, Queue logs the timeout and asks BullMQ to force-close the worker before continuing resource cleanup.
98
98
 
@@ -122,7 +122,7 @@ Treat low-level provider assembly as an internal implementation detail: low-leve
122
122
  ### Core
123
123
  - `QueueModule`: Main entry point for queue registration.
124
124
  - `QueueModule.forRoot(options)`: Registers queue support for an application module.
125
- - `QueueLifecycleService`: Primary service for enqueuing jobs (`enqueue(job)`).
125
+ - `QueueLifecycleService`: Primary service for enqueuing jobs and creating lifecycle/status snapshots (`enqueue(job)`, `createPlatformStatusSnapshot()`).
126
126
  - `@QueueWorker(JobClass, options?)`: Decorator to mark a class as a job handler.
127
127
  - `QUEUE`: Compatibility injection token for the queue facade.
128
128
  - `createQueuePlatformStatusSnapshot(...)`: Status snapshot helper for lifecycle/readiness diagnostics.
@@ -136,9 +136,9 @@ Treat low-level provider assembly as an internal implementation detail: low-leve
136
136
  - `QueueBackoffType`: Supported retry backoff strategy names (`fixed`, `exponential`).
137
137
  - `QueueBackoffOptions`: Retry backoff settings (`type`, `delayMs`).
138
138
  - `QueueRateLimiterOptions`: Worker-level distributed rate limiter settings (`max`, `duration`).
139
- - `QueueLifecycleState`: Lifecycle states reported by Queue status adapters (`idle`, `starting`, `started`, `stopping`, `stopped`).
140
- - `QueueStatusAdapterInput`: Normalized queue metrics passed to `createQueuePlatformStatusSnapshot(...)`.
141
- - `QueuePlatformStatusSnapshot`: Queue-specific readiness, health, ownership, and detail snapshot returned by the status helper.
139
+ - `QueueLifecycleState`: Lifecycle states reported by Queue status adapters (`idle`, `starting`, `started`, `stopping`, `stopped`, `failed`).
140
+ - `QueueStatusAdapterInput`: Normalized queue metrics and worker-start diagnostics passed to `createQueuePlatformStatusSnapshot(...)`.
141
+ - `QueuePlatformStatusSnapshot`: Queue-specific readiness, health, ownership, and detail snapshot returned by the status helper and `QueueLifecycleService.createPlatformStatusSnapshot()`.
142
142
 
143
143
  `QueueModuleOptions` also includes lifecycle and dead-letter retention controls such as `workerShutdownTimeoutMs` and `defaultDeadLetterMaxEntries`.
144
144
 
@@ -148,7 +148,7 @@ Treat low-level provider assembly as an internal implementation detail: low-leve
148
148
  - `workerShutdownTimeoutMs`: maximum time to wait for active worker processors during shutdown before force-closing the BullMQ worker. Defaults to `30_000`.
149
149
  - `defaultDeadLetterMaxEntries`: maximum retained dead-letter records per job, or `false` to disable trimming. Defaults to `1_000`.
150
150
 
151
- `createQueuePlatformStatusSnapshot(...)` reports readiness as `ready` only after Queue reaches `started`; `starting` reports degraded readiness, and `stopping`/`stopped` report not-ready. Snapshot details include the Redis dependency id, lifecycle state, ready/discovered worker counts, pending dead-letter writes, the dead-letter drain timeout, and `workerShutdownTimeoutMs`.
151
+ `QueueLifecycleService.createPlatformStatusSnapshot()` uses the same public snapshot contract as `createQueuePlatformStatusSnapshot(...)`. It reports readiness as `ready` only after Queue reaches `started` and every discovered BullMQ worker processor has started. `started` resources with pending processors report degraded readiness, `starting` reports degraded readiness, `stopping`/`stopped` report not-ready, and worker-start failures report not-ready/unhealthy with `workerStartFailures` and `lastWorkerStartFailure` details. Snapshot details include the Redis dependency id, lifecycle state, ready/discovered worker counts, pending dead-letter writes, the dead-letter drain timeout, and `workerShutdownTimeoutMs`.
152
152
 
153
153
  Only singleton `@QueueWorker()` providers/controllers are registered. Request/transient workers are skipped during discovery.
154
154
 
package/dist/helpers.d.ts CHANGED
@@ -15,6 +15,8 @@ export interface DiscoveryCandidate {
15
15
  targetType: Function;
16
16
  token: Token;
17
17
  }
18
+ /** Selects whether one compiled module participates in worker discovery. */
19
+ export type DiscoveryModuleFilter = (compiledModule: CompiledModule) => boolean;
18
20
  /**
19
21
  * Scope from provider.
20
22
  *
@@ -38,7 +40,7 @@ export declare function isClassProvider(provider: Provider): provider is Extract
38
40
  * @param compiledModules The compiled modules.
39
41
  * @returns The collect discovery candidates result.
40
42
  */
41
- export declare function collectDiscoveryCandidates(compiledModules: readonly CompiledModule[]): DiscoveryCandidate[];
43
+ export declare function collectDiscoveryCandidates(compiledModules: readonly CompiledModule[], moduleFilter?: DiscoveryModuleFilter): DiscoveryCandidate[];
42
44
  /**
43
45
  * Normalize positive integer.
44
46
  *
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,QAAQ,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAU3D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,IAAI,OAAO,CAAC,QAAQ,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAEzH;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,eAAe,EAAE,SAAS,cAAc,EAAE,GAAG,kBAAkB,EAAE,CAoC3G;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY5F;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,EACjC,QAAQ,EAAE,MAAM,GAAG,KAAK,GACvB,MAAM,GAAG,KAAK,CAgBhB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,uBAAuB,GAAG,SAAS,GAAG,uBAAuB,GAAG,SAAS,CAS1H;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,mBAAmB,EAAE,MAAM,KAAK,GAC/B,OAAO,CAAC,CAAC,CAAC,CAeZ"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE1D;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,QAAQ,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,4EAA4E;AAC5E,MAAM,MAAM,qBAAqB,GAAG,CAAC,cAAc,EAAE,cAAc,KAAK,OAAO,CAAC;AAEhF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,KAAK,CAU3D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,IAAI,OAAO,CAAC,QAAQ,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,CAAC,CAEzH;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,YAAY,GAAE,qBAAkC,GAC/C,kBAAkB,EAAE,CAwCtB;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAY5F;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,EACjC,QAAQ,EAAE,MAAM,GAAG,KAAK,GACvB,MAAM,GAAG,KAAK,CAgBhB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,uBAAuB,GAAG,SAAS,GAAG,uBAAuB,GAAG,SAAS,CAS1H;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,SAAS,EAAE,MAAM,EACjB,mBAAmB,EAAE,MAAM,KAAK,GAC/B,OAAO,CAAC,CAAC,CAAC,CAeZ"}
package/dist/helpers.js CHANGED
@@ -8,6 +8,8 @@ import { getClassDiMetadata } from '@fluojs/core/internal';
8
8
  * Describes the discovery candidate contract.
9
9
  */
10
10
 
11
+ /** Selects whether one compiled module participates in worker discovery. */
12
+
11
13
  /**
12
14
  * Scope from provider.
13
15
  *
@@ -40,9 +42,12 @@ export function isClassProvider(provider) {
40
42
  * @param compiledModules The compiled modules.
41
43
  * @returns The collect discovery candidates result.
42
44
  */
43
- export function collectDiscoveryCandidates(compiledModules) {
45
+ export function collectDiscoveryCandidates(compiledModules, moduleFilter = () => true) {
44
46
  const candidates = [];
45
47
  for (const compiledModule of compiledModules) {
48
+ if (!moduleFilter(compiledModule)) {
49
+ continue;
50
+ }
46
51
  for (const provider of compiledModule.definition.providers ?? []) {
47
52
  if (typeof provider === 'function') {
48
53
  candidates.push({
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKhE,OAAO,KAAK,EAAgC,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAsCnF;;GAEG;AACH,qBAAa,WAAW;IACtB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,kBAAuB,GAAG,UAAU;CAS7D"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKhE,OAAO,KAAK,EAAgC,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAuCnF;;GAEG;AACH,qBAAa,WAAW;IACtB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,kBAAuB,GAAG,UAAU;CAS7D"}
package/dist/module.js CHANGED
@@ -14,6 +14,7 @@ function normalizeQueueModuleOptions(options = {}) {
14
14
  defaultConcurrency: normalizePositiveInteger(options.defaultConcurrency, 1),
15
15
  defaultDeadLetterMaxEntries: normalizePositiveIntegerOrFalse(options.defaultDeadLetterMaxEntries, 1_000),
16
16
  defaultRateLimiter,
17
+ global: options.global ?? true,
17
18
  workerShutdownTimeoutMs: normalizePositiveInteger(options.workerShutdownTimeoutMs, 30_000)
18
19
  };
19
20
  }
package/dist/service.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Container } from '@fluojs/di';
2
2
  import type { ApplicationLogger, CompiledModule, OnApplicationBootstrap, OnApplicationShutdown, OnModuleDestroy } from '@fluojs/runtime';
3
3
  import { type BootstrapReadySignal } from '@fluojs/runtime/internal';
4
+ import { type QueuePlatformStatusSnapshot } from './status.js';
4
5
  import type { NormalizedQueueModuleOptions, Queue } from './types.js';
5
6
  /**
6
7
  * Lifecycle-managed queue runtime for worker discovery and job dispatch.
@@ -20,10 +21,14 @@ export declare class QueueLifecycleService implements Queue, OnApplicationBootst
20
21
  private readonly ownedConnections;
21
22
  private readonly deadLetterManager;
22
23
  private readonly readyWorkers;
24
+ private readonly runningWorkerJobNames;
25
+ private readonly failedWorkerJobNames;
26
+ private readonly workerStartFailures;
23
27
  private lifecycleState;
24
28
  private redisClient;
25
29
  private startPromise;
26
30
  private shutdownPromise;
31
+ private startupFailureRollbackPromise;
27
32
  constructor(options: NormalizedQueueModuleOptions, runtimeContainer: Container, compiledModules: readonly CompiledModule[], logger: ApplicationLogger, bootstrapReadySignal?: BootstrapReadySignal);
28
33
  onApplicationBootstrap(): Promise<void>;
29
34
  onApplicationShutdown(): Promise<void>;
@@ -42,9 +47,10 @@ export declare class QueueLifecycleService implements Queue, OnApplicationBootst
42
47
  *
43
48
  * @returns A structured snapshot describing lifecycle state, discovered workers, and pending dead-letter writes.
44
49
  */
45
- createPlatformStatusSnapshot(): import("./status.js").QueuePlatformStatusSnapshot;
50
+ createPlatformStatusSnapshot(): QueuePlatformStatusSnapshot;
46
51
  private ensureStarted;
47
52
  private startLifecycle;
53
+ private shouldDiscoverWorkersInModule;
48
54
  private handleStartupFailure;
49
55
  private resolveRedisClient;
50
56
  private getRedisClient;
@@ -58,12 +64,17 @@ export declare class QueueLifecycleService implements Queue, OnApplicationBootst
58
64
  private registerInitializedWorker;
59
65
  private scheduleReadyWorkers;
60
66
  private runWorker;
67
+ private markWorkerReadyWhenStarted;
68
+ private recordWorkerStartFailure;
69
+ private rollbackAfterWorkerStartupFailure;
70
+ private toErrorMessage;
61
71
  private cleanupWorkerInitializationFailure;
62
72
  private createOwnedConnection;
63
73
  private executeWorker;
64
74
  private resolveWorkerHandler;
65
75
  private rehydrateWorkerPayload;
66
76
  private shutdown;
77
+ private waitForStartupFailureRollback;
67
78
  private waitForInFlightStartup;
68
79
  private closeInitializedResources;
69
80
  private tryCloseWorker;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAQlC,OAAO,KAAK,EACV,4BAA4B,EAC5B,KAAK,EAIN,MAAM,YAAY,CAAC;AAqHpB;;;;;GAKG;AACH,qBACa,qBAAsB,YAAW,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,eAAe;IAa/G,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAhBvC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAkD;IACvF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IACpE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqC;IACtE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA8B;IAC/D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyB;IAC3D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,eAAe,CAA4B;gBAGhC,OAAO,EAAE,4BAA4B,EACrC,gBAAgB,EAAE,SAAS,EAC3B,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB,EACzB,oBAAoB,GAAE,oBAAuD;IAK1F,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IA2B9D;;;;OAIG;IACH,4BAA4B;YAYd,aAAa;YAwBb,cAAc;YAgBd,oBAAoB;YASpB,kBAAkB;IAgBhC,OAAO,CAAC,cAAc;YAQR,iBAAiB;YAOjB,yBAAyB;IAyBvC,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,0BAA0B;IAkBlC,OAAO,CAAC,0BAA0B;IASlC,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,oBAAoB;IAwB5B,OAAO,CAAC,SAAS;YAgBH,kCAAkC;YAkBlC,qBAAqB;YAiBrB,aAAa;YAOb,oBAAoB;IAyBlC,OAAO,CAAC,sBAAsB;YAQhB,QAAQ;YAyBR,sBAAsB;YAgBtB,yBAAyB;YAsBzB,cAAc;YAkBd,aAAa;YAQb,uBAAuB;CAOtC"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAKL,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAKlC,OAAO,EAGL,KAAK,2BAA2B,EACjC,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EACV,4BAA4B,EAC5B,KAAK,EAIN,MAAM,YAAY,CAAC;AA8HpB;;;;;GAKG;AACH,qBACa,qBAAsB,YAAW,KAAK,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,eAAe;IAiB/G,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IApBvC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAkD;IACvF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IACpE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAqC;IACtE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA8B;IAC/D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyB;IAC3D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAqB;IAC3D,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAqB;IAC1D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA4B;IAChE,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,6BAA6B,CAA4B;gBAG9C,OAAO,EAAE,4BAA4B,EACrC,gBAAgB,EAAE,SAAS,EAC3B,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,MAAM,EAAE,iBAAiB,EACzB,oBAAoB,GAAE,oBAAuD;IAK1F,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC;;;;;;;OAOG;IACG,OAAO,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IA2B9D;;;;OAIG;IACH,4BAA4B,IAAI,2BAA2B;YAgB7C,aAAa;YAwBb,cAAc;IAqB5B,OAAO,CAAC,6BAA6B;YAQvB,oBAAoB;YASpB,kBAAkB;IAgBhC,OAAO,CAAC,cAAc;YAQR,iBAAiB;YAOjB,yBAAyB;IAyBvC,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,oBAAoB;IAa5B,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,0BAA0B;IAkBlC,OAAO,CAAC,0BAA0B;IASlC,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,oBAAoB;IAuC5B,OAAO,CAAC,SAAS;YA+BH,0BAA0B;IAkBxC,OAAO,CAAC,wBAAwB;IA6BhC,OAAO,CAAC,iCAAiC;IAgBzC,OAAO,CAAC,cAAc;YAIR,kCAAkC;YAkBlC,qBAAqB;YAiBrB,aAAa;YAOb,oBAAoB;IAyBlC,OAAO,CAAC,sBAAsB;YAQhB,QAAQ;YA0BR,6BAA6B;YAS7B,sBAAsB;YAgBtB,yBAAyB;YAuBzB,cAAc;YAkBd,aAAa;YAQb,uBAAuB;CAOtC"}
package/dist/service.js CHANGED
@@ -12,7 +12,7 @@ import { Queue as BullQueue, Worker as BullWorker } from 'bullmq';
12
12
  import { QueueDeadLetterManager } from './dead-letter-manager.js';
13
13
  import { normalizePositiveInteger, withTimeout } from './helpers.js';
14
14
  import { createQueuePlatformStatusSnapshot } from './status.js';
15
- import { QUEUE_OPTIONS } from './tokens.js';
15
+ import { QUEUE, QUEUE_OPTIONS } from './tokens.js';
16
16
  import { discoverQueueWorkerDescriptors } from './worker-discovery.js';
17
17
  const IMMEDIATE_BOOTSTRAP_READY_SIGNAL = {
18
18
  wait: () => Promise.resolve()
@@ -77,10 +77,14 @@ class QueueLifecycleService {
77
77
  ownedConnections = [];
78
78
  deadLetterManager;
79
79
  readyWorkers = [];
80
+ runningWorkerJobNames = new Set();
81
+ failedWorkerJobNames = new Set();
82
+ workerStartFailures = [];
80
83
  lifecycleState = 'idle';
81
84
  redisClient;
82
85
  startPromise;
83
86
  shutdownPromise;
87
+ startupFailureRollbackPromise;
84
88
  constructor(options, runtimeContainer, compiledModules, logger, bootstrapReadySignal = IMMEDIATE_BOOTSTRAP_READY_SIGNAL) {
85
89
  this.options = options;
86
90
  this.runtimeContainer = runtimeContainer;
@@ -133,21 +137,26 @@ class QueueLifecycleService {
133
137
  * @returns A structured snapshot describing lifecycle state, discovered workers, and pending dead-letter writes.
134
138
  */
135
139
  createPlatformStatusSnapshot() {
140
+ const lastWorkerStartFailure = this.workerStartFailures[this.workerStartFailures.length - 1];
136
141
  return createQueuePlatformStatusSnapshot({
137
142
  dependencyId: getRedisComponentId(this.options.clientName),
138
143
  lifecycleState: this.lifecycleState,
144
+ ...(lastWorkerStartFailure ? {
145
+ lastWorkerStartFailure: lastWorkerStartFailure.message
146
+ } : {}),
139
147
  pendingDeadLetterWrites: this.deadLetterManager.pendingWriteCount,
140
148
  queuesReady: this.queuesByJobName.size,
149
+ workerStartFailures: this.workerStartFailures.length,
141
150
  workerShutdownTimeoutMs: this.options.workerShutdownTimeoutMs,
142
151
  workersDiscovered: this.descriptorsByJobType.size,
143
- workersReady: this.workersByJobName.size
152
+ workersReady: this.runningWorkerJobNames.size
144
153
  });
145
154
  }
146
155
  async ensureStarted() {
147
156
  if (this.lifecycleState === 'started') {
148
157
  return;
149
158
  }
150
- if (this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') {
159
+ if (this.lifecycleState === 'failed' || this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') {
151
160
  throw new Error(`Queue lifecycle state is ${this.lifecycleState}.`);
152
161
  }
153
162
  if (!this.startPromise) {
@@ -166,7 +175,7 @@ class QueueLifecycleService {
166
175
  const redis = await this.resolveRedisClient();
167
176
  this.redisClient = redis;
168
177
  this.descriptorsByJobType.clear();
169
- for (const [jobType, descriptor] of discoverQueueWorkerDescriptors(this.compiledModules, this.options, this.logger)) {
178
+ for (const [jobType, descriptor] of discoverQueueWorkerDescriptors(this.compiledModules, this.options, this.logger, compiledModule => this.shouldDiscoverWorkersInModule(compiledModule))) {
170
179
  this.descriptorsByJobType.set(jobType, descriptor);
171
180
  }
172
181
  await this.initializeWorkers(redis);
@@ -175,6 +184,12 @@ class QueueLifecycleService {
175
184
  this.scheduleReadyWorkers();
176
185
  }
177
186
  }
187
+ shouldDiscoverWorkersInModule(compiledModule) {
188
+ if (this.options.global) {
189
+ return true;
190
+ }
191
+ return compiledModule.accessibleTokens.has(_QueueLifecycleServic) || compiledModule.accessibleTokens.has(QUEUE);
192
+ }
178
193
  async handleStartupFailure() {
179
194
  await this.closeInitializedResources();
180
195
  if (this.lifecycleState === 'starting') {
@@ -278,23 +293,91 @@ class QueueLifecycleService {
278
293
  descriptor,
279
294
  worker
280
295
  } of workers) {
296
+ if (this.lifecycleState !== 'started') {
297
+ break;
298
+ }
281
299
  this.runWorker(descriptor, worker);
300
+ if (this.lifecycleState !== 'started') {
301
+ break;
302
+ }
282
303
  }
283
304
  }).catch(error => {
284
305
  if (this.lifecycleState !== 'started') {
285
306
  return;
286
307
  }
308
+ this.lifecycleState = 'failed';
309
+ this.workerStartFailures.push({
310
+ jobName: '*',
311
+ message: this.toErrorMessage(error),
312
+ workerName: 'bootstrap-ready-signal'
313
+ });
287
314
  this.logger.error('Failed to start queue workers after application bootstrap readiness.', error, 'QueueLifecycleService');
315
+ this.rollbackAfterWorkerStartupFailure();
288
316
  });
289
317
  }
290
318
  runWorker(descriptor, worker) {
291
319
  const runnableWorker = worker;
292
320
  if (typeof runnableWorker.run !== 'function') {
321
+ this.recordWorkerStartFailure(descriptor, new Error(`Queue worker ${descriptor.workerName} cannot start because BullMQ Worker.run() is unavailable.`));
322
+ return;
323
+ }
324
+ let runResult;
325
+ try {
326
+ runResult = runnableWorker.run();
327
+ } catch (error) {
328
+ this.recordWorkerStartFailure(descriptor, error);
329
+ return;
330
+ }
331
+ const runPromise = Promise.resolve(runResult);
332
+ void runPromise.catch(error => {
333
+ this.recordWorkerStartFailure(descriptor, error);
334
+ });
335
+ void this.markWorkerReadyWhenStarted(descriptor, runnableWorker, runPromise).catch(error => {
336
+ this.recordWorkerStartFailure(descriptor, error);
337
+ });
338
+ }
339
+ async markWorkerReadyWhenStarted(descriptor, worker, runPromise) {
340
+ if (typeof worker.waitUntilReady === 'function') {
341
+ await Promise.race([worker.waitUntilReady(), runPromise]);
342
+ } else {
343
+ await Promise.race([Promise.resolve(), runPromise]);
344
+ }
345
+ if (this.lifecycleState !== 'started' || this.failedWorkerJobNames.has(descriptor.jobName)) {
293
346
  return;
294
347
  }
295
- void Promise.resolve(runnableWorker.run()).catch(error => {
296
- this.logger.error(`Failed to start queue worker ${descriptor.workerName} after application bootstrap.`, error, 'QueueLifecycleService');
348
+ this.runningWorkerJobNames.add(descriptor.jobName);
349
+ }
350
+ recordWorkerStartFailure(descriptor, error) {
351
+ if (this.lifecycleState === 'stopping' || this.lifecycleState === 'stopped') {
352
+ return;
353
+ }
354
+ if (this.failedWorkerJobNames.has(descriptor.jobName)) {
355
+ return;
356
+ }
357
+ this.failedWorkerJobNames.add(descriptor.jobName);
358
+ this.runningWorkerJobNames.delete(descriptor.jobName);
359
+ this.workerStartFailures.push({
360
+ jobName: descriptor.jobName,
361
+ message: this.toErrorMessage(error),
362
+ workerName: descriptor.workerName
297
363
  });
364
+ if (this.lifecycleState === 'started' || this.lifecycleState === 'starting') {
365
+ this.lifecycleState = 'failed';
366
+ }
367
+ this.logger.error(`Failed to start queue worker ${descriptor.workerName} after application bootstrap.`, error, 'QueueLifecycleService');
368
+ this.rollbackAfterWorkerStartupFailure();
369
+ }
370
+ rollbackAfterWorkerStartupFailure() {
371
+ if (!this.startupFailureRollbackPromise) {
372
+ this.startupFailureRollbackPromise = this.closeInitializedResources().then(() => {
373
+ this.redisClient = undefined;
374
+ }).catch(rollbackError => {
375
+ this.logger.error('Failed to roll back queue resources after worker startup failure.', rollbackError, 'QueueLifecycleService');
376
+ });
377
+ }
378
+ }
379
+ toErrorMessage(error) {
380
+ return error instanceof Error ? error.message : String(error);
298
381
  }
299
382
  async cleanupWorkerInitializationFailure(resources) {
300
383
  if (resources.worker) {
@@ -362,6 +445,7 @@ class QueueLifecycleService {
362
445
  this.lifecycleState = 'stopping';
363
446
  this.shutdownPromise = (async () => {
364
447
  await this.waitForInFlightStartup();
448
+ await this.waitForStartupFailureRollback();
365
449
  await this.closeInitializedResources();
366
450
  await this.deadLetterManager.drainPendingWrites();
367
451
  this.lifecycleState = 'stopped';
@@ -370,6 +454,13 @@ class QueueLifecycleService {
370
454
  })();
371
455
  await this.shutdownPromise;
372
456
  }
457
+ async waitForStartupFailureRollback() {
458
+ if (!this.startupFailureRollbackPromise) {
459
+ return;
460
+ }
461
+ await this.startupFailureRollbackPromise;
462
+ this.startupFailureRollbackPromise = undefined;
463
+ }
373
464
  async waitForInFlightStartup() {
374
465
  const startup = this.startPromise;
375
466
  if (!startup) {
@@ -390,6 +481,7 @@ class QueueLifecycleService {
390
481
  this.workersByJobName.clear();
391
482
  this.queuesByJobName.clear();
392
483
  this.readyWorkers.splice(0);
484
+ this.runningWorkerJobNames.clear();
393
485
  for (const worker of workers) {
394
486
  await this.tryCloseWorker(worker);
395
487
  }
package/dist/status.d.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import type { PlatformHealthReport, PlatformReadinessReport, PlatformSnapshot } from '@fluojs/runtime';
2
2
  /** Lifecycle phases reported by the queue platform status adapter. */
3
- export type QueueLifecycleState = 'idle' | 'starting' | 'started' | 'stopping' | 'stopped';
3
+ export type QueueLifecycleState = 'idle' | 'starting' | 'started' | 'stopping' | 'stopped' | 'failed';
4
4
  /** Input payload used to derive queue readiness, health, and dependency details. */
5
5
  export interface QueueStatusAdapterInput {
6
6
  dependencyId?: string;
7
+ lastWorkerStartFailure?: string;
7
8
  lifecycleState: QueueLifecycleState;
8
9
  pendingDeadLetterWrites: number;
9
10
  queuesReady: number;
11
+ workerStartFailures?: number;
10
12
  workerShutdownTimeoutMs: number;
11
13
  workersDiscovered: number;
12
14
  workersReady: number;
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvG,sEAAsE;AACtE,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3F,oFAAoF;AACpF,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,mBAAmB,CAAC;IACpC,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,sFAAsF;AACtF,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAkFD;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,uBAAuB,GAAG,2BAA2B,CAmB7G"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvG,sEAAsE;AACtE,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtG,oFAAoF;AACpF,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,cAAc,EAAE,mBAAmB,CAAC;IACpC,uBAAuB,EAAE,MAAM,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uBAAuB,EAAE,MAAM,CAAC;IAChC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,sFAAsF;AACtF,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAoHD;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,uBAAuB,GAAG,2BAA2B,CAuB7G"}
package/dist/status.js CHANGED
@@ -5,7 +5,22 @@
5
5
  /** Queue-specific platform snapshot returned to health and readiness integrations. */
6
6
 
7
7
  function createReadiness(input) {
8
+ const workerStartFailures = input.workerStartFailures ?? 0;
9
+ if (input.lifecycleState === 'failed' || workerStartFailures > 0) {
10
+ return {
11
+ critical: true,
12
+ reason: 'Queue worker startup failed.',
13
+ status: 'not-ready'
14
+ };
15
+ }
8
16
  if (input.lifecycleState === 'started') {
17
+ if (input.workersReady < input.workersDiscovered) {
18
+ return {
19
+ critical: true,
20
+ reason: 'Queue workers are waiting for BullMQ processors to start.',
21
+ status: 'degraded'
22
+ };
23
+ }
9
24
  return {
10
25
  critical: true,
11
26
  status: 'ready'
@@ -39,6 +54,13 @@ function createReadiness(input) {
39
54
  };
40
55
  }
41
56
  function createHealth(input) {
57
+ const workerStartFailures = input.workerStartFailures ?? 0;
58
+ if (input.lifecycleState === 'failed' || workerStartFailures > 0) {
59
+ return {
60
+ reason: 'Queue worker startup failed.',
61
+ status: 'unhealthy'
62
+ };
63
+ }
42
64
  if (input.lifecycleState === 'stopped') {
43
65
  return {
44
66
  reason: 'Queue workers are stopped.',
@@ -57,6 +79,12 @@ function createHealth(input) {
57
79
  status: 'degraded'
58
80
  };
59
81
  }
82
+ if (input.lifecycleState === 'started' && input.workersReady < input.workersDiscovered) {
83
+ return {
84
+ reason: 'Queue workers are waiting for BullMQ processors to start.',
85
+ status: 'degraded'
86
+ };
87
+ }
60
88
  if (input.lifecycleState === 'started' && input.pendingDeadLetterWrites > 0) {
61
89
  return {
62
90
  reason: 'Queue dead-letter writes are still pending.',
@@ -81,13 +109,18 @@ function createHealth(input) {
81
109
  * @returns Readiness, health, ownership, and queue detail fields.
82
110
  */
83
111
  export function createQueuePlatformStatusSnapshot(input) {
112
+ const workerStartFailures = input.workerStartFailures ?? 0;
84
113
  return {
85
114
  details: {
86
115
  deadLetterDrainTimeoutMs: 5_000,
87
116
  dependencies: [input.dependencyId ?? 'redis.default'],
88
117
  lifecycleState: input.lifecycleState,
118
+ ...(input.lastWorkerStartFailure ? {
119
+ lastWorkerStartFailure: input.lastWorkerStartFailure
120
+ } : {}),
89
121
  pendingDeadLetterWrites: input.pendingDeadLetterWrites,
90
122
  queuesReady: input.queuesReady,
123
+ workerStartFailures,
91
124
  workerShutdownTimeoutMs: input.workerShutdownTimeoutMs,
92
125
  workersDiscovered: input.workersDiscovered,
93
126
  workersReady: input.workersReady
package/dist/types.d.ts CHANGED
@@ -49,6 +49,7 @@ export interface NormalizedQueueModuleOptions {
49
49
  defaultConcurrency: number;
50
50
  defaultDeadLetterMaxEntries: number | false;
51
51
  defaultRateLimiter?: QueueRateLimiterOptions;
52
+ global: boolean;
52
53
  workerShutdownTimeoutMs: number;
53
54
  }
54
55
  /** Metadata captured by {@link QueueWorker} during decorator evaluation. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,oFAAoF;AACpF,MAAM,WAAW,YAAY,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACxD,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;CAC9B;AAED,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,aAAa,CAAC;AAEvD,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,qEAAqE;AACrE,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2BAA2B,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7C,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;IAC7C,kHAAkH;IAClH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,yEAAyE;AACzE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2BAA2B,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5C,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;IAC7C,uBAAuB,EAAE,MAAM,CAAC;CACjC;AAED,4EAA4E;AAC5E,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAED,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB;;;;;OAKG;IACH,OAAO,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1D"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,oFAAoF;AACpF,MAAM,WAAW,YAAY,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM;IACxD,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;CAC9B;AAED,sEAAsE;AACtE,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,aAAa,CAAC;AAEvD,4DAA4D;AAC5D,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,qEAAqE;AACrE,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,2BAA2B,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7C,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;IAC7C,kHAAkH;IAClH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,yEAAyE;AACzE,MAAM,WAAW,4BAA4B;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2BAA2B,EAAE,MAAM,GAAG,KAAK,CAAC;IAC5C,kBAAkB,CAAC,EAAE,uBAAuB,CAAC;IAC7C,MAAM,EAAE,OAAO,CAAC;IAChB,uBAAuB,EAAE,MAAM,CAAC;CACjC;AAED,4EAA4E;AAC5E,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,kBAAkB,CAAC;CAC7B;AAED,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,KAAK,EAAE,KAAK,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB;;;;;OAKG;IACH,OAAO,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1D"}
@@ -1,4 +1,5 @@
1
1
  import type { ApplicationLogger, CompiledModule } from '@fluojs/runtime';
2
+ import { type DiscoveryModuleFilter } from './helpers.js';
2
3
  import type { NormalizedQueueModuleOptions, QueueJobType, QueueWorkerDescriptor } from './types.js';
3
4
  /**
4
5
  * Discover queue worker descriptors.
@@ -8,5 +9,5 @@ import type { NormalizedQueueModuleOptions, QueueJobType, QueueWorkerDescriptor
8
9
  * @param logger The logger.
9
10
  * @returns The discover queue worker descriptors result.
10
11
  */
11
- export declare function discoverQueueWorkerDescriptors(compiledModules: readonly CompiledModule[], options: NormalizedQueueModuleOptions, logger: ApplicationLogger): Map<QueueJobType, QueueWorkerDescriptor>;
12
+ export declare function discoverQueueWorkerDescriptors(compiledModules: readonly CompiledModule[], options: NormalizedQueueModuleOptions, logger: ApplicationLogger, moduleFilter?: DiscoveryModuleFilter): Map<QueueJobType, QueueWorkerDescriptor>;
12
13
  //# sourceMappingURL=worker-discovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-discovery.d.ts","sourceRoot":"","sources":["../src/worker-discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIzE,OAAO,KAAK,EAAE,4BAA4B,EAAE,YAAY,EAAE,qBAAqB,EAAuB,MAAM,YAAY,CAAC;AAEzH;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,OAAO,EAAE,4BAA4B,EACrC,MAAM,EAAE,iBAAiB,GACxB,GAAG,CAAC,YAAY,EAAE,qBAAqB,CAAC,CA4C1C"}
1
+ {"version":3,"file":"worker-discovery.d.ts","sourceRoot":"","sources":["../src/worker-discovery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGzE,OAAO,EAA8B,KAAK,qBAAqB,EAAkD,MAAM,cAAc,CAAC;AACtI,OAAO,KAAK,EAAE,4BAA4B,EAAE,YAAY,EAAE,qBAAqB,EAAuB,MAAM,YAAY,CAAC;AAEzH;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,eAAe,EAAE,SAAS,cAAc,EAAE,EAC1C,OAAO,EAAE,4BAA4B,EACrC,MAAM,EAAE,iBAAiB,EACzB,YAAY,CAAC,EAAE,qBAAqB,GACnC,GAAG,CAAC,YAAY,EAAE,qBAAqB,CAAC,CA4C1C"}
@@ -8,10 +8,10 @@ import { collectDiscoveryCandidates, normalizePositiveInteger, normalizeRateLimi
8
8
  * @param logger The logger.
9
9
  * @returns The discover queue worker descriptors result.
10
10
  */
11
- export function discoverQueueWorkerDescriptors(compiledModules, options, logger) {
11
+ export function discoverQueueWorkerDescriptors(compiledModules, options, logger, moduleFilter) {
12
12
  const descriptorsByJobType = new Map();
13
13
  const seenJobNames = new Set();
14
- for (const candidate of collectDiscoveryCandidates(compiledModules)) {
14
+ for (const candidate of collectDiscoveryCandidates(compiledModules, moduleFilter)) {
15
15
  const metadata = getQueueWorkerMetadata(candidate.targetType);
16
16
  if (!metadata) {
17
17
  continue;
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "redis",
11
11
  "dlq"
12
12
  ],
13
- "version": "1.0.1",
13
+ "version": "1.0.2",
14
14
  "private": false,
15
15
  "license": "MIT",
16
16
  "repository": {
@@ -39,9 +39,9 @@
39
39
  "dependencies": {
40
40
  "bullmq": "^5.58.0",
41
41
  "@fluojs/core": "^1.0.3",
42
- "@fluojs/di": "^1.0.3",
43
- "@fluojs/redis": "^1.0.1",
44
- "@fluojs/runtime": "^1.1.2"
42
+ "@fluojs/di": "^1.1.0",
43
+ "@fluojs/redis": "^1.0.2",
44
+ "@fluojs/runtime": "^1.1.8"
45
45
  },
46
46
  "devDependencies": {
47
47
  "vitest": "^3.2.4"