@nest-batch/bullmq 0.2.0
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/LICENSE +21 -0
- package/README.md +333 -0
- package/dist/src/adapters/bullmq.adapter.d.ts +157 -0
- package/dist/src/adapters/bullmq.adapter.d.ts.map +1 -0
- package/dist/src/adapters/bullmq.adapter.js +252 -0
- package/dist/src/adapters/bullmq.adapter.js.map +1 -0
- package/dist/src/adapters/index.d.ts +12 -0
- package/dist/src/adapters/index.d.ts.map +1 -0
- package/dist/src/adapters/index.js +29 -0
- package/dist/src/adapters/index.js.map +1 -0
- package/dist/src/bullmq-execution-strategy.d.ts +59 -0
- package/dist/src/bullmq-execution-strategy.d.ts.map +1 -0
- package/dist/src/bullmq-execution-strategy.js +60 -0
- package/dist/src/bullmq-execution-strategy.js.map +1 -0
- package/dist/src/bullmq-runtime.service.d.ts +237 -0
- package/dist/src/bullmq-runtime.service.d.ts.map +1 -0
- package/dist/src/bullmq-runtime.service.js +441 -0
- package/dist/src/bullmq-runtime.service.js.map +1 -0
- package/dist/src/bullmq-schedule.service.d.ts +121 -0
- package/dist/src/bullmq-schedule.service.d.ts.map +1 -0
- package/dist/src/bullmq-schedule.service.js +232 -0
- package/dist/src/bullmq-schedule.service.js.map +1 -0
- package/dist/src/connection.d.ts +83 -0
- package/dist/src/connection.d.ts.map +1 -0
- package/dist/src/connection.js +72 -0
- package/dist/src/connection.js.map +1 -0
- package/dist/src/index.d.ts +29 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +46 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/module-options.d.ts +68 -0
- package/dist/src/module-options.d.ts.map +1 -0
- package/dist/src/module-options.js +13 -0
- package/dist/src/module-options.js.map +1 -0
- package/package.json +71 -0
- package/src/adapters/bullmq.adapter.ts +346 -0
- package/src/adapters/index.ts +11 -0
- package/src/bullmq-execution-strategy.ts +81 -0
- package/src/bullmq-runtime.service.ts +540 -0
- package/src/bullmq-schedule.service.ts +271 -0
- package/src/connection.ts +97 -0
- package/src/index.ts +28 -0
- package/src/module-options.ts +74 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Inject,
|
|
3
|
+
Injectable,
|
|
4
|
+
Logger,
|
|
5
|
+
OnApplicationBootstrap,
|
|
6
|
+
OnApplicationShutdown,
|
|
7
|
+
} from '@nestjs/common';
|
|
8
|
+
import { Queue, type JobsOptions } from 'bullmq';
|
|
9
|
+
|
|
10
|
+
import { BATCH_SCHEDULE_REGISTRY, BatchScheduleRegistry, type BatchScheduleEntry } from '@nest-batch/core';
|
|
11
|
+
|
|
12
|
+
import { BULLMQ_MODULE_OPTIONS, type ResolvedBullMqModuleOptions } from './module-options';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The single BullMQ queue name used by the schedule service. We
|
|
16
|
+
* intentionally use a DIFFERENT queue from the runtime service's
|
|
17
|
+
* `BULLMQ_QUEUE_NAME` so cron-triggered jobs and ad-hoc
|
|
18
|
+
* `launch()`-triggered jobs are inspectable in isolation (and so
|
|
19
|
+
* the schedule-removal path on shutdown can tear them down
|
|
20
|
+
* without touching the runtime work queue).
|
|
21
|
+
*
|
|
22
|
+
* BullMQ 5 rejects queue names that contain a colon (`:`) because
|
|
23
|
+
* it is the path separator in the key layout. We use a hyphen
|
|
24
|
+
* (`-`) instead, matching the existing `BULLMQ_QUEUE_NAME`
|
|
25
|
+
* convention (`'nest-batch-work'`).
|
|
26
|
+
*/
|
|
27
|
+
export const BULLMQ_SCHEDULE_QUEUE_NAME = 'nest-batch-schedule';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* `BullmqScheduleService` — the runtime scheduler for
|
|
31
|
+
* `@BatchScheduled` entries.
|
|
32
|
+
*
|
|
33
|
+
* Lifecycle:
|
|
34
|
+
* 1. `OnApplicationBootstrap` walks the `BatchScheduleRegistry`
|
|
35
|
+
* and, for every entry with `inert: false`, registers a
|
|
36
|
+
* BullMQ repeating job via `queue.upsertJobScheduler(...)`.
|
|
37
|
+
* Entries with `inert: true` are logged and skipped — that
|
|
38
|
+
* is the only place the inert flag is consulted.
|
|
39
|
+
* 2. BullMQ's `upsertJobScheduler` internally fires the
|
|
40
|
+
* schedule at the configured cron time. Each fire enqueues a
|
|
41
|
+
* job into the schedule queue (named after the schedule
|
|
42
|
+
* entry's method). A separate `Worker` (the one owned by
|
|
43
|
+
* `BullmqRuntimeService` if `autoStartWorker` is `true`)
|
|
44
|
+
* processes the jobs.
|
|
45
|
+
* 3. `OnApplicationShutdown` removes every installed scheduler
|
|
46
|
+
* (via `queue.removeJobScheduler`) and closes the queue.
|
|
47
|
+
* Removal is best-effort: a partial failure logs a warning
|
|
48
|
+
* but does not block the rest of the shutdown.
|
|
49
|
+
*
|
|
50
|
+
* Why a dedicated service (not a method on `BullmqRuntimeService`)?
|
|
51
|
+
* - The runtime service is `IExecutionStrategy`-facing; it
|
|
52
|
+
* knows about `JobExecution`, the in-process launch contract,
|
|
53
|
+
* and the worker bridge. Mixing scheduler concerns in would
|
|
54
|
+
* bloat its surface and couple two lifecycles that happen to
|
|
55
|
+
* share a Redis client but are otherwise independent.
|
|
56
|
+
* - The scheduler does NOT need a `Worker`; the runtime service
|
|
57
|
+
* does. A separate service can run with `autoStartWorker:
|
|
58
|
+
* false` cleanly (a launcher-only deployment that still wants
|
|
59
|
+
* cron schedules to fire).
|
|
60
|
+
* - The schedule service owns its own `Queue` (the schedule
|
|
61
|
+
* queue) so cron jobs are not interleaved with manually-launched
|
|
62
|
+
* jobs. They share the same `keyPrefix` so the host's Redis
|
|
63
|
+
* namespace policy still applies.
|
|
64
|
+
*/
|
|
65
|
+
@Injectable()
|
|
66
|
+
export class BullmqScheduleService implements OnApplicationBootstrap, OnApplicationShutdown {
|
|
67
|
+
private readonly logger = new Logger(BullmqScheduleService.name);
|
|
68
|
+
|
|
69
|
+
/** BullMQ queue for the scheduler (producer side only). */
|
|
70
|
+
private scheduleQueue: Queue | null = null;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Every schedule key installed during `onApplicationBootstrap`.
|
|
74
|
+
* Tracked so the shutdown path can `removeJobScheduler` for
|
|
75
|
+
* each one deterministically. A `Set` keeps the test assertions
|
|
76
|
+
* order-independent.
|
|
77
|
+
*/
|
|
78
|
+
private readonly installedKeys = new Set<string>();
|
|
79
|
+
|
|
80
|
+
/** Promise-chain lock for the close path. Mirrors the runtime service. */
|
|
81
|
+
private closePromise: Promise<void> | null = null;
|
|
82
|
+
|
|
83
|
+
constructor(
|
|
84
|
+
private readonly scheduleRegistry: BatchScheduleRegistry,
|
|
85
|
+
@Inject(BULLMQ_MODULE_OPTIONS)
|
|
86
|
+
private readonly options: ResolvedBullMqModuleOptions,
|
|
87
|
+
) {}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Walk the registry and install every non-inert entry as a
|
|
91
|
+
* BullMQ repeating job. Runs AFTER the `BatchBootstrapper` has
|
|
92
|
+
* populated the registry (both hooks are on
|
|
93
|
+
* `OnApplicationBootstrap`, but Nest calls them in
|
|
94
|
+
* provider-registration order; the bootstrapper is registered
|
|
95
|
+
* before this service by `BullmqBatchModule.forRoot()`).
|
|
96
|
+
*
|
|
97
|
+
* Each entry is wrapped in a per-entry `try` so a single bad
|
|
98
|
+
* schedule does not abort the rest of the installation. Bad
|
|
99
|
+
* schedules are logged and skipped — the runtime keeps running
|
|
100
|
+
* for the valid ones.
|
|
101
|
+
*/
|
|
102
|
+
onApplicationBootstrap(): void {
|
|
103
|
+
this.scheduleQueue = this.buildScheduleQueue();
|
|
104
|
+
const entries = this.scheduleRegistry.getAll();
|
|
105
|
+
for (const entry of entries) {
|
|
106
|
+
try {
|
|
107
|
+
this.installSchedule(entry);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
this.logger.warn(
|
|
110
|
+
`Failed to install schedule for "${entry.jobId}::${entry.methodName}": ` +
|
|
111
|
+
`${err instanceof Error ? err.message : String(err)}`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
this.logger.log(
|
|
116
|
+
`BullmqScheduleService started: queue="${BULLMQ_SCHEDULE_QUEUE_NAME}" ` +
|
|
117
|
+
`schedules=${this.installedKeys.size}/${entries.length} ` +
|
|
118
|
+
`(skipped=${entries.length - this.installedKeys.size} inert)`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Tear down every installed scheduler and close the schedule
|
|
124
|
+
* queue. Idempotent: a second `onApplicationShutdown` short-
|
|
125
|
+
* circuits to the first close's promise.
|
|
126
|
+
*/
|
|
127
|
+
async onApplicationShutdown(): Promise<void> {
|
|
128
|
+
if (this.closePromise !== null) {
|
|
129
|
+
return this.closePromise;
|
|
130
|
+
}
|
|
131
|
+
this.closePromise = this.close();
|
|
132
|
+
return this.closePromise;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Installed scheduler keys, in insertion order. Exposed for
|
|
137
|
+
* tests and diagnostics. Read-only: callers MUST NOT mutate
|
|
138
|
+
* the returned array.
|
|
139
|
+
*/
|
|
140
|
+
installedSchedulerKeys(): readonly string[] {
|
|
141
|
+
return Array.from(this.installedKeys);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// -------------------------------------------------------------------------
|
|
145
|
+
// Installation
|
|
146
|
+
// -------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Install a single entry as a BullMQ repeating job. Skips
|
|
150
|
+
* inert entries (the runtime honours the inert flag by NOT
|
|
151
|
+
* calling `upsertJobScheduler` for them). Throws on
|
|
152
|
+
* installation failure so the caller can log + continue.
|
|
153
|
+
*/
|
|
154
|
+
private installSchedule(entry: BatchScheduleEntry): void {
|
|
155
|
+
if (entry.inert) {
|
|
156
|
+
this.logger.log(
|
|
157
|
+
`Skipping inert schedule: ${entry.jobId}::${entry.methodName} ` +
|
|
158
|
+
`(cron="${entry.cron}", tz="${entry.timezone}")`,
|
|
159
|
+
);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (this.scheduleQueue === null) {
|
|
163
|
+
// Defensive: should never happen because `onApplicationBootstrap`
|
|
164
|
+
// builds the queue before iterating entries, but a future
|
|
165
|
+
// refactor that calls `installSchedule` from elsewhere
|
|
166
|
+
// should fail loudly.
|
|
167
|
+
throw new Error('[BullmqScheduleService] scheduleQueue is null');
|
|
168
|
+
}
|
|
169
|
+
const schedulerKey = `${entry.jobId}::${entry.methodName}`;
|
|
170
|
+
const template: {
|
|
171
|
+
name: string;
|
|
172
|
+
data: Record<string, unknown>;
|
|
173
|
+
opts: JobsOptions;
|
|
174
|
+
} = {
|
|
175
|
+
name: entry.methodName,
|
|
176
|
+
data: { jobId: entry.jobId, methodName: entry.methodName },
|
|
177
|
+
opts: {
|
|
178
|
+
attempts: 3,
|
|
179
|
+
backoff: { type: 'exponential', delay: 100, jitter: 0.5 },
|
|
180
|
+
removeOnComplete: { count: 100, age: 3600 },
|
|
181
|
+
removeOnFail: { count: 1000 },
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
void this.scheduleQueue.upsertJobScheduler(
|
|
185
|
+
schedulerKey,
|
|
186
|
+
{ pattern: entry.cron, tz: entry.timezone },
|
|
187
|
+
template,
|
|
188
|
+
);
|
|
189
|
+
this.installedKeys.add(schedulerKey);
|
|
190
|
+
this.logger.log(
|
|
191
|
+
`Installed schedule: ${schedulerKey} (cron="${entry.cron}", tz="${entry.timezone}")`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// -------------------------------------------------------------------------
|
|
196
|
+
// Queue construction
|
|
197
|
+
// -------------------------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Build the producer-side BullMQ queue for the scheduler. The
|
|
201
|
+
* connection tuning mirrors the runtime service's producer
|
|
202
|
+
* options: fail-fast on Redis-down (`enableOfflineQueue:
|
|
203
|
+
* false`) and a tight per-request retry budget
|
|
204
|
+
* (`maxRetriesPerRequest: 1`).
|
|
205
|
+
*/
|
|
206
|
+
private buildScheduleQueue(): Queue {
|
|
207
|
+
return new Queue(BULLMQ_SCHEDULE_QUEUE_NAME, {
|
|
208
|
+
connection: this.producerConnectionOptions(),
|
|
209
|
+
defaultJobOptions: {
|
|
210
|
+
attempts: 3,
|
|
211
|
+
backoff: { type: 'exponential', delay: 100, jitter: 0.5 },
|
|
212
|
+
removeOnComplete: { count: 100, age: 3600 },
|
|
213
|
+
removeOnFail: { count: 1000 },
|
|
214
|
+
},
|
|
215
|
+
prefix: this.options.connection.keyPrefix,
|
|
216
|
+
skipWaitingForReady: true,
|
|
217
|
+
// Mirrors the runtime service: skip the constructor-time
|
|
218
|
+
// version probe so the queue does not throw on a Redis
|
|
219
|
+
// client that is not yet ready.
|
|
220
|
+
skipVersionCheck: true,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private producerConnectionOptions(): Record<string, unknown> {
|
|
225
|
+
return {
|
|
226
|
+
host: this.options.connection.host,
|
|
227
|
+
port: this.options.connection.port,
|
|
228
|
+
password: this.options.connection.password,
|
|
229
|
+
username: this.options.connection.username,
|
|
230
|
+
db: this.options.connection.db,
|
|
231
|
+
...(this.options.connection.tls ? { tls: true } : {}),
|
|
232
|
+
enableOfflineQueue: false,
|
|
233
|
+
maxRetriesPerRequest: 1,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// -------------------------------------------------------------------------
|
|
238
|
+
// Close
|
|
239
|
+
// -------------------------------------------------------------------------
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Close the schedule queue. `removeJobScheduler` is called
|
|
243
|
+
* first for every installed key so the next run of the host
|
|
244
|
+
* app does not inherit leftover schedulers. Each removal is
|
|
245
|
+
* best-effort: a failure on one key does not prevent the
|
|
246
|
+
* others from being removed.
|
|
247
|
+
*/
|
|
248
|
+
private async close(): Promise<void> {
|
|
249
|
+
if (this.scheduleQueue !== null) {
|
|
250
|
+
for (const key of this.installedKeys) {
|
|
251
|
+
try {
|
|
252
|
+
await this.scheduleQueue.removeJobScheduler(key);
|
|
253
|
+
} catch (err) {
|
|
254
|
+
this.logger.warn(
|
|
255
|
+
`removeJobScheduler("${key}") failed: ${
|
|
256
|
+
err instanceof Error ? err.message : String(err)
|
|
257
|
+
}`,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
await this.scheduleQueue.close();
|
|
263
|
+
} catch (err) {
|
|
264
|
+
this.logger.warn(
|
|
265
|
+
`Schedule queue close failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
this.scheduleQueue = null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BullMQ Redis connection options accepted by `BullmqBatchModule`.
|
|
3
|
+
*
|
|
4
|
+
* `BullMQ` is opinionated about Redis client behavior: workers and
|
|
5
|
+
* producers must opt into different connection options so that a
|
|
6
|
+
* Redis outage is observed correctly in each role:
|
|
7
|
+
*
|
|
8
|
+
* - Workers MUST set `maxRetriesPerRequest: null` and
|
|
9
|
+
* `enableReadyCheck: false`. BullMQ's internal blocking commands
|
|
10
|
+
* (`BLPOP`, `BRPOPLPUSH`, `XREADGROUP`) MUST NOT retry
|
|
11
|
+
* per-request — a stalled worker will not surface as a connection
|
|
12
|
+
* error. The Redis client is expected to keep retrying
|
|
13
|
+
* `reconnectOnError` until the operator intervenes.
|
|
14
|
+
*
|
|
15
|
+
* - Producers (the `Queue` used to enqueue work) MUST set
|
|
16
|
+
* `enableOfflineQueue: false` so that a Redis-down condition
|
|
17
|
+
* raises an error *synchronously* on the enqueue call rather than
|
|
18
|
+
* buffering the command and returning success. The `JobLauncher`
|
|
19
|
+
* propagates the failure to the caller, so the call site can
|
|
20
|
+
* mark the `JobExecution` as `FAILED` and surface the error
|
|
21
|
+
* to its caller (HTTP, RPC, cron trigger, ...).
|
|
22
|
+
*
|
|
23
|
+
* Both roles share `host` / `port` / `password` / `username` /
|
|
24
|
+
* `db` / `keyPrefix` / `tls` for connection-target configuration. The
|
|
25
|
+
* role-specific tuning lives on `BullMqConnectionOptions` so callers
|
|
26
|
+
* declare the split explicitly. The default keyPrefix is
|
|
27
|
+
* `nest-batch:` — every key the package writes is namespaced under
|
|
28
|
+
* it, and a key-collision in a shared Redis is impossible.
|
|
29
|
+
*
|
|
30
|
+
* The interface is intentionally `Partial<>`-friendly: a host that
|
|
31
|
+
* only needs a local single-node Redis can pass `{ host: '127.0.0.1' }`
|
|
32
|
+
* and accept all defaults.
|
|
33
|
+
*/
|
|
34
|
+
export interface BullMqConnectionOptions {
|
|
35
|
+
/** Redis host (default: `'127.0.0.1'`). */
|
|
36
|
+
host?: string;
|
|
37
|
+
/** Redis port (default: `6379`). */
|
|
38
|
+
port?: number;
|
|
39
|
+
/** AUTH password, if any. */
|
|
40
|
+
password?: string;
|
|
41
|
+
/** ACL username, if any (Redis 6+ ACL). */
|
|
42
|
+
username?: string;
|
|
43
|
+
/** Logical database index (default: `0`). */
|
|
44
|
+
db?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Key prefix. Every BullMQ key the package writes is prefixed with
|
|
47
|
+
* this string (BullMQ appends its own `bull:` after the prefix).
|
|
48
|
+
* Default: `'nest-batch:'`.
|
|
49
|
+
*/
|
|
50
|
+
keyPrefix?: string;
|
|
51
|
+
/** Enable TLS for the connection. */
|
|
52
|
+
tls?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Resolved Redis connection settings, with all defaults filled in.
|
|
57
|
+
*
|
|
58
|
+
* `BullmqBatchModule.forRoot()` returns a frozen copy of this object
|
|
59
|
+
* under its module-options token; `BullMqExecutionStrategy` reads it
|
|
60
|
+
* to build the `ConnectionOptions` passed into BullMQ's `Queue` /
|
|
61
|
+
* `Worker` / `QueueEvents` constructors.
|
|
62
|
+
*/
|
|
63
|
+
export interface BullMqResolvedConnection {
|
|
64
|
+
readonly host: string;
|
|
65
|
+
readonly port: number;
|
|
66
|
+
readonly password: string | undefined;
|
|
67
|
+
readonly username: string | undefined;
|
|
68
|
+
readonly db: number;
|
|
69
|
+
readonly keyPrefix: string;
|
|
70
|
+
readonly tls: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const BULLMQ_DEFAULT_HOST = '127.0.0.1';
|
|
74
|
+
export const BULLMQ_DEFAULT_PORT = 6379;
|
|
75
|
+
export const BULLMQ_DEFAULT_KEY_PREFIX = 'nest-batch:';
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Fill in defaults for a `BullMqConnectionOptions` bag and return a
|
|
79
|
+
* frozen, fully-resolved connection descriptor.
|
|
80
|
+
*
|
|
81
|
+
* Splitting this out from the module factory keeps the module file
|
|
82
|
+
* focused on DI plumbing and lets the strategy (and tests) construct
|
|
83
|
+
* a resolved connection without re-implementing the defaults.
|
|
84
|
+
*/
|
|
85
|
+
export function resolveBullMqConnection(
|
|
86
|
+
options: BullMqConnectionOptions | undefined,
|
|
87
|
+
): BullMqResolvedConnection {
|
|
88
|
+
return Object.freeze({
|
|
89
|
+
host: options?.host ?? BULLMQ_DEFAULT_HOST,
|
|
90
|
+
port: options?.port ?? BULLMQ_DEFAULT_PORT,
|
|
91
|
+
password: options?.password,
|
|
92
|
+
username: options?.username,
|
|
93
|
+
db: options?.db ?? 0,
|
|
94
|
+
keyPrefix: options?.keyPrefix ?? BULLMQ_DEFAULT_KEY_PREFIX,
|
|
95
|
+
tls: options?.tls ?? false,
|
|
96
|
+
});
|
|
97
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public API barrel for `@nest-batch/bullmq`.
|
|
3
|
+
*
|
|
4
|
+
* The host application should depend exclusively on this barrel:
|
|
5
|
+
* - `BullmqAdapter` is the new factory-pattern transport
|
|
6
|
+
* adapter (use with `NestBatchModule.forRoot({ adapters:
|
|
7
|
+
* { transport: BullmqAdapter.forRoot(...) } })`).
|
|
8
|
+
* - `BullMqExecutionStrategy` is the strategy class (also
|
|
9
|
+
* exported individually so callers can inject it directly for
|
|
10
|
+
* inspection / health checks).
|
|
11
|
+
* - `BULLMQ_MODULE_OPTIONS` is the DI token for the resolved
|
|
12
|
+
* module options bag.
|
|
13
|
+
* - the connection helpers are re-exported so callers can build
|
|
14
|
+
* a fully-resolved `BullMqResolvedConnection` from a partial
|
|
15
|
+
* `BullMqConnectionOptions` without importing the internal
|
|
16
|
+
* `connection.ts` file.
|
|
17
|
+
*
|
|
18
|
+
* The legacy `BullmqBatchModule` (with `forRoot` / `forRootAsync`
|
|
19
|
+
* static methods) has been replaced by `BullmqAdapter`. Internal
|
|
20
|
+
* modules (`./bullmq-execution-strategy`, `./module-options`,
|
|
21
|
+
* `./connection`, `./adapters/bullmq.module`) are implementation
|
|
22
|
+
* details and may move between releases.
|
|
23
|
+
*/
|
|
24
|
+
export * from './connection';
|
|
25
|
+
export * from './module-options';
|
|
26
|
+
export * from './bullmq-execution-strategy';
|
|
27
|
+
export * from './bullmq-schedule.service';
|
|
28
|
+
export * from './adapters';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { BullMqConnectionOptions, BullMqResolvedConnection } from './connection';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Public options bag for `BullmqBatchModule.forRoot()` and
|
|
5
|
+
* `forRootAsync()`.
|
|
6
|
+
*
|
|
7
|
+
* The fields cover the connections the package needs to wire up:
|
|
8
|
+
* - `connection` — the BullMQ `Queue` / `Worker` / `QueueEvents`
|
|
9
|
+
* share. T17 stores it under `BULLMQ_MODULE_OPTIONS`; T18 splits
|
|
10
|
+
* the role-specific tuning (worker `maxRetriesPerRequest: null`
|
|
11
|
+
* + `enableReadyCheck: false`; producer `enableOfflineQueue:
|
|
12
|
+
* false`) onto this same connection record and derives the
|
|
13
|
+
* per-role client from it.
|
|
14
|
+
* - `autoStartWorker` — whether the module should also start a
|
|
15
|
+
* BullMQ `Worker` on `onApplicationBootstrap`. Defaults to
|
|
16
|
+
* `false` so a launcher-only deployment does not accidentally
|
|
17
|
+
* consume Redis. T18 wires the actual worker construction.
|
|
18
|
+
*
|
|
19
|
+
* The interface extends `BullMqConnectionOptions` via composition
|
|
20
|
+
* (not `extends`) so the field can be `undefined` at the top level
|
|
21
|
+
* (the module applies its own defaults via `resolveBullMqConnection`)
|
|
22
|
+
* and the resolved form (with defaults filled in) is what gets
|
|
23
|
+
* handed to the strategy.
|
|
24
|
+
*/
|
|
25
|
+
export interface BullMqModuleOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Redis connection settings shared by the BullMQ `Queue`,
|
|
28
|
+
* `Worker`, and `QueueEvents` clients this package builds.
|
|
29
|
+
* Optional — defaults are filled in by
|
|
30
|
+
* `resolveBullMqConnection()`.
|
|
31
|
+
*/
|
|
32
|
+
connection?: BullMqConnectionOptions;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Whether the module should also spin up a BullMQ `Worker` on
|
|
36
|
+
* `OnApplicationBootstrap`. Default: `false` (launcher-only).
|
|
37
|
+
* Reserved for T18 — the skeleton in T17 does not implement
|
|
38
|
+
* worker lifecycle.
|
|
39
|
+
*/
|
|
40
|
+
autoStartWorker?: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Reserved for future per-adapter extension. Adapter packages
|
|
44
|
+
* (e.g. a future `@nest-batch/mikro-orm` companion) can read
|
|
45
|
+
* the full options bag through this field for cross-cutting
|
|
46
|
+
* config.
|
|
47
|
+
*/
|
|
48
|
+
readonly [key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Token under which the resolved module options are registered.
|
|
53
|
+
*
|
|
54
|
+
* The strategy injects the options via this token so it can build
|
|
55
|
+
* the per-role BullMQ connection clients. The token is a
|
|
56
|
+
* package-scoped `Symbol.for` key (mirroring
|
|
57
|
+
* `@nest-batch/core/MODULE_OPTIONS_TOKEN`) so it is unique across
|
|
58
|
+
* the host process even if multiple `@nest-batch/bullmq` versions
|
|
59
|
+
* are loaded.
|
|
60
|
+
*/
|
|
61
|
+
export const BULLMQ_MODULE_OPTIONS: symbol = Symbol.for(
|
|
62
|
+
'@nest-batch/bullmq/MODULE_OPTIONS',
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Type alias for the fully-resolved options bag. Used by
|
|
67
|
+
* `BullmqBatchModule.forRoot()` to freeze the resolved value under
|
|
68
|
+
* `BULLMQ_MODULE_OPTIONS` and by the strategy to type its injected
|
|
69
|
+
* dependency.
|
|
70
|
+
*/
|
|
71
|
+
export interface ResolvedBullMqModuleOptions {
|
|
72
|
+
readonly connection: BullMqResolvedConnection;
|
|
73
|
+
readonly autoStartWorker: boolean;
|
|
74
|
+
}
|