@basaltkit/queue 1.3.0 → 1.3.1
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.md +8 -0
- package/dist/drivers/sync.d.ts +12 -0
- package/dist/drivers/sync.js +21 -2
- package/dist/index.js +19 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -330,6 +330,14 @@ The sync driver always runs immediately. Delays, timed backoff, and priority onl
|
|
|
330
330
|
**Do I need Redis to run the tests?**
|
|
331
331
|
No. Without `connection`, the plugin uses `SyncQueueDriver`. You can also instantiate the driver directly and inspect `driver.executed`.
|
|
332
332
|
|
|
333
|
+
## Sync driver semantics
|
|
334
|
+
|
|
335
|
+
The inline sync driver (the default without a `connection`) is at-most-once:
|
|
336
|
+
handler errors reject `dispatch()` and an exhausted job is lost. Selecting it
|
|
337
|
+
implicitly in production logs a boot warning — pass `driver: new
|
|
338
|
+
SyncQueueDriver()` to opt in deliberately. Its `executed[]` history is capped
|
|
339
|
+
at 1000 entries.
|
|
340
|
+
|
|
333
341
|
## How it connects to other modules
|
|
334
342
|
|
|
335
343
|
- **`@basaltkit/core`** — provides `createApp`/`definePlugin` (`queuePlugin` is a core plugin), the ALS context (`runWithContext`/`ctx`) propagated to workers, `parseDuration` (formats `'30s'`, `'10m'`), and the base `BasaltError` class.
|
package/dist/drivers/sync.d.ts
CHANGED
|
@@ -3,6 +3,15 @@ import type { AddJobOptions, JobExecutor, QueueDriver } from '../driver.js';
|
|
|
3
3
|
* Synchronous driver: executes the job inline on dispatch, honoring `attempts`
|
|
4
4
|
* (immediate retry). It is the driver for tests and Redis-less dev — the
|
|
5
5
|
* equivalent of Laravel's `sync` queue driver.
|
|
6
|
+
*
|
|
7
|
+
* Semantics to be aware of (they differ from a broker-backed driver):
|
|
8
|
+
* - **At-most-once.** A job that exhausts its inline retries is LOST — there is
|
|
9
|
+
* no persistence and no later redelivery.
|
|
10
|
+
* - **Errors propagate to the dispatcher.** `job.dispatch()` rejects when the
|
|
11
|
+
* handler fails (useful in tests/dev; a broker driver would return
|
|
12
|
+
* immediately and retry in the background).
|
|
13
|
+
* Deploying this driver to production is almost always a misconfiguration —
|
|
14
|
+
* `queuePlugin` warns when it is selected by default there.
|
|
6
15
|
*/
|
|
7
16
|
export declare class SyncQueueDriver implements QueueDriver {
|
|
8
17
|
readonly name = "sync";
|
|
@@ -22,5 +31,8 @@ export declare class SyncQueueDriver implements QueueDriver {
|
|
|
22
31
|
setExecutor(executor: JobExecutor): void;
|
|
23
32
|
add(queue: string, jobName: string, data: unknown, options: AddJobOptions): Promise<void>;
|
|
24
33
|
startWorker(): void;
|
|
34
|
+
/** Appends to `executed[]`, evicting the oldest entries past the cap so a
|
|
35
|
+
* long-running process on this driver cannot leak memory unboundedly. */
|
|
36
|
+
private record;
|
|
25
37
|
close(): Promise<void>;
|
|
26
38
|
}
|
package/dist/drivers/sync.js
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
+
/** `executed[]` keeps at most this many entries (oldest evicted first). */
|
|
2
|
+
const EXECUTED_HISTORY_LIMIT = 1000;
|
|
1
3
|
/**
|
|
2
4
|
* Synchronous driver: executes the job inline on dispatch, honoring `attempts`
|
|
3
5
|
* (immediate retry). It is the driver for tests and Redis-less dev — the
|
|
4
6
|
* equivalent of Laravel's `sync` queue driver.
|
|
7
|
+
*
|
|
8
|
+
* Semantics to be aware of (they differ from a broker-backed driver):
|
|
9
|
+
* - **At-most-once.** A job that exhausts its inline retries is LOST — there is
|
|
10
|
+
* no persistence and no later redelivery.
|
|
11
|
+
* - **Errors propagate to the dispatcher.** `job.dispatch()` rejects when the
|
|
12
|
+
* handler fails (useful in tests/dev; a broker driver would return
|
|
13
|
+
* immediately and retry in the background).
|
|
14
|
+
* Deploying this driver to production is almost always a misconfiguration —
|
|
15
|
+
* `queuePlugin` warns when it is selected by default there.
|
|
5
16
|
*/
|
|
6
17
|
export class SyncQueueDriver {
|
|
7
18
|
name = 'sync';
|
|
@@ -19,18 +30,26 @@ export class SyncQueueDriver {
|
|
|
19
30
|
for (let attempt = 1; attempt <= options.attempts; attempt++) {
|
|
20
31
|
try {
|
|
21
32
|
await this.executor?.(jobName, data);
|
|
22
|
-
this.
|
|
33
|
+
this.record({ queue, jobName, attempts: attempt });
|
|
23
34
|
return;
|
|
24
35
|
}
|
|
25
36
|
catch (error) {
|
|
26
37
|
lastError = error;
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
|
-
this.
|
|
40
|
+
this.record({ queue, jobName, attempts: options.attempts });
|
|
30
41
|
throw lastError;
|
|
31
42
|
}
|
|
32
43
|
startWorker() {
|
|
33
44
|
// no-op: add() already executes inline
|
|
34
45
|
}
|
|
46
|
+
/** Appends to `executed[]`, evicting the oldest entries past the cap so a
|
|
47
|
+
* long-running process on this driver cannot leak memory unboundedly. */
|
|
48
|
+
record(entry) {
|
|
49
|
+
this.executed.push(entry);
|
|
50
|
+
if (this.executed.length > EXECUTED_HISTORY_LIMIT) {
|
|
51
|
+
this.executed.splice(0, this.executed.length - EXECUTED_HISTORY_LIMIT);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
35
54
|
async close() { }
|
|
36
55
|
}
|
package/dist/index.js
CHANGED
|
@@ -14,10 +14,25 @@ export function queuePlugin(options = {}) {
|
|
|
14
14
|
register({ container }) {
|
|
15
15
|
registerQueueCommands(container);
|
|
16
16
|
container.singleton(QUEUE, () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
let driver = options.driver;
|
|
18
|
+
if (!driver) {
|
|
19
|
+
if (options.connection) {
|
|
20
|
+
driver = new BullmqQueueDriver({ connection: options.connection });
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
driver = new SyncQueueDriver();
|
|
24
|
+
if (process.env['NODE_ENV'] === 'production') {
|
|
25
|
+
// The silent default without a Redis connection is the inline sync
|
|
26
|
+
// driver: at-most-once, no background retries, handler errors
|
|
27
|
+
// propagate into the dispatching request. Deliberate sync use in
|
|
28
|
+
// production stays possible — pass `driver: new SyncQueueDriver()`
|
|
29
|
+
// explicitly to silence this.
|
|
30
|
+
console.warn('[basalt:queue] No `connection` (Redis) configured — falling back to the inline sync driver. ' +
|
|
31
|
+
'Jobs run at-most-once inside the dispatching request and are lost on failure. ' +
|
|
32
|
+
'Configure a Redis `connection` for production, or pass `driver: new SyncQueueDriver()` to opt in explicitly.');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
21
36
|
const manager = new QueueManager(driver, {
|
|
22
37
|
...(options.onUnsupported !== undefined ? { onUnsupported: options.onUnsupported } : {}),
|
|
23
38
|
...(options.removeOnComplete !== undefined ? { removeOnComplete: options.removeOnComplete } : {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basaltkit/queue",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Basalt queues on top of BullMQ: declarative jobs with Zod payloads, context propagation (tenant/requestId) to workers and a sync driver for tests.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"bullmq": "^6.2.1",
|
|
18
18
|
"@basaltkit/core": "^1.3.0",
|
|
19
|
-
"@basaltkit/events": "^1.0
|
|
19
|
+
"@basaltkit/events": "^1.1.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^26.3.0",
|