@warlock.js/queue 5.32.0 → 5.34.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/CHANGELOG.md +12 -51
- package/README.md +18 -3
- package/cjs/index.cjs +644 -168
- package/cjs/index.cjs.map +1 -1
- package/esm/dashboard-boot.mjs +1 -0
- package/esm/dashboard-boot.mjs.map +1 -1
- package/esm/dashboard.d.mts.map +1 -1
- package/esm/dashboard.mjs +2 -1
- package/esm/dashboard.mjs.map +1 -1
- package/esm/define-job.d.mts +0 -1
- package/esm/define-job.d.mts.map +1 -1
- package/esm/define-job.mjs +11 -64
- package/esm/define-job.mjs.map +1 -1
- package/esm/driver.d.mts +23 -0
- package/esm/driver.d.mts.map +1 -0
- package/esm/drivers/bullmq.mjs +132 -0
- package/esm/drivers/bullmq.mjs.map +1 -0
- package/esm/drivers/local-store.mjs +9 -0
- package/esm/drivers/local-store.mjs.map +1 -0
- package/esm/drivers/local.mjs +307 -0
- package/esm/drivers/local.mjs.map +1 -0
- package/esm/drivers/memory-store.mjs +48 -0
- package/esm/drivers/memory-store.mjs.map +1 -0
- package/esm/drivers/sqlite-store.mjs +89 -0
- package/esm/drivers/sqlite-store.mjs.map +1 -0
- package/esm/failed-jobs.d.mts +2 -8
- package/esm/failed-jobs.d.mts.map +1 -1
- package/esm/failed-jobs.mjs +5 -30
- package/esm/failed-jobs.mjs.map +1 -1
- package/esm/index.d.mts +4 -2
- package/esm/index.mjs +2 -1
- package/esm/job-options.d.mts +9 -0
- package/esm/job-options.d.mts.map +1 -0
- package/esm/job-options.mjs +20 -0
- package/esm/job-options.mjs.map +1 -0
- package/esm/queue-driver.d.mts +6 -0
- package/esm/queue-driver.d.mts.map +1 -0
- package/esm/queue-driver.mjs +37 -0
- package/esm/queue-driver.mjs.map +1 -0
- package/esm/queue-manager.d.mts +5 -26
- package/esm/queue-manager.d.mts.map +1 -1
- package/esm/queue-manager.mjs +28 -84
- package/esm/queue-manager.mjs.map +1 -1
- package/esm/types.d.mts +18 -5
- package/esm/types.d.mts.map +1 -1
- package/llms-full.txt +129 -3
- package/llms.txt +2 -1
- package/package.json +4 -4
- package/skills/configure-queue/SKILL.md +1 -1
- package/skills/index.md +4 -2
- package/skills/local-queues/SKILL.md +121 -0
- package/skills/overview/SKILL.md +3 -2
package/llms-full.txt
CHANGED
|
@@ -13,7 +13,7 @@ description: 'Configure `@warlock.js/queue`: the declarative `src/config/queue.t
|
|
|
13
13
|
|
|
14
14
|
# Configure the queue
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
BullMQ is the default and requires Redis. Select `driver: "local"` for durable SQLite without Redis (Node >= 22.15), or `driver: "memory"` for tests; see `local-queues` for working examples, leases, and cancellation. The config is declarative; the connector applies it.
|
|
17
17
|
|
|
18
18
|
```ts title="src/config/queue.ts"
|
|
19
19
|
import type { QueueConfig } from "@warlock.js/queue";
|
|
@@ -168,6 +168,131 @@ Jobs are not run — call the handler to test it. Opt out with `tests.fakes: { q
|
|
|
168
168
|
- Defining the same name again replaces the handler (this is what keeps dev reloads working), so keep names unique.
|
|
169
169
|
|
|
170
170
|
|
|
171
|
+
## local-queues `@warlock.js/queue/local-queues/SKILL.md`
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
name: local-queues
|
|
175
|
+
description: 'Run @warlock.js/queue without Redis: driver: "local" for durable single-host SQLite jobs using node:sqlite on Node >= 22.15, or driver: "memory" for real handler execution in tests. Covers local.path, leases, crash recovery, polling, concurrency, cancellation, graceful shutdown, and at-least-once delivery. Use for local/desktop background jobs, testing workers without Redis, or configuring SQLite queues.'
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
# Local and memory queues
|
|
179
|
+
|
|
180
|
+
Select a backend in the usual queue config; register `queueConnector()` as usual.
|
|
181
|
+
No extra dependency is needed. Existing config defaults to BullMQ.
|
|
182
|
+
|
|
183
|
+
```ts title="src/config/queue.ts"
|
|
184
|
+
import type { QueueConfig } from "@warlock.js/queue";
|
|
185
|
+
|
|
186
|
+
export default {
|
|
187
|
+
driver: "local",
|
|
188
|
+
prefix: "my-app",
|
|
189
|
+
local: {
|
|
190
|
+
path: "/var/lib/my-app/jobs.sqlite",
|
|
191
|
+
leaseDuration: 30_000,
|
|
192
|
+
pollInterval: 100,
|
|
193
|
+
},
|
|
194
|
+
workers: { concurrency: 3, shutdownTimeout: 5000 },
|
|
195
|
+
defaultJobOptions: { attempts: 3, backoff: { type: "exponential", delay: 1000 } },
|
|
196
|
+
} satisfies QueueConfig;
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Node's built-in SQLite is available without flags from Node 22.13, still
|
|
200
|
+
experimental in Node 22.15. A no-flags import/query was verified on Node
|
|
201
|
+
22.15.0. Require Node >= 22.15 with `node:sqlite` available; unavailable
|
|
202
|
+
runtimes throw on selecting local storage. Sources:
|
|
203
|
+
[Node changelog](https://nodejs.org/en/blog/release/v22.13.0),
|
|
204
|
+
[Node 22.15 API](https://nodejs.org/download/release/v22.15.0/docs/api/sqlite.html).
|
|
205
|
+
BullMQ/memory do not load SQLite.
|
|
206
|
+
|
|
207
|
+
## Define, dispatch, inspect, and retry
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { defineJob, failedJobs, retryFailedJob } from "@warlock.js/queue";
|
|
211
|
+
|
|
212
|
+
const resize = defineJob({
|
|
213
|
+
name: "images.resize",
|
|
214
|
+
async handle(payload: { imageId: string }, ctx) {
|
|
215
|
+
ctx.signal?.throwIfAborted();
|
|
216
|
+
await ctx.progress(50);
|
|
217
|
+
await ctx.log(`resizing ${payload.imageId}`);
|
|
218
|
+
return { ready: true };
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const { id } = await resize.dispatch(
|
|
223
|
+
{ imageId: "42" },
|
|
224
|
+
{
|
|
225
|
+
jobId: "resize-42",
|
|
226
|
+
delay: "2s",
|
|
227
|
+
priority: 1,
|
|
228
|
+
attempts: 4,
|
|
229
|
+
backoff: { type: "fixed", delay: 500 },
|
|
230
|
+
},
|
|
231
|
+
);
|
|
232
|
+
const snapshot = await resize.find(id);
|
|
233
|
+
console.log(snapshot?.state, snapshot?.result);
|
|
234
|
+
for (const job of await failedJobs()) await job.retry();
|
|
235
|
+
// For an id known to be failed: await retryFailedJob(id);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
The same API supports fixed/numeric or exponential backoff, priority/FIFO,
|
|
239
|
+
retention, context propagation, `UnrecoverableJobError`, and failed-job retry.
|
|
240
|
+
Payload/context/result/progress are JSON data. Attempts include the first
|
|
241
|
+
execution; crashed leases re-deliver without consuming a settled attempt.
|
|
242
|
+
|
|
243
|
+
## Memory tests and scripts
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
import {
|
|
247
|
+
closeQueue,
|
|
248
|
+
defineJob,
|
|
249
|
+
setDispatchInterceptor,
|
|
250
|
+
setQueueConfig,
|
|
251
|
+
startWorkers,
|
|
252
|
+
} from "@warlock.js/queue";
|
|
253
|
+
|
|
254
|
+
setDispatchInterceptor(undefined); // opt out of queueFake for real execution
|
|
255
|
+
setQueueConfig({ driver: "memory", workers: { concurrency: 2 } });
|
|
256
|
+
const double = defineJob({ name: "numbers.double", handle: (value: number) => value * 2 });
|
|
257
|
+
await startWorkers();
|
|
258
|
+
const { id } = await double.dispatch(21);
|
|
259
|
+
try {
|
|
260
|
+
const deadline = Date.now() + 5000;
|
|
261
|
+
while ((await double.find(id))?.state !== "completed") {
|
|
262
|
+
if (Date.now() >= deadline) throw new Error("Job did not complete");
|
|
263
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
264
|
+
}
|
|
265
|
+
console.log((await double.find(id))?.result); // 42
|
|
266
|
+
} finally {
|
|
267
|
+
await closeQueue({ timeout: 1000 });
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Memory storage belongs to one process and is cleared by `closeQueue()`.
|
|
272
|
+
Use temporary SQLite files for restart/crash tests. `workers.enabled: false`
|
|
273
|
+
and process-role gating work for every backend.
|
|
274
|
+
|
|
275
|
+
## Durability and shutdown rules
|
|
276
|
+
|
|
277
|
+
- Use the same absolute file path, namespace (`prefix`), and job definitions
|
|
278
|
+
in every producer/worker. Default file: `.warlock/queue.sqlite`, relative
|
|
279
|
+
to cwd. Parents are created; use writable local disk, never a network share.
|
|
280
|
+
- SQLite WAL plus FULL synchronous writes persist jobs. Short synchronous
|
|
281
|
+
transactions claim jobs atomically. A unique token fences every renewal,
|
|
282
|
+
progress update, and completion; an expired owner cannot write.
|
|
283
|
+
- The lease defaults to 30 seconds, renews every third of its duration, and
|
|
284
|
+
must be at least 30 ms. Polling defaults to 100 ms, minimum 1 ms. Concurrency
|
|
285
|
+
is per queue per process. Avoid blocking the event loop longer than the lease.
|
|
286
|
+
- Shutdown stops claims immediately, finishes handlers within the timeout,
|
|
287
|
+
then aborts `ctx.signal` and releases remaining leases. Observe cancellation
|
|
288
|
+
in handlers and operations. Await close before changing config or restarting.
|
|
289
|
+
- A crashed process's jobs re-deliver after lease expiry. Delivery is at least
|
|
290
|
+
once: make external side effects idempotent. Expired/released handlers that
|
|
291
|
+
ignore cancellation can keep running even though queue writes are fenced.
|
|
292
|
+
- `getQueue()` and bull-board require BullMQ. Use `find()` / `failedJobs()`
|
|
293
|
+
with local/memory queues. Prefer BullMQ for multi-host/high-volume workloads.
|
|
294
|
+
|
|
295
|
+
|
|
171
296
|
## manage-failed-jobs `@warlock.js/queue/manage-failed-jobs/SKILL.md`
|
|
172
297
|
|
|
173
298
|
---
|
|
@@ -249,12 +374,12 @@ description: "@warlock.js/queue overview for orientation and choosing the focuse
|
|
|
249
374
|
|
|
250
375
|
# `@warlock.js/queue` — overview
|
|
251
376
|
|
|
252
|
-
Durable background jobs. Jobs are stored in **Redis** (
|
|
377
|
+
Durable background jobs. Jobs are stored in **Redis** (default), local SQLite without Redis, or memory for tests, retried on failure, and processed by workers — inside the app process by default.
|
|
253
378
|
|
|
254
379
|
## Mental model
|
|
255
380
|
|
|
256
381
|
- **Job definition** — `defineJob({ name, handle })` registers a handler by name and returns a typed job.
|
|
257
|
-
- **Dispatch** — `job.dispatch(payload, options)` stores the job in
|
|
382
|
+
- **Dispatch** — `job.dispatch(payload, options)` stores the job in the configured backend. Any process with the same definition and a worker can run it.
|
|
258
383
|
- **Worker** — one per queue name, started by `queueConnector()` (or `startWorkers()`); routes each job to the handler with its name.
|
|
259
384
|
- **Shutdown** — workers stop taking jobs, active jobs finish (up to `workers.shutdownTimeout`), then connections close.
|
|
260
385
|
|
|
@@ -264,6 +389,7 @@ Durable background jobs. Jobs are stored in **Redis** (required), retried on fai
|
|
|
264
389
|
|
|
265
390
|
## Skills index
|
|
266
391
|
|
|
392
|
+
- `local-queues` — SQLite/memory configuration, leases, crash recovery, cancellation.
|
|
267
393
|
- `configure-queue` — `src/config/queue.ts`, `queueConnector()`, workers on/off, shutdown.
|
|
268
394
|
- `define-jobs` — `defineJob`, `dispatch` options, retries, progress, `find`.
|
|
269
395
|
- `manage-failed-jobs` — `failedJobs`, `retryFailedJob`, the bull-board dashboard.
|
package/llms.txt
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
> Package: `@warlock.js/queue`
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> Background jobs for Warlock.js on BullMQ + Redis, local SQLite, or memory: defineJob, dispatch with delay/priority, retries with backoff, progress, failed-job access, in-process workers with graceful shutdown.
|
|
6
6
|
|
|
7
7
|
## Skills
|
|
8
8
|
|
|
9
9
|
- [configure-queue](@warlock.js/queue/configure-queue/SKILL.md): Configure `@warlock.js/queue`: the declarative `src/config/queue.ts` (`QueueConfig` — `connection`, `prefix`, `defaultQueue`, `defaultJobOptions`, `workers: { enabled, concurrency, shutdownTimeout }`), registering `queueConnector()` in `warlock.config.ts > connectors`, running a dispatch-only process, and graceful shutdown. Programmatic `setQueueConfig` / `startWorkers` / `closeQueue` for scripts and tests. Triggers: `QueueConfig`, `queueConnector`, `setQueueConfig`, `startWorkers`, `closeQueue`, `workers.enabled`, `shutdownTimeout`; "configure the queue", "connect BullMQ to Redis", "disable workers in the web process", "graceful shutdown of jobs". Skip: writing jobs — the `define-jobs` topic.
|
|
10
10
|
- [define-jobs](@warlock.js/queue/define-jobs/SKILL.md): Define and dispatch background jobs with `@warlock.js/queue`: `defineJob({ name, queue?, attempts?, backoff?, removeOnComplete?, removeOnFail?, handle(payload, ctx) })` returns a typed job; `job.dispatch(payload, { delay, priority, jobId, attempts, backoff })`; the handler context (`id`, `attempt`, `maxAttempts`, `progress()`, `log()`); reading a job with `job.find(id)`. Triggers: `setDispatchInterceptor`, `queueFake`, `defineJob`, `.dispatch(`, `ctx.progress`, `JobContext`, `DispatchOptions`, `backoff`, `attempts`, `priority`, `jobId`; "run this in the background", "retry with backoff", "delay a job", "job progress", "idempotent dispatch". Skip: config and workers — the `configure-queue` topic; failed jobs — the `manage-failed-jobs` topic.
|
|
11
|
+
- [local-queues](@warlock.js/queue/local-queues/SKILL.md): Run @warlock.js/queue without Redis: driver: "local" for durable single-host SQLite jobs using node:sqlite on Node >= 22.15, or driver: "memory" for real handler execution in tests. Covers local.path, leases, crash recovery, polling, concurrency, cancellation, graceful shutdown, and at-least-once delivery. Use for local/desktop background jobs, testing workers without Redis, or configuring SQLite queues.
|
|
11
12
|
- [manage-failed-jobs](@warlock.js/queue/manage-failed-jobs/SKILL.md): Inspect and retry failed `@warlock.js/queue` jobs: `failedJobs({ queue, start, end })` returns `FailedJob[]` (`id`, `name`, `payload`, `attemptsMade`, `failedReason`, `stacktrace`, `failedAt`, `retry()`), `retryFailedJob(id, { queue })` (throws `FailedJobNotFoundError`), and the bull-board UI — `warlock add bull-board` (requires: ["queue"], so it installs queue first when missing) writes a `dashboard: { enabled, path, middleware }` block to `src/config/queue.ts`; a guard middleware is required in production or boot throws `QueueDashboardUnguardedError`. Manual `queueDashboard(server, { basePath, queues, middleware })` also available — needs `@bull-board/api` + `@bull-board/fastify`, loaded only on call, missing ones throw `QueueDashboardDependencyError`. Triggers: `failedJobs`, `retryFailedJob`, `queueDashboard`, `bull-board`, `dashboard.enabled`, `QueueDashboardUnguardedError`; "list failed jobs", "retry a failed job", "queue dashboard", "job admin UI". Skip: defining retries — the `define-jobs` topic.
|
|
12
13
|
- [overview](@warlock.js/queue/overview/SKILL.md): @warlock.js/queue overview for orientation and choosing the focused skill for the task.
|
|
13
14
|
- [queue-notifications](@warlock.js/queue/queue-notifications/SKILL.md): Send `@warlock.js/notifications` `.queue()` deliveries through BullMQ with `bullmqQueue({ queue?, attempts?, backoff? })` from `@warlock.js/notifications` — put it in the `queue` slot of `src/config/notifications.ts`; the queue workers deliver. Honours `SendOptions.delay` (number = seconds, or "10m"), retries a failing `channel.send`, and fails at once for a channel missing from the worker config. Triggers: `bullmqQueue`, `NotificationConfig.queue`, `QueuePackageNotInstalledError`; "queue notifications with BullMQ", "retry notification delivery", "delayed notification". Skip: the herald backend — the `queue-notifications` topic of the `warlock-js-notifications` skill.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warlock.js/queue",
|
|
3
|
-
"description": "
|
|
3
|
+
"description": "Background jobs for Warlock.js on BullMQ + Redis, local SQLite, or memory: defineJob, dispatch with delay/priority, retries with backoff, progress, failed-job access, in-process workers with graceful shutdown.",
|
|
4
4
|
"warlock": {
|
|
5
5
|
"environment": "server"
|
|
6
6
|
},
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"peerDependencies": {
|
|
12
12
|
"@bull-board/api": "^9.0.0",
|
|
13
13
|
"@bull-board/fastify": "^9.0.0",
|
|
14
|
-
"@warlock.js/core": "5.
|
|
15
|
-
"@warlock.js/logger": "5.
|
|
14
|
+
"@warlock.js/core": "5.34.0",
|
|
15
|
+
"@warlock.js/logger": "5.34.0"
|
|
16
16
|
},
|
|
17
17
|
"peerDependenciesMeta": {
|
|
18
18
|
"@bull-board/api": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
],
|
|
39
39
|
"author": "hassanzohdy",
|
|
40
40
|
"license": "MIT",
|
|
41
|
-
"version": "5.
|
|
41
|
+
"version": "5.34.0",
|
|
42
42
|
"main": "./cjs/index.cjs",
|
|
43
43
|
"module": "./esm/index.mjs",
|
|
44
44
|
"types": "./esm/index.d.mts",
|
|
@@ -5,7 +5,7 @@ description: 'Configure `@warlock.js/queue`: the declarative `src/config/queue.t
|
|
|
5
5
|
|
|
6
6
|
# Configure the queue
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
BullMQ is the default and requires Redis. Select `driver: "local"` for durable SQLite without Redis (Node >= 22.15), or `driver: "memory"` for tests; see `local-queues` for working examples, leases, and cancellation. The config is declarative; the connector applies it.
|
|
9
9
|
|
|
10
10
|
```ts title="src/config/queue.ts"
|
|
11
11
|
import type { QueueConfig } from "@warlock.js/queue";
|
package/skills/index.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: '
|
|
2
|
+
description: 'Background jobs on BullMQ + Redis, local SQLite, or memory: retries, delays, priorities, workers, failed-job recovery. Exports `defineJob`, `queueConnector`, `startWorkers`, `closeQueue`, `getQueue`, `failedJobs`, `retryFailedJob`, `mountQueueDashboard`, `QueueConfig`, `QueueJob`. Use for: "run this in the background", "retry a failed job", "delay a job", "survive restarts", "job dashboard". Not this package: cron or recurring schedules → @warlock.js/scheduler; broker pub/sub messaging → @warlock.js/herald; core `Queue` is an in-memory batcher.'
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# @warlock.js/queue
|
|
6
6
|
|
|
7
|
-
Jobs are stored in Redis (
|
|
7
|
+
Jobs are stored in Redis (default; not bundled), local SQLite without Redis, or memory for tests, retried on failure, and run by workers, in the app process by default. `defineJob({ name, handle })` registers a handler and returns a typed job; `job.dispatch(payload, options)` enqueues it. Any process with the same definition and a worker can run it. Server-only.
|
|
8
8
|
|
|
9
9
|
## The 80% path
|
|
10
10
|
|
|
@@ -22,3 +22,5 @@ Jobs are stored in Redis (required; not bundled), retried on failure, and run by
|
|
|
22
22
|
- Guard the dashboard with middleware; an unguarded one is refused on purpose.
|
|
23
23
|
- Core `Queue` is an in-memory batcher, lost on exit; use this package when work must survive.
|
|
24
24
|
- Do not value-import from client code.
|
|
25
|
+
|
|
26
|
+
Use the `local-queues` topic for `driver: "local"` / `"memory"`, Node requirements, leases, crash recovery, and cancellation.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: local-queues
|
|
3
|
+
description: 'Run @warlock.js/queue without Redis: driver: "local" for durable single-host SQLite jobs using node:sqlite on Node >= 22.15, or driver: "memory" for real handler execution in tests. Covers local.path, leases, crash recovery, polling, concurrency, cancellation, graceful shutdown, and at-least-once delivery. Use for local/desktop background jobs, testing workers without Redis, or configuring SQLite queues.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Local and memory queues
|
|
7
|
+
|
|
8
|
+
Select a backend in the usual queue config; register `queueConnector()` as usual.
|
|
9
|
+
No extra dependency is needed. Existing config defaults to BullMQ.
|
|
10
|
+
|
|
11
|
+
```ts title="src/config/queue.ts"
|
|
12
|
+
import type { QueueConfig } from "@warlock.js/queue";
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
driver: "local",
|
|
16
|
+
prefix: "my-app",
|
|
17
|
+
local: {
|
|
18
|
+
path: "/var/lib/my-app/jobs.sqlite",
|
|
19
|
+
leaseDuration: 30_000,
|
|
20
|
+
pollInterval: 100,
|
|
21
|
+
},
|
|
22
|
+
workers: { concurrency: 3, shutdownTimeout: 5000 },
|
|
23
|
+
defaultJobOptions: { attempts: 3, backoff: { type: "exponential", delay: 1000 } },
|
|
24
|
+
} satisfies QueueConfig;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Node's built-in SQLite is available without flags from Node 22.13, still
|
|
28
|
+
experimental in Node 22.15. A no-flags import/query was verified on Node
|
|
29
|
+
22.15.0. Require Node >= 22.15 with `node:sqlite` available; unavailable
|
|
30
|
+
runtimes throw on selecting local storage. Sources:
|
|
31
|
+
[Node changelog](https://nodejs.org/en/blog/release/v22.13.0),
|
|
32
|
+
[Node 22.15 API](https://nodejs.org/download/release/v22.15.0/docs/api/sqlite.html).
|
|
33
|
+
BullMQ/memory do not load SQLite.
|
|
34
|
+
|
|
35
|
+
## Define, dispatch, inspect, and retry
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { defineJob, failedJobs, retryFailedJob } from "@warlock.js/queue";
|
|
39
|
+
|
|
40
|
+
const resize = defineJob({
|
|
41
|
+
name: "images.resize",
|
|
42
|
+
async handle(payload: { imageId: string }, ctx) {
|
|
43
|
+
ctx.signal?.throwIfAborted();
|
|
44
|
+
await ctx.progress(50);
|
|
45
|
+
await ctx.log(`resizing ${payload.imageId}`);
|
|
46
|
+
return { ready: true };
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const { id } = await resize.dispatch(
|
|
51
|
+
{ imageId: "42" },
|
|
52
|
+
{
|
|
53
|
+
jobId: "resize-42",
|
|
54
|
+
delay: "2s",
|
|
55
|
+
priority: 1,
|
|
56
|
+
attempts: 4,
|
|
57
|
+
backoff: { type: "fixed", delay: 500 },
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
const snapshot = await resize.find(id);
|
|
61
|
+
console.log(snapshot?.state, snapshot?.result);
|
|
62
|
+
for (const job of await failedJobs()) await job.retry();
|
|
63
|
+
// For an id known to be failed: await retryFailedJob(id);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The same API supports fixed/numeric or exponential backoff, priority/FIFO,
|
|
67
|
+
retention, context propagation, `UnrecoverableJobError`, and failed-job retry.
|
|
68
|
+
Payload/context/result/progress are JSON data. Attempts include the first
|
|
69
|
+
execution; crashed leases re-deliver without consuming a settled attempt.
|
|
70
|
+
|
|
71
|
+
## Memory tests and scripts
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import {
|
|
75
|
+
closeQueue,
|
|
76
|
+
defineJob,
|
|
77
|
+
setDispatchInterceptor,
|
|
78
|
+
setQueueConfig,
|
|
79
|
+
startWorkers,
|
|
80
|
+
} from "@warlock.js/queue";
|
|
81
|
+
|
|
82
|
+
setDispatchInterceptor(undefined); // opt out of queueFake for real execution
|
|
83
|
+
setQueueConfig({ driver: "memory", workers: { concurrency: 2 } });
|
|
84
|
+
const double = defineJob({ name: "numbers.double", handle: (value: number) => value * 2 });
|
|
85
|
+
await startWorkers();
|
|
86
|
+
const { id } = await double.dispatch(21);
|
|
87
|
+
try {
|
|
88
|
+
const deadline = Date.now() + 5000;
|
|
89
|
+
while ((await double.find(id))?.state !== "completed") {
|
|
90
|
+
if (Date.now() >= deadline) throw new Error("Job did not complete");
|
|
91
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
92
|
+
}
|
|
93
|
+
console.log((await double.find(id))?.result); // 42
|
|
94
|
+
} finally {
|
|
95
|
+
await closeQueue({ timeout: 1000 });
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Memory storage belongs to one process and is cleared by `closeQueue()`.
|
|
100
|
+
Use temporary SQLite files for restart/crash tests. `workers.enabled: false`
|
|
101
|
+
and process-role gating work for every backend.
|
|
102
|
+
|
|
103
|
+
## Durability and shutdown rules
|
|
104
|
+
|
|
105
|
+
- Use the same absolute file path, namespace (`prefix`), and job definitions
|
|
106
|
+
in every producer/worker. Default file: `.warlock/queue.sqlite`, relative
|
|
107
|
+
to cwd. Parents are created; use writable local disk, never a network share.
|
|
108
|
+
- SQLite WAL plus FULL synchronous writes persist jobs. Short synchronous
|
|
109
|
+
transactions claim jobs atomically. A unique token fences every renewal,
|
|
110
|
+
progress update, and completion; an expired owner cannot write.
|
|
111
|
+
- The lease defaults to 30 seconds, renews every third of its duration, and
|
|
112
|
+
must be at least 30 ms. Polling defaults to 100 ms, minimum 1 ms. Concurrency
|
|
113
|
+
is per queue per process. Avoid blocking the event loop longer than the lease.
|
|
114
|
+
- Shutdown stops claims immediately, finishes handlers within the timeout,
|
|
115
|
+
then aborts `ctx.signal` and releases remaining leases. Observe cancellation
|
|
116
|
+
in handlers and operations. Await close before changing config or restarting.
|
|
117
|
+
- A crashed process's jobs re-deliver after lease expiry. Delivery is at least
|
|
118
|
+
once: make external side effects idempotent. Expired/released handlers that
|
|
119
|
+
ignore cancellation can keep running even though queue writes are fenced.
|
|
120
|
+
- `getQueue()` and bull-board require BullMQ. Use `find()` / `failedJobs()`
|
|
121
|
+
with local/memory queues. Prefer BullMQ for multi-host/high-volume workloads.
|
package/skills/overview/SKILL.md
CHANGED
|
@@ -5,12 +5,12 @@ description: "@warlock.js/queue overview for orientation and choosing the focuse
|
|
|
5
5
|
|
|
6
6
|
# `@warlock.js/queue` — overview
|
|
7
7
|
|
|
8
|
-
Durable background jobs. Jobs are stored in **Redis** (
|
|
8
|
+
Durable background jobs. Jobs are stored in **Redis** (default), local SQLite without Redis, or memory for tests, retried on failure, and processed by workers — inside the app process by default.
|
|
9
9
|
|
|
10
10
|
## Mental model
|
|
11
11
|
|
|
12
12
|
- **Job definition** — `defineJob({ name, handle })` registers a handler by name and returns a typed job.
|
|
13
|
-
- **Dispatch** — `job.dispatch(payload, options)` stores the job in
|
|
13
|
+
- **Dispatch** — `job.dispatch(payload, options)` stores the job in the configured backend. Any process with the same definition and a worker can run it.
|
|
14
14
|
- **Worker** — one per queue name, started by `queueConnector()` (or `startWorkers()`); routes each job to the handler with its name.
|
|
15
15
|
- **Shutdown** — workers stop taking jobs, active jobs finish (up to `workers.shutdownTimeout`), then connections close.
|
|
16
16
|
|
|
@@ -20,6 +20,7 @@ Durable background jobs. Jobs are stored in **Redis** (required), retried on fai
|
|
|
20
20
|
|
|
21
21
|
## Skills index
|
|
22
22
|
|
|
23
|
+
- `local-queues` — SQLite/memory configuration, leases, crash recovery, cancellation.
|
|
23
24
|
- `configure-queue` — `src/config/queue.ts`, `queueConnector()`, workers on/off, shutdown.
|
|
24
25
|
- `define-jobs` — `defineJob`, `dispatch` options, retries, progress, `find`.
|
|
25
26
|
- `manage-failed-jobs` — `failedJobs`, `retryFailedJob`, the bull-board dashboard.
|