@owlmeans/queue 0.1.18-rc.21 → 0.1.18-rc.23
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 +55 -8
- package/agent-meta/manifest.json +9 -2
- package/agent-meta/skills/queue/SKILL.md +45 -6
- package/agent-meta/skills/scheduled-jobs/SKILL.md +149 -0
- package/build/config.d.ts +29 -1
- package/build/config.d.ts.map +1 -1
- package/build/config.js +101 -1
- package/build/config.js.map +1 -1
- package/build/errors.d.ts +9 -0
- package/build/errors.d.ts.map +1 -1
- package/build/errors.js +13 -0
- package/build/errors.js.map +1 -1
- package/build/types.d.ts +55 -0
- package/build/types.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/config.ts +117 -2
- package/src/errors.ts +15 -0
- package/src/types.ts +56 -0
- package/tests/schedule.spec.ts +100 -0
package/README.md
CHANGED
|
@@ -6,8 +6,8 @@ than about five seconds, accepted work must survive a process restart, or the wo
|
|
|
6
6
|
against a third party (payment capture, provider sync, bulk mail, report generation, model
|
|
7
7
|
pipelines). A request that finishes in under about two seconds and makes no external call should
|
|
8
8
|
not queue: do it inline. Check for a smaller fix first, such as an index, a batch write or a cached
|
|
9
|
-
aggregate.
|
|
10
|
-
|
|
9
|
+
aggregate. Recurring work is a declared schedule: the broker produces a job on an interval or a
|
|
10
|
+
cron pattern and a registered processor runs it.
|
|
11
11
|
|
|
12
12
|
This package is contracts only and carries no broker code. A driver implements them
|
|
13
13
|
(`@owlmeans/redis-queue` does, over BullMQ). Depend on this package from a shared contract package,
|
|
@@ -16,7 +16,7 @@ and on the driver only where the application wires itself up.
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
bun add @owlmeans/queue@^0.1.18-rc.
|
|
19
|
+
bun add @owlmeans/queue@^0.1.18-rc.23
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## Concepts
|
|
@@ -36,6 +36,9 @@ bun add @owlmeans/queue@^0.1.18-rc.19
|
|
|
36
36
|
a `JobContext`. It must be safe to run twice and must `touch()` in long loops.
|
|
37
37
|
- **Single-flight id**: a `JobOptions.id` derived from what the job is about (`develop:<storyId>`).
|
|
38
38
|
A duplicate enqueue then returns the existing job instead of a second one.
|
|
39
|
+
- **Schedule**: `declareSchedule(cfg, { id, queue, name, every | pattern, … })` in the shared config.
|
|
40
|
+
Every process that listens to the queue reconciles the broker with the declared schedules when its
|
|
41
|
+
worker starts; each run is an ordinary job, and `JobContext.scheduled` names the schedule.
|
|
39
42
|
|
|
40
43
|
## Usage
|
|
41
44
|
|
|
@@ -155,6 +158,41 @@ await context.jobs(APP_OPS).flow({
|
|
|
155
158
|
})
|
|
156
159
|
```
|
|
157
160
|
|
|
161
|
+
### Recurring jobs
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { declareQueue, declareSchedule } from '@owlmeans/queue'
|
|
165
|
+
|
|
166
|
+
// Shared config: the queue first, then its schedules.
|
|
167
|
+
declareQueue(cfg, APP_MAINTENANCE, ['app:maintenance:reconcile', 'app:maintenance:purge'], {
|
|
168
|
+
worker: { concurrency: 1, lockDuration: 60_000 }
|
|
169
|
+
})
|
|
170
|
+
declareSchedule(cfg, {
|
|
171
|
+
id: 'app-nightly-reconcile', queue: APP_MAINTENANCE, name: 'app:maintenance:reconcile',
|
|
172
|
+
pattern: '17 3 * * *', tz: 'UTC', data: {},
|
|
173
|
+
})
|
|
174
|
+
declareSchedule(cfg, {
|
|
175
|
+
id: 'app-purge', queue: APP_MAINTENANCE, name: 'app:maintenance:purge', every: 15 * 60_000,
|
|
176
|
+
})
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The process that `listenQueues(cfg, APP_MAINTENANCE)` registers the processors; nothing else is
|
|
180
|
+
wired. A scheduled run carries no caller and no credentials, so it goes to a processor, never to a
|
|
181
|
+
guarded entrypoint:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
worker.process(APP_MAINTENANCE, 'app:maintenance:reconcile', async job => {
|
|
185
|
+
for await (const page of organizations()) {
|
|
186
|
+
await job.touch()
|
|
187
|
+
await reconcile(page) // safe to run twice: a restart re-runs the whole sweep
|
|
188
|
+
}
|
|
189
|
+
return { scheduled: job.scheduled } // 'app-nightly-reconcile'
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Renaming a schedule id removes the old scheduler and creates a new one; deleting a declaration
|
|
194
|
+
removes its scheduler at the next worker start.
|
|
195
|
+
|
|
158
196
|
### Jobs are records
|
|
159
197
|
|
|
160
198
|
```ts
|
|
@@ -197,6 +235,9 @@ not the job, so read the job back to learn what became of it.
|
|
|
197
235
|
| `declareQueue(cfg, name, jobs, opts?)` | function | Declare a queue and the job names it accepts; re-declaring replaces |
|
|
198
236
|
| `listenQueues(cfg, ...names)` | function | Name the queues this process consumes |
|
|
199
237
|
| `queueOf(cfg, name)`, `queueOfJob(cfg, job)`, `isListening(cfg, name)` | function | Read declarations back; `queueOf` throws `UnknownQueue` |
|
|
238
|
+
| `declareSchedule(cfg, schedule)` | function | Declare a recurring job after checking it; re-declaring an id replaces |
|
|
239
|
+
| `schedulesOf(cfg, queue?)` | function | The declared schedules, of one queue when named |
|
|
240
|
+
| `assertSchedule(cfg, schedule)`, `assertSchedules(cfg)` | function | Check one schedule, or every declared one plus duplicate ids |
|
|
200
241
|
| `enqueueProtocol(ctx, protocol, request, options?)` | function | Enqueue a QUEUE protocol with `JobOptions`, typed by the protocol |
|
|
201
242
|
| `waitForProtocol(ctx, protocol, job, { timeout }?)` | function | Wait for that job and unwrap the typed reply |
|
|
202
243
|
| `queueJobOf(request)` | function | `QueueJobMeta` of the broker job behind a handler's request, or `null` |
|
|
@@ -204,11 +245,11 @@ not the job, so read the job back to learn what became of it.
|
|
|
204
245
|
| `queueWorkerMiddleware(alias?)` | function | Start the worker at the Ready stage, only in a process that listens |
|
|
205
246
|
| `handleJob(ctx, job)`, `entrypointProcessor(ctx)`, `servedJobs(ctx)` | function | The bridge a driver dispatches entrypoint jobs through |
|
|
206
247
|
| `requestOf(envelope)`, `assertFresh(envelope, ttl?)` | function | Rebuild a request from an envelope; enforce `envelopeTtl` |
|
|
207
|
-
| `QueueConfig`, `QueueDeclaration`, `QueueWorkerOptions`, `JobOptions` | type | Configuration shapes |
|
|
248
|
+
| `QueueConfig`, `QueueDeclaration`, `QueueWorkerOptions`, `JobOptions`, `ScheduleDeclaration` | type | Configuration shapes |
|
|
208
249
|
| `QueueResource<D, R>` | type | A queue as a resource, plus `queue`, `wait`, `flow`, `counts`, `close` |
|
|
209
250
|
| `JobRecord<D, R>`, `JobEvent<R>`, `FlowSpec<D>` | type | A job as a record, a lifecycle event, a graph node |
|
|
210
251
|
| `QueueWorkerService` | type | `process`, `start`, `stop`, `listening`, `hooks` |
|
|
211
|
-
| `JobContext<D>`, `JobProcessor<D, R>` | type | What a processor receives; the processor signature |
|
|
252
|
+
| `JobContext<D>`, `JobProcessor<D, R>` | type | What a processor receives (`scheduled` names the schedule of a scheduled run); the processor signature |
|
|
212
253
|
| `QueueHooks` | type | `wrapHandler`, `onJobResult`, `onJobStalled`, `onJobDead` |
|
|
213
254
|
| `QueueJobMeta`, `JobEnvelope`, `JobReply<T>` | type | Broker identity for a handler, the call envelope, the reply shape |
|
|
214
255
|
| `QueueAppend`, `QueueDriver` | type | The `ctx.jobs(queue?)` mixin a driver installs; what a driver supplies |
|
|
@@ -216,7 +257,7 @@ not the job, so read the job back to learn what became of it.
|
|
|
216
257
|
| `JobState`, `JobEventType` | enum | Job lifecycle states and event types |
|
|
217
258
|
| `isSettled(state?)` | function | Whether a state is final |
|
|
218
259
|
| `DEFAULT_ALIAS`, `DEFAULT_JOB_TIMEOUT`, `DEFAULT_ATTEMPTS` | const | `'queue'`, `60_000` ms, `1` |
|
|
219
|
-
| `QueueError`, `QueueTimeout`, `UnknownJob`, `UnknownJobName`, `UnknownQueue`, `QueueNotListening`, `JobNotServed`, `EnvelopeExpired` | class | Error family |
|
|
260
|
+
| `QueueError`, `QueueTimeout`, `UnknownJob`, `UnknownJobName`, `UnknownQueue`, `QueueNotListening`, `JobNotServed`, `EnvelopeExpired`, `ScheduleMisdeclared` | class | Error family |
|
|
220
261
|
|
|
221
262
|
## Common pitfalls
|
|
222
263
|
|
|
@@ -233,7 +274,13 @@ not the job, so read the job back to learn what became of it.
|
|
|
233
274
|
- **Long loops without `touch()`**, or processors that are not safe to run twice.
|
|
234
275
|
- **Throwing from a child for a domain failure.** Return `{ ok: false, error }` instead.
|
|
235
276
|
- **Equal child ids across queues in one flow.** `children()` is keyed by bare id and collapses them.
|
|
236
|
-
- **A self-re-enqueueing job as a cron.**
|
|
277
|
+
- **A self-re-enqueueing job as a cron.** Declare a schedule. `delay` defers a single job once.
|
|
278
|
+
- **A schedule on a queue nothing consumes.** Only a listening worker creates the scheduler, and the
|
|
279
|
+
broker produces each next run when the previous one starts.
|
|
280
|
+
- **Loading different schedule lists in two processes that listen to one queue.** Each start removes
|
|
281
|
+
the schedules its own list does not name.
|
|
282
|
+
- **Declaring a schedule before its queue.** `declareSchedule` checks the queue and the job name and
|
|
283
|
+
throws `UnknownQueue` / `UnknownJobName`.
|
|
237
284
|
- **Calling `ctx.jobs()` with no name once a second queue exists.** It throws `UnknownQueue`.
|
|
238
285
|
|
|
239
286
|
## Related packages
|
|
@@ -253,7 +300,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
253
300
|
your project's skill store (`.agents/skills/`):
|
|
254
301
|
|
|
255
302
|
```sh
|
|
256
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
303
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.28
|
|
257
304
|
```
|
|
258
305
|
|
|
259
306
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/queue",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-09-
|
|
4
|
+
"version": "0.1.18-rc.23",
|
|
5
|
+
"generatedAt": "2026-09-17T08:16:11.196Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "skill",
|
|
10
|
+
"name": "scheduled-jobs",
|
|
11
|
+
"category": "multi-package",
|
|
12
|
+
"file": "skills/scheduled-jobs/SKILL.md",
|
|
13
|
+
"canonicalPath": ".agents/skills/scheduled-jobs/SKILL.md"
|
|
14
|
+
},
|
|
8
15
|
{
|
|
9
16
|
"kind": "skill",
|
|
10
17
|
"name": "queue",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: queue
|
|
3
|
-
description: How to use @owlmeans/queue — protocol-object enqueue/wait, job queues as resources, QUEUE declarations, producer/consumer split, single-flight job ids, lifecycle hooks, and processor rules. Auto-invoked when importing queue types
|
|
3
|
+
description: How to use @owlmeans/queue — protocol-object enqueue/wait, job queues as resources, QUEUE declarations, producer/consumer split, single-flight job ids, recurring schedules, lifecycle hooks, and processor rules. Auto-invoked when importing queue types, declaring a job protocol, or declaring a schedule.
|
|
4
4
|
user-invocable: false
|
|
5
5
|
---
|
|
6
6
|
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/queue
|
|
9
9
|
|
|
10
10
|
**Layer:** Infra
|
|
11
|
-
**Install:** `"@owlmeans/queue": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/queue": "^0.1.18-rc.23"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
Contracts only. It carries no broker code — a driver package (`@owlmeans/redis-queue`) implements
|
|
14
14
|
them. Depend on this one from a shared contract package; depend on the driver only where the
|
|
@@ -142,6 +142,43 @@ run twice — skip work already recorded, delete the rows a previous attempt cre
|
|
|
142
142
|
regenerating over a baseline. Decide this per job and say so in a comment; there is no way to make
|
|
143
143
|
it automatic.
|
|
144
144
|
|
|
145
|
+
## Recurring jobs
|
|
146
|
+
|
|
147
|
+
A recurring sweep is a SCHEDULE on a queue, run by a registered processor:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
declareQueue(cfg, APP_MAINTENANCE, ['app:maintenance:reconcile'], {
|
|
151
|
+
worker: { concurrency: 1, lockDuration: 60_000 }
|
|
152
|
+
})
|
|
153
|
+
declareSchedule(cfg, {
|
|
154
|
+
id: 'app-nightly-reconcile', queue: APP_MAINTENANCE, name: 'app:maintenance:reconcile',
|
|
155
|
+
pattern: '17 3 * * *', tz: 'UTC', data: {},
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
- **Declare it in the shared package, after its queue.** `declareSchedule` runs `assertSchedule`
|
|
160
|
+
first: the queue must be declared (`UnknownQueue`) and must accept the job name
|
|
161
|
+
(`UnknownJobName`), exactly one of `every` (a positive integer of milliseconds) and `pattern`
|
|
162
|
+
(cron), `tz` and `immediately` only with a pattern, never `immediately` with `startDate`, and
|
|
163
|
+
`opts.id` / `opts.delay` refused — the broker names and places every run
|
|
164
|
+
(`ScheduleMisdeclared`, message `<id>:<reason>`). Re-declaring an id replaces it.
|
|
165
|
+
- **The id is the identity.** Changing any other field updates the schedule in place; renaming the
|
|
166
|
+
id is one schedule removed and another created.
|
|
167
|
+
- **Every process that listens to the queue reconciles it at worker start** — the driver creates
|
|
168
|
+
and updates what is declared and removes the schedules it owns that are not. Two listeners of one
|
|
169
|
+
queue must therefore load the same schedule list, which is why it lives in the shared package.
|
|
170
|
+
A producer creates nothing.
|
|
171
|
+
- **A scheduled run is a processor job, never a guarded entrypoint.** Nobody's credentials travel
|
|
172
|
+
with it. `job.scheduled` carries the schedule id; it is absent on a job enqueued any other way.
|
|
173
|
+
- **It needs a consumer.** The broker produces the next run when the previous one starts
|
|
174
|
+
processing, so a schedule on a queue nothing consumes stops producing.
|
|
175
|
+
- **The processor obeys every rule above** — `touch()` through the sweep, and safe to run twice,
|
|
176
|
+
because a restart mid-sweep re-runs it from the start.
|
|
177
|
+
|
|
178
|
+
`schedulesOf(cfg, queue?)` reads the declarations back; `assertSchedules(cfg)` checks all of them,
|
|
179
|
+
including one written into `cfg.queue.schedules` directly, and refuses a duplicate id.
|
|
180
|
+
Which recurring work belongs on a schedule at all is `scheduled-jobs`.
|
|
181
|
+
|
|
145
182
|
## A queued call is still a guarded call
|
|
146
183
|
|
|
147
184
|
`handleJob` rebuilds the request from the envelope and runs the entrypoint as the HTTP boundary
|
|
@@ -183,20 +220,21 @@ listens to, grouped by queue, which is what a driver binds.
|
|
|
183
220
|
| `enqueueProtocol` / `waitForProtocol` | Typed protocol-object enqueue and result wait; validates protocol kind and queue configuration. |
|
|
184
221
|
| `queueJobOf(request)` / `QueueJobMeta` | Explicit broker identity boundary for a protocol handler, including `touch()` for long work. |
|
|
185
222
|
| `declareQueue` / `listenQueues` | Configuration — what exists, and what this process consumes |
|
|
223
|
+
| `declareSchedule` / `schedulesOf` / `assertSchedule` / `assertSchedules` | Recurring jobs — declare, read back, check |
|
|
186
224
|
| `queueOf` / `queueOfJob` / `isListening` | Reading it back; `queueOf` throws `UnknownQueue` |
|
|
187
|
-
| `QueueConfig` / `QueueDeclaration` / `QueueWorkerOptions` / `JobOptions` | The configuration shapes |
|
|
225
|
+
| `QueueConfig` / `QueueDeclaration` / `QueueWorkerOptions` / `JobOptions` / `ScheduleDeclaration` | The configuration shapes |
|
|
188
226
|
| `QueueResource<D, R>` | A queue addressed as a resource, plus `wait` / `flow` / `counts` / `close` |
|
|
189
227
|
| `JobRecord<D, R>` / `JobState` / `isSettled` / `JobEvent` / `JobEventType` | The record shape and lifecycle |
|
|
190
228
|
| `FlowSpec<D>` | A graph node — `name`, optional `queue`, `data`, `opts`, `children` |
|
|
191
229
|
| `QueueWorkerService` | `process(queue, name, fn)`, `start`, `stop`, `listening`, `hooks` |
|
|
192
|
-
| `JobContext<D>` | What a processor gets: `id`, `name`, `queue`, `attempt`, `data`, `signal`, `touch`, `progress`, `children`, `failedChildren` |
|
|
230
|
+
| `JobContext<D>` | What a processor gets: `id`, `name`, `queue`, `attempt`, `data`, `signal`, `scheduled?`, `touch`, `progress`, `children`, `failedChildren` |
|
|
193
231
|
| `QueueHooks` | `wrapHandler`, `onJobResult`, `onJobStalled`, `onJobDead` — the compensation seam |
|
|
194
232
|
| `QueueAppend` | The `ctx.jobs(queue?)` mixin a driver installs; `QueueDriver` is what a driver supplies |
|
|
195
233
|
| `appendQueueTransport` / `makeQueueTransport` | Binds the QUEUE protocol so `call()` routes through the broker |
|
|
196
234
|
| `queueWorkerMiddleware` | Starts the worker at Ready stage |
|
|
197
235
|
| `handleJob` / `servedJobs` / `entrypointProcessor` | The bridge a driver dispatches through |
|
|
198
236
|
| `requestOf` / `assertFresh` / `JobEnvelope` / `JobReply` | The envelope, and rebuilding a request from it |
|
|
199
|
-
| Errors | `QueueError`, `QueueTimeout`, `UnknownJob`, `UnknownJobName`, `UnknownQueue`, `QueueNotListening`, `JobNotServed`, `EnvelopeExpired` |
|
|
237
|
+
| Errors | `QueueError`, `QueueTimeout`, `UnknownJob`, `UnknownJobName`, `UnknownQueue`, `QueueNotListening`, `JobNotServed`, `EnvelopeExpired`, `ScheduleMisdeclared` |
|
|
200
238
|
| Constants | `DEFAULT_ALIAS` (`queue`), `DEFAULT_JOB_TIMEOUT` (60 000 ms), `DEFAULT_ATTEMPTS` (1) |
|
|
201
239
|
|
|
202
240
|
## Depends On
|
|
@@ -210,7 +248,8 @@ listens to, grouped by queue, which is what a driver binds.
|
|
|
210
248
|
|
|
211
249
|
## Related
|
|
212
250
|
|
|
213
|
-
- `redis-queue` — the BullMQ driver;
|
|
251
|
+
- `redis-queue` — the BullMQ driver; how schedules are reconciled; integration tests live there
|
|
252
|
+
- `scheduled-jobs` — when recurring work is a schedule, and how to write the sweep it runs
|
|
214
253
|
- `server-job` / `client-job` — exposing a queue's jobs to an application's UI (list, cancel,
|
|
215
254
|
progress over a socket), without touching the contracts here
|
|
216
255
|
- `entrypoint` — protocol declarations, bindings, and the transport lookup
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scheduled-jobs
|
|
3
|
+
description: Recurring background work on OwlMeans queues — declareSchedule in @owlmeans/queue, reconciled into BullMQ job schedulers by @owlmeans/redis-queue when a listening worker starts. Covers interval vs cron schedules, the processor a sweep runs, overlap and restart semantics, renaming and removing a schedule, multi-replica and rolling-deploy behaviour, and testing. Use when adding a nightly or periodic job, replacing a platform CronJob or a setInterval with a schedule, or diagnosing a schedule that does not run.
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
7
|
+
|
|
8
|
+
# Scheduled jobs
|
|
9
|
+
|
|
10
|
+
**Layer:** Infra · **Packages:** `@owlmeans/queue` (lead — the declaration), `@owlmeans/redis-queue`
|
|
11
|
+
(the reconciliation)
|
|
12
|
+
|
|
13
|
+
A schedule is a declaration that makes the broker produce an ordinary job on an interval or a cron
|
|
14
|
+
pattern. A worker that consumes the queue runs it with a processor. There is no second scheduler
|
|
15
|
+
process, no cron table and no timer in application code.
|
|
16
|
+
|
|
17
|
+
## When a schedule is the answer
|
|
18
|
+
|
|
19
|
+
| Situation | Answer |
|
|
20
|
+
|---|---|
|
|
21
|
+
| Periodic sweep, reconciliation, digest, retention delete, rollup | A schedule on a queue a worker consumes |
|
|
22
|
+
| "Remind this user in 20 minutes" | `JobOptions.delay` on one job — a schedule is not per record |
|
|
23
|
+
| A far-future or cancellable per-record deadline | A row with a due date, swept by one schedule |
|
|
24
|
+
| A deployment that runs no worker | A platform `CronJob` calling a guarded entrypoint |
|
|
25
|
+
| A `setInterval` inside a request handler or a long-lived service | A schedule — the interval dies with the process and runs once per replica |
|
|
26
|
+
|
|
27
|
+
Whether the work belongs on a queue at all is `resource-choice`.
|
|
28
|
+
|
|
29
|
+
## Declaring
|
|
30
|
+
|
|
31
|
+
In the SHARED package every process loads, after the queue:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
declareQueue(cfg, APP_MAINTENANCE, ['app:maintenance:reconcile', 'app:maintenance:purge'], {
|
|
35
|
+
worker: { concurrency: 1, lockDuration: 60_000 }
|
|
36
|
+
})
|
|
37
|
+
declareSchedule(cfg, {
|
|
38
|
+
id: 'app-nightly-reconcile', queue: APP_MAINTENANCE, name: 'app:maintenance:reconcile',
|
|
39
|
+
pattern: '17 3 * * *', tz: 'UTC', data: {},
|
|
40
|
+
})
|
|
41
|
+
declareSchedule(cfg, {
|
|
42
|
+
id: 'app-purge-expired', queue: APP_MAINTENANCE, name: 'app:maintenance:purge', every: 15 * 60_000,
|
|
43
|
+
})
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
And only in the process that runs them: `listenQueues(cfg, APP_MAINTENANCE)` plus a processor per
|
|
47
|
+
job name, registered while the context is wired.
|
|
48
|
+
|
|
49
|
+
- **`pattern` for the calendar, `every` for a frequency.** A nightly job is a pattern with an
|
|
50
|
+
explicit `tz`; "every quarter hour, whenever" is `every`. Pick an odd minute (`17 3 * * *`) so
|
|
51
|
+
many applications' sweeps do not stampede one Redis at the top of the hour.
|
|
52
|
+
- **`every` runs at once** when the scheduler is created (unless `startDate` says otherwise); a
|
|
53
|
+
pattern waits for its next match unless `immediately: true`, which fires only when the scheduler
|
|
54
|
+
is created or its declaration changes — never on an unchanged restart.
|
|
55
|
+
- **The id is permanent.** It is the scheduler's identity in the broker: any other field changes in
|
|
56
|
+
place, a renamed id is a removed schedule plus a new one. Prefix it with the application
|
|
57
|
+
(`app-…`) — ids share one namespace per queue.
|
|
58
|
+
- **Declaration errors surface at boot.** `declareSchedule` throws `UnknownQueue`,
|
|
59
|
+
`UnknownJobName` or `ScheduleMisdeclared` (`<id>:<reason>`) rather than letting the broker fail
|
|
60
|
+
on every tick.
|
|
61
|
+
- **`opts` sets retries and retention for every run; `id` and `delay` are refused** — the broker
|
|
62
|
+
names each run and places it on the schedule.
|
|
63
|
+
|
|
64
|
+
## Writing the processor
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
worker.process<Record<string, never>, SweepReport>(APP_MAINTENANCE, 'app:maintenance:reconcile',
|
|
68
|
+
async job => {
|
|
69
|
+
const report = { scanned: 0, repaired: 0, failed: 0 }
|
|
70
|
+
for await (const page of organizations({ size: 200 })) {
|
|
71
|
+
await job.touch()
|
|
72
|
+
for (const organization of page) {
|
|
73
|
+
try {
|
|
74
|
+
report.repaired += await reconcile(organization.entityId) ? 1 : 0
|
|
75
|
+
} catch (error) {
|
|
76
|
+
report.failed++
|
|
77
|
+
console.error(`reconcile ${organization.entityId}`, error)
|
|
78
|
+
}
|
|
79
|
+
report.scanned++
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
console.info(`[maintenance] ${job.scheduled}`, report)
|
|
83
|
+
return report
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- **A scheduled run has no caller.** No credentials, no entity, no request: it is a processor job,
|
|
88
|
+
never a guarded entrypoint. A step that must go through another service's guard calls that
|
|
89
|
+
service with service credentials.
|
|
90
|
+
- **Idempotent per item, not per run.** A worker rolled out mid-sweep, a stalled lock or a manual
|
|
91
|
+
re-run repeats the whole sweep; each item's repair must be a no-op the second time.
|
|
92
|
+
- **One item's failure is logged and counted, never thrown.** A thrown sweep is retried from the
|
|
93
|
+
start, or dropped after its attempts — either way the other items pay for one bad record.
|
|
94
|
+
- **`touch()` per page.** A sweep over every organization outlives `lockDuration` easily; silence
|
|
95
|
+
hands the job to another worker while this one is still going.
|
|
96
|
+
- **Return a small report.** It lands on the completed job, where an operator reads it.
|
|
97
|
+
|
|
98
|
+
## Overlap, replicas and deploys
|
|
99
|
+
|
|
100
|
+
- **One job per tick, whatever the replica count.** The broker produces one job; one worker takes
|
|
101
|
+
it. Several replicas listening to the queue do not multiply runs.
|
|
102
|
+
- **Runs can overlap on a concurrent queue.** The next run is produced when the previous one
|
|
103
|
+
starts, so a run that outlasts the interval meets its successor. Put sweeps on their own queue
|
|
104
|
+
with `concurrency: 1`, apart from interactive lanes.
|
|
105
|
+
- **A schedule needs a consumer.** Only a listening worker creates the scheduler, and the next run
|
|
106
|
+
is produced only when the previous one starts processing. A queue nothing consumes stops.
|
|
107
|
+
- **Every listener reconciles at start, from its own configuration.** It creates or updates what it
|
|
108
|
+
declares and removes the schedules it owns (`owlmeans:` scheduler ids) that it does not. Every
|
|
109
|
+
process listening to one queue must load the same schedule list; during a rolling deploy the
|
|
110
|
+
newest process to start wins, and the old pods leave the broker alone after their own start.
|
|
111
|
+
- **Removing a schedule is deleting its declaration.** The next worker start removes the scheduler.
|
|
112
|
+
So is a declaration whose job name the queue no longer accepts, or whose `endDate` has passed.
|
|
113
|
+
- **Scheduling never costs consumption.** A refused declaration or a broker failure while
|
|
114
|
+
reconciling is logged and skipped; the worker keeps consuming. Look for
|
|
115
|
+
`redis-queue-schedules:<queue>` in the worker's log when a schedule does not appear.
|
|
116
|
+
|
|
117
|
+
## Diagnosing "it never ran"
|
|
118
|
+
|
|
119
|
+
1. Is some process listening to the queue (`listenQueues`) and is its worker up?
|
|
120
|
+
2. Did that process's start log a `redis-queue-schedules:<queue>` refusal?
|
|
121
|
+
3. Does `new Queue(queue, { connection, prefix }).getJobSchedulers()` list `owlmeans:<id>` with the
|
|
122
|
+
expected `pattern`/`every`, `tz` and `next`?
|
|
123
|
+
4. Is a processor registered for the job name? A run without one fails once as `UnknownJobName`
|
|
124
|
+
and shows in the failed state.
|
|
125
|
+
5. Is a previous run still active on a `concurrency: 1` queue?
|
|
126
|
+
|
|
127
|
+
## Testing
|
|
128
|
+
|
|
129
|
+
- **The declaration** is category A in the application's own shared package: build the config,
|
|
130
|
+
`assertSchedules(cfg)`, and pin ids and job names against the queue declaration.
|
|
131
|
+
- **The processor** is tested as a function over a fake `JobContext` or a real context — it is
|
|
132
|
+
ordinary code with a `touch`.
|
|
133
|
+
- **Against a broker** (Redis-gated): the worker's Ready-stage start is not awaited by
|
|
134
|
+
`context.init()`, so poll the broker (`getJobSchedulers`) or wait for the first run instead of
|
|
135
|
+
asserting straight after boot. A queue's schedulers go with `obliterate()`.
|
|
136
|
+
|
|
137
|
+
## External docs
|
|
138
|
+
|
|
139
|
+
- https://docs.bullmq.io/guide/job-schedulers — `upsertJobScheduler(id, { every | pattern, tz,
|
|
140
|
+
startDate, endDate, limit, immediately }, { name, data, opts })` creates or updates one scheduler
|
|
141
|
+
per id and replaces its pending run; `getJobSchedulers()` / `removeJobScheduler(id)`. The next job
|
|
142
|
+
is added when the previous one starts processing, so a worker must be consuming. Produced jobs
|
|
143
|
+
carry `repeatJobKey` = the scheduler id.
|
|
144
|
+
|
|
145
|
+
## Related
|
|
146
|
+
|
|
147
|
+
- `queue` — `declareSchedule`, `JobContext.scheduled`, processor rules
|
|
148
|
+
- `redis-queue` — `syncSchedules`, the `owlmeans:` ownership prefix, the reconciliation rules
|
|
149
|
+
- `resource-choice` — whether recurring work belongs on a queue at all
|
package/build/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Config, JobOptions, QueueDeclaration, QueueWorkerOptions } from './types.js';
|
|
1
|
+
import type { Config, JobOptions, QueueDeclaration, QueueWorkerOptions, ScheduleDeclaration } from './types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Add a queue to the shared configuration. Every process in the monorepo loads the same
|
|
4
4
|
* declarations — a producer needs them to know a queue exists and what it accepts, a worker needs
|
|
@@ -19,4 +19,32 @@ export declare const listenQueues: <C extends Config>(cfg: C, ...names: string[]
|
|
|
19
19
|
export declare const queueOf: <C extends Config>(cfg: C, name: string) => QueueDeclaration;
|
|
20
20
|
export declare const queueOfJob: <C extends Config>(cfg: C, job: string) => QueueDeclaration | undefined;
|
|
21
21
|
export declare const isListening: <C extends Config>(cfg: C, name: string) => boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the broker can carry a schedule out as written. Nothing is recorded or changed.
|
|
24
|
+
*
|
|
25
|
+
* @throws {ScheduleMisdeclared} naming the schedule id and the reason.
|
|
26
|
+
* @throws {UnknownQueue} when the schedule's queue is not declared.
|
|
27
|
+
* @throws {UnknownJobName} when that queue does not accept the schedule's job name — a scheduled
|
|
28
|
+
* run of a name nothing declares would fail on every tick instead of once, at declaration.
|
|
29
|
+
*/
|
|
30
|
+
export declare const assertSchedule: <C extends Config>(cfg: C, schedule: ScheduleDeclaration) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Add a recurring job to the shared configuration. Declare the queue first — a schedule is checked
|
|
33
|
+
* against it here. Declaring an id twice replaces, like `declareQueue`.
|
|
34
|
+
*
|
|
35
|
+
* Declaring is all this does: the driver creates, updates and removes the broker's schedulers when
|
|
36
|
+
* a worker that listens to the queue starts.
|
|
37
|
+
*
|
|
38
|
+
* @throws {ScheduleMisdeclared} / {UnknownQueue} / {UnknownJobName} — see `assertSchedule`.
|
|
39
|
+
*/
|
|
40
|
+
export declare const declareSchedule: <C extends Config>(cfg: C, schedule: ScheduleDeclaration) => C;
|
|
41
|
+
/** The declared schedules, of one queue when it is named. */
|
|
42
|
+
export declare const schedulesOf: <C extends Config>(cfg: C, queue?: string) => ScheduleDeclaration[];
|
|
43
|
+
/**
|
|
44
|
+
* Check every declared schedule, including any written into the configuration without
|
|
45
|
+
* `declareSchedule`, and refuse two under one id.
|
|
46
|
+
*
|
|
47
|
+
* @throws {ScheduleMisdeclared} / {UnknownQueue} / {UnknownJobName} for the first one that fails.
|
|
48
|
+
*/
|
|
49
|
+
export declare const assertSchedules: <C extends Config>(cfg: C) => void;
|
|
22
50
|
//# sourceMappingURL=config.d.ts.map
|
package/build/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAAE,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,mBAAmB,EAC9E,MAAM,YAAY,CAAA;AAGnB;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,OACtC,CAAC,QAAQ,MAAM,QAAQ,MAAM,EAAE,SAAS;IAAE,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,EAAE,UAAU,CAAA;CAAE,KAClG,CAcF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,YAAY,MAAM,EAAE,KAAG,CAM3E,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,QAAQ,MAAM,KAAG,gBAQhE,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,OAAO,MAAM,KAAG,gBAAgB,GAAG,SACpB,CAAA;AAElE,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,QAAQ,MAAM,KAAG,OACzB,CAAA;AAM5C;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,YAAY,mBAAmB,KAAG,IAoDxF,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,YAAY,mBAAmB,KAAG,CAezF,CAAA;AAED,6DAA6D;AAC7D,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,UAAU,MAAM,KAAG,mBAAmB,EACE,CAAA;AAE5F;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,KAAG,IAS1D,CAAA"}
|
package/build/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UnknownQueue } from './errors.js';
|
|
1
|
+
import { ScheduleMisdeclared, UnknownJobName, UnknownQueue } from './errors.js';
|
|
2
2
|
/**
|
|
3
3
|
* Add a queue to the shared configuration. Every process in the monorepo loads the same
|
|
4
4
|
* declarations — a producer needs them to know a queue exists and what it accepts, a worker needs
|
|
@@ -38,4 +38,104 @@ export const queueOf = (cfg, name) => {
|
|
|
38
38
|
};
|
|
39
39
|
export const queueOfJob = (cfg, job) => cfg.queue?.queues?.find(declared => declared.jobs.includes(job));
|
|
40
40
|
export const isListening = (cfg, name) => cfg.queue?.listen?.includes(name) === true;
|
|
41
|
+
/** A date option the broker can read — a `Date`, epoch milliseconds or a parsable string. */
|
|
42
|
+
const isInstant = (value) => Number.isFinite((value instanceof Date ? value : new Date(value)).getTime());
|
|
43
|
+
/**
|
|
44
|
+
* Whether the broker can carry a schedule out as written. Nothing is recorded or changed.
|
|
45
|
+
*
|
|
46
|
+
* @throws {ScheduleMisdeclared} naming the schedule id and the reason.
|
|
47
|
+
* @throws {UnknownQueue} when the schedule's queue is not declared.
|
|
48
|
+
* @throws {UnknownJobName} when that queue does not accept the schedule's job name — a scheduled
|
|
49
|
+
* run of a name nothing declares would fail on every tick instead of once, at declaration.
|
|
50
|
+
*/
|
|
51
|
+
export const assertSchedule = (cfg, schedule) => {
|
|
52
|
+
const { id } = schedule;
|
|
53
|
+
if (typeof id !== 'string' || id.trim() === '') {
|
|
54
|
+
throw new ScheduleMisdeclared('(empty):id');
|
|
55
|
+
}
|
|
56
|
+
const queue = queueOf(cfg, schedule.queue);
|
|
57
|
+
if (!queue.jobs.includes(schedule.name)) {
|
|
58
|
+
throw new UnknownJobName(`${schedule.queue}:${schedule.name}`);
|
|
59
|
+
}
|
|
60
|
+
const every = schedule.every != null;
|
|
61
|
+
const pattern = schedule.pattern != null;
|
|
62
|
+
if (every === pattern) {
|
|
63
|
+
throw new ScheduleMisdeclared(`${id}:every-or-pattern`);
|
|
64
|
+
}
|
|
65
|
+
if (every && !(Number.isSafeInteger(schedule.every) && schedule.every > 0)) {
|
|
66
|
+
throw new ScheduleMisdeclared(`${id}:every`);
|
|
67
|
+
}
|
|
68
|
+
if (pattern && (typeof schedule.pattern !== 'string' || schedule.pattern.trim() === '')) {
|
|
69
|
+
throw new ScheduleMisdeclared(`${id}:pattern`);
|
|
70
|
+
}
|
|
71
|
+
// Both only mean something to a cron pattern: an interval has no wall clock to read in a time
|
|
72
|
+
// zone, and its first run is immediate already.
|
|
73
|
+
if (!pattern && schedule.tz != null) {
|
|
74
|
+
throw new ScheduleMisdeclared(`${id}:tz`);
|
|
75
|
+
}
|
|
76
|
+
if (!pattern && schedule.immediately === true) {
|
|
77
|
+
throw new ScheduleMisdeclared(`${id}:immediately`);
|
|
78
|
+
}
|
|
79
|
+
if (schedule.immediately === true && schedule.startDate != null) {
|
|
80
|
+
throw new ScheduleMisdeclared(`${id}:immediately-start-date`);
|
|
81
|
+
}
|
|
82
|
+
if (schedule.limit != null && !(Number.isSafeInteger(schedule.limit) && schedule.limit > 0)) {
|
|
83
|
+
throw new ScheduleMisdeclared(`${id}:limit`);
|
|
84
|
+
}
|
|
85
|
+
if (schedule.startDate != null && !isInstant(schedule.startDate)) {
|
|
86
|
+
throw new ScheduleMisdeclared(`${id}:start-date`);
|
|
87
|
+
}
|
|
88
|
+
if (schedule.endDate != null && !isInstant(schedule.endDate)) {
|
|
89
|
+
throw new ScheduleMisdeclared(`${id}:end-date`);
|
|
90
|
+
}
|
|
91
|
+
// The type already omits both; a declaration assembled at runtime still reaches this check. The
|
|
92
|
+
// broker names every run itself and places it on the schedule, so either would be overridden.
|
|
93
|
+
const opts = schedule.opts;
|
|
94
|
+
if (opts?.id != null) {
|
|
95
|
+
throw new ScheduleMisdeclared(`${id}:opts-id`);
|
|
96
|
+
}
|
|
97
|
+
if (opts?.delay != null) {
|
|
98
|
+
throw new ScheduleMisdeclared(`${id}:opts-delay`);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Add a recurring job to the shared configuration. Declare the queue first — a schedule is checked
|
|
103
|
+
* against it here. Declaring an id twice replaces, like `declareQueue`.
|
|
104
|
+
*
|
|
105
|
+
* Declaring is all this does: the driver creates, updates and removes the broker's schedulers when
|
|
106
|
+
* a worker that listens to the queue starts.
|
|
107
|
+
*
|
|
108
|
+
* @throws {ScheduleMisdeclared} / {UnknownQueue} / {UnknownJobName} — see `assertSchedule`.
|
|
109
|
+
*/
|
|
110
|
+
export const declareSchedule = (cfg, schedule) => {
|
|
111
|
+
assertSchedule(cfg, schedule);
|
|
112
|
+
const schedules = cfg.queue?.schedules ?? [];
|
|
113
|
+
const existing = schedules.findIndex(declared => declared.id === schedule.id);
|
|
114
|
+
if (existing < 0) {
|
|
115
|
+
schedules.push(schedule);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
schedules[existing] = schedule;
|
|
119
|
+
}
|
|
120
|
+
cfg.queue = { ...cfg.queue, schedules };
|
|
121
|
+
return cfg;
|
|
122
|
+
};
|
|
123
|
+
/** The declared schedules, of one queue when it is named. */
|
|
124
|
+
export const schedulesOf = (cfg, queue) => (cfg.queue?.schedules ?? []).filter(schedule => queue == null || schedule.queue === queue);
|
|
125
|
+
/**
|
|
126
|
+
* Check every declared schedule, including any written into the configuration without
|
|
127
|
+
* `declareSchedule`, and refuse two under one id.
|
|
128
|
+
*
|
|
129
|
+
* @throws {ScheduleMisdeclared} / {UnknownQueue} / {UnknownJobName} for the first one that fails.
|
|
130
|
+
*/
|
|
131
|
+
export const assertSchedules = (cfg) => {
|
|
132
|
+
const seen = new Set();
|
|
133
|
+
for (const schedule of schedulesOf(cfg)) {
|
|
134
|
+
assertSchedule(cfg, schedule);
|
|
135
|
+
if (seen.has(schedule.id)) {
|
|
136
|
+
throw new ScheduleMisdeclared(`${schedule.id}:duplicate`);
|
|
137
|
+
}
|
|
138
|
+
seen.add(schedule.id);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
41
141
|
//# sourceMappingURL=config.js.map
|
package/build/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/build/errors.d.ts
CHANGED
|
@@ -56,4 +56,13 @@ export declare class EnvelopeExpired extends QueueError {
|
|
|
56
56
|
static typeName: string;
|
|
57
57
|
constructor(msg: string);
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* A schedule the broker could not carry out as written: no id, both or neither of `every` and
|
|
61
|
+
* `pattern`, an option that only means something for the other one, or a job option the broker
|
|
62
|
+
* owns for a scheduled run. The message names the schedule id and the reason.
|
|
63
|
+
*/
|
|
64
|
+
export declare class ScheduleMisdeclared extends QueueError {
|
|
65
|
+
static typeName: string;
|
|
66
|
+
constructor(msg: string);
|
|
67
|
+
}
|
|
59
68
|
//# sourceMappingURL=errors.d.ts.map
|
package/build/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,qBAAa,UAAW,SAAQ,cAAc;IAC5C,OAAuB,QAAQ,SAAe;IAE9C,YAAY,OAAO,GAAE,MAAgB,EAEpC;CACF;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,UAAU;IAC1C,OAAuB,QAAQ,SAAkC;IAEjE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED,gGAAgG;AAChG,qBAAa,UAAW,SAAQ,UAAU;IACxC,OAAuB,QAAQ,SAAqC;IAEpE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED;;;GAGG;AACH,qBAAa,cAAe,SAAQ,UAAU;IAC5C,OAAuB,QAAQ,SAAyC;IAExE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED,mDAAmD;AACnD,qBAAa,YAAa,SAAQ,UAAU;IAC1C,OAAuB,QAAQ,SAAuC;IAEtE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,OAAuB,QAAQ,SAAuC;IAEtE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,UAAU;IAC1C,OAAuB,QAAQ,SAAuC;IAEtE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,UAAU;IAC7C,OAAuB,QAAQ,SAA0C;IAEzE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,qBAAa,UAAW,SAAQ,cAAc;IAC5C,OAAuB,QAAQ,SAAe;IAE9C,YAAY,OAAO,GAAE,MAAgB,EAEpC;CACF;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,UAAU;IAC1C,OAAuB,QAAQ,SAAkC;IAEjE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED,gGAAgG;AAChG,qBAAa,UAAW,SAAQ,UAAU;IACxC,OAAuB,QAAQ,SAAqC;IAEpE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED;;;GAGG;AACH,qBAAa,cAAe,SAAQ,UAAU;IAC5C,OAAuB,QAAQ,SAAyC;IAExE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED,mDAAmD;AACnD,qBAAa,YAAa,SAAQ,UAAU;IAC1C,OAAuB,QAAQ,SAAuC;IAEtE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,OAAuB,QAAQ,SAAuC;IAEtE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,UAAU;IAC1C,OAAuB,QAAQ,SAAuC;IAEtE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,UAAU;IAC7C,OAAuB,QAAQ,SAA0C;IAEzE,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,UAAU;IACjD,OAAuB,QAAQ,SAA8C;IAE7E,YAAY,GAAG,EAAE,MAAM,EAGtB;CACF"}
|
package/build/errors.js
CHANGED
|
@@ -79,6 +79,18 @@ export class EnvelopeExpired extends QueueError {
|
|
|
79
79
|
this.type = EnvelopeExpired.typeName;
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* A schedule the broker could not carry out as written: no id, both or neither of `every` and
|
|
84
|
+
* `pattern`, an option that only means something for the other one, or a job option the broker
|
|
85
|
+
* owns for a scheduled run. The message names the schedule id and the reason.
|
|
86
|
+
*/
|
|
87
|
+
export class ScheduleMisdeclared extends QueueError {
|
|
88
|
+
static typeName = `${QueueError.typeName}ScheduleMisdeclared`;
|
|
89
|
+
constructor(msg) {
|
|
90
|
+
super(`schedule-misdeclared:${msg}`);
|
|
91
|
+
this.type = ScheduleMisdeclared.typeName;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
82
94
|
ResilientError.registerErrorClass(QueueError);
|
|
83
95
|
ResilientError.registerErrorClass(QueueTimeout);
|
|
84
96
|
ResilientError.registerErrorClass(UnknownJob);
|
|
@@ -87,4 +99,5 @@ ResilientError.registerErrorClass(UnknownQueue);
|
|
|
87
99
|
ResilientError.registerErrorClass(QueueNotListening);
|
|
88
100
|
ResilientError.registerErrorClass(JobNotServed);
|
|
89
101
|
ResilientError.registerErrorClass(EnvelopeExpired);
|
|
102
|
+
ResilientError.registerErrorClass(ScheduleMisdeclared);
|
|
90
103
|
//# sourceMappingURL=errors.js.map
|
package/build/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
CHANGED
|
@@ -19,6 +19,56 @@ export interface QueueConfig {
|
|
|
19
19
|
* check and the nonce cache, so a job delayed behind a long backlog is still admitted.
|
|
20
20
|
*/
|
|
21
21
|
envelopeTtl?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Recurring jobs. Declared beside the queues, in the shared package, because every process that
|
|
24
|
+
* listens to a schedule's queue reconciles the broker against this list when its worker starts —
|
|
25
|
+
* two listeners loading different lists would remove each other's schedules.
|
|
26
|
+
*/
|
|
27
|
+
schedules?: ScheduleDeclaration[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A job the broker produces on its own, on an interval or a cron pattern.
|
|
31
|
+
*
|
|
32
|
+
* A scheduled run is an ordinary job of a name the queue declares, dispatched to the processor
|
|
33
|
+
* registered for that name — never to a guarded entrypoint, because nobody's credentials travel
|
|
34
|
+
* with it. Only a process that listens to the queue creates the scheduler, and the broker produces
|
|
35
|
+
* each next run when the previous one STARTS processing — a schedule needs a running consumer.
|
|
36
|
+
*/
|
|
37
|
+
export interface ScheduleDeclaration {
|
|
38
|
+
/**
|
|
39
|
+
* The schedule's stable identity: the broker keeps one scheduler per id, so changing any other
|
|
40
|
+
* field updates it in place, while renaming the id removes one schedule and creates another.
|
|
41
|
+
*/
|
|
42
|
+
id: string;
|
|
43
|
+
queue: string;
|
|
44
|
+
/** A job name the queue declares. */
|
|
45
|
+
name: string;
|
|
46
|
+
/**
|
|
47
|
+
* Milliseconds between runs. Exactly one of `every` and `pattern`. Without `startDate` the first
|
|
48
|
+
* run is produced as soon as the scheduler is created.
|
|
49
|
+
*/
|
|
50
|
+
every?: number;
|
|
51
|
+
/** A cron pattern (an optional leading seconds field is accepted). Exactly one of the two. */
|
|
52
|
+
pattern?: string;
|
|
53
|
+
/** The IANA time zone `pattern` is read in. Pattern only. */
|
|
54
|
+
tz?: string;
|
|
55
|
+
startDate?: Date | number | string;
|
|
56
|
+
/** Once passed, the schedule is treated as absent and a scheduler left from it is removed. */
|
|
57
|
+
endDate?: Date | number | string;
|
|
58
|
+
/** Stop after this many runs. */
|
|
59
|
+
limit?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Run once as soon as the scheduler is created or its declaration changes, then follow the
|
|
62
|
+
* pattern. Pattern only, and never together with `startDate`. A restart that finds the
|
|
63
|
+
* declaration unchanged does not run it again.
|
|
64
|
+
*/
|
|
65
|
+
immediately?: boolean;
|
|
66
|
+
data?: unknown;
|
|
67
|
+
/**
|
|
68
|
+
* Options every run is enqueued with, over the queue's defaults. `id` and `delay` belong to the
|
|
69
|
+
* broker for a scheduled run and are refused.
|
|
70
|
+
*/
|
|
71
|
+
opts?: Omit<JobOptions, 'id' | 'delay'>;
|
|
22
72
|
}
|
|
23
73
|
export interface QueueDeclaration {
|
|
24
74
|
name: string;
|
|
@@ -147,6 +197,11 @@ export interface JobContext<D = unknown> {
|
|
|
147
197
|
attempt: number;
|
|
148
198
|
data: D;
|
|
149
199
|
signal: AbortSignal;
|
|
200
|
+
/**
|
|
201
|
+
* The id of the schedule that produced this run (`ScheduleDeclaration.id`). Absent for a job
|
|
202
|
+
* enqueued any other way.
|
|
203
|
+
*/
|
|
204
|
+
scheduled?: string;
|
|
150
205
|
/**
|
|
151
206
|
* Renew the lock. A processor that runs a long loop MUST call it as it goes: the broker judges
|
|
152
207
|
* liveness by the lock, so silence for longer than `lockDuration` is indistinguishable from a
|
package/build/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAClF,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEzD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAC3B,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,sFAAsF;IACtF,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,QAAQ,CAAC,EAAE,UAAU,CAAA;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/queue",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.23",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
35
|
-
"@owlmeans/auth-common": "^0.1.18-rc.
|
|
36
|
-
"@owlmeans/context": "^0.1.18-rc.
|
|
37
|
-
"@owlmeans/entrypoint": "^0.1.18-rc.
|
|
38
|
-
"@owlmeans/error": "^0.1.18-rc.
|
|
39
|
-
"@owlmeans/resource": "^0.1.18-rc.
|
|
40
|
-
"@owlmeans/route": "^0.1.18-rc.
|
|
34
|
+
"@owlmeans/auth": "^0.1.18-rc.27",
|
|
35
|
+
"@owlmeans/auth-common": "^0.1.18-rc.29",
|
|
36
|
+
"@owlmeans/context": "^0.1.18-rc.25",
|
|
37
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.28",
|
|
38
|
+
"@owlmeans/error": "^0.1.18-rc.27",
|
|
39
|
+
"@owlmeans/resource": "^0.1.18-rc.27",
|
|
40
|
+
"@owlmeans/route": "^0.1.18-rc.26"
|
|
41
41
|
}
|
|
42
42
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
Config, JobOptions, QueueDeclaration, QueueWorkerOptions, ScheduleDeclaration
|
|
3
|
+
} from './types.js'
|
|
4
|
+
import { ScheduleMisdeclared, UnknownJobName, UnknownQueue } from './errors.js'
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Add a queue to the shared configuration. Every process in the monorepo loads the same
|
|
@@ -54,3 +56,116 @@ export const queueOfJob = <C extends Config>(cfg: C, job: string): QueueDeclarat
|
|
|
54
56
|
|
|
55
57
|
export const isListening = <C extends Config>(cfg: C, name: string): boolean =>
|
|
56
58
|
cfg.queue?.listen?.includes(name) === true
|
|
59
|
+
|
|
60
|
+
/** A date option the broker can read — a `Date`, epoch milliseconds or a parsable string. */
|
|
61
|
+
const isInstant = (value: Date | number | string): boolean =>
|
|
62
|
+
Number.isFinite((value instanceof Date ? value : new Date(value)).getTime())
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Whether the broker can carry a schedule out as written. Nothing is recorded or changed.
|
|
66
|
+
*
|
|
67
|
+
* @throws {ScheduleMisdeclared} naming the schedule id and the reason.
|
|
68
|
+
* @throws {UnknownQueue} when the schedule's queue is not declared.
|
|
69
|
+
* @throws {UnknownJobName} when that queue does not accept the schedule's job name — a scheduled
|
|
70
|
+
* run of a name nothing declares would fail on every tick instead of once, at declaration.
|
|
71
|
+
*/
|
|
72
|
+
export const assertSchedule = <C extends Config>(cfg: C, schedule: ScheduleDeclaration): void => {
|
|
73
|
+
const { id } = schedule
|
|
74
|
+
if (typeof id !== 'string' || id.trim() === '') {
|
|
75
|
+
throw new ScheduleMisdeclared('(empty):id')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const queue = queueOf(cfg, schedule.queue)
|
|
79
|
+
if (!queue.jobs.includes(schedule.name)) {
|
|
80
|
+
throw new UnknownJobName(`${schedule.queue}:${schedule.name}`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const every = schedule.every != null
|
|
84
|
+
const pattern = schedule.pattern != null
|
|
85
|
+
if (every === pattern) {
|
|
86
|
+
throw new ScheduleMisdeclared(`${id}:every-or-pattern`)
|
|
87
|
+
}
|
|
88
|
+
if (every && !(Number.isSafeInteger(schedule.every) && (schedule.every as number) > 0)) {
|
|
89
|
+
throw new ScheduleMisdeclared(`${id}:every`)
|
|
90
|
+
}
|
|
91
|
+
if (pattern && (typeof schedule.pattern !== 'string' || schedule.pattern.trim() === '')) {
|
|
92
|
+
throw new ScheduleMisdeclared(`${id}:pattern`)
|
|
93
|
+
}
|
|
94
|
+
// Both only mean something to a cron pattern: an interval has no wall clock to read in a time
|
|
95
|
+
// zone, and its first run is immediate already.
|
|
96
|
+
if (!pattern && schedule.tz != null) {
|
|
97
|
+
throw new ScheduleMisdeclared(`${id}:tz`)
|
|
98
|
+
}
|
|
99
|
+
if (!pattern && schedule.immediately === true) {
|
|
100
|
+
throw new ScheduleMisdeclared(`${id}:immediately`)
|
|
101
|
+
}
|
|
102
|
+
if (schedule.immediately === true && schedule.startDate != null) {
|
|
103
|
+
throw new ScheduleMisdeclared(`${id}:immediately-start-date`)
|
|
104
|
+
}
|
|
105
|
+
if (schedule.limit != null && !(Number.isSafeInteger(schedule.limit) && schedule.limit > 0)) {
|
|
106
|
+
throw new ScheduleMisdeclared(`${id}:limit`)
|
|
107
|
+
}
|
|
108
|
+
if (schedule.startDate != null && !isInstant(schedule.startDate)) {
|
|
109
|
+
throw new ScheduleMisdeclared(`${id}:start-date`)
|
|
110
|
+
}
|
|
111
|
+
if (schedule.endDate != null && !isInstant(schedule.endDate)) {
|
|
112
|
+
throw new ScheduleMisdeclared(`${id}:end-date`)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// The type already omits both; a declaration assembled at runtime still reaches this check. The
|
|
116
|
+
// broker names every run itself and places it on the schedule, so either would be overridden.
|
|
117
|
+
const opts = schedule.opts as JobOptions | undefined
|
|
118
|
+
if (opts?.id != null) {
|
|
119
|
+
throw new ScheduleMisdeclared(`${id}:opts-id`)
|
|
120
|
+
}
|
|
121
|
+
if (opts?.delay != null) {
|
|
122
|
+
throw new ScheduleMisdeclared(`${id}:opts-delay`)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Add a recurring job to the shared configuration. Declare the queue first — a schedule is checked
|
|
128
|
+
* against it here. Declaring an id twice replaces, like `declareQueue`.
|
|
129
|
+
*
|
|
130
|
+
* Declaring is all this does: the driver creates, updates and removes the broker's schedulers when
|
|
131
|
+
* a worker that listens to the queue starts.
|
|
132
|
+
*
|
|
133
|
+
* @throws {ScheduleMisdeclared} / {UnknownQueue} / {UnknownJobName} — see `assertSchedule`.
|
|
134
|
+
*/
|
|
135
|
+
export const declareSchedule = <C extends Config>(cfg: C, schedule: ScheduleDeclaration): C => {
|
|
136
|
+
assertSchedule(cfg, schedule)
|
|
137
|
+
|
|
138
|
+
const schedules = cfg.queue?.schedules ?? []
|
|
139
|
+
const existing = schedules.findIndex(declared => declared.id === schedule.id)
|
|
140
|
+
|
|
141
|
+
if (existing < 0) {
|
|
142
|
+
schedules.push(schedule)
|
|
143
|
+
} else {
|
|
144
|
+
schedules[existing] = schedule
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
cfg.queue = { ...cfg.queue, schedules }
|
|
148
|
+
|
|
149
|
+
return cfg
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** The declared schedules, of one queue when it is named. */
|
|
153
|
+
export const schedulesOf = <C extends Config>(cfg: C, queue?: string): ScheduleDeclaration[] =>
|
|
154
|
+
(cfg.queue?.schedules ?? []).filter(schedule => queue == null || schedule.queue === queue)
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Check every declared schedule, including any written into the configuration without
|
|
158
|
+
* `declareSchedule`, and refuse two under one id.
|
|
159
|
+
*
|
|
160
|
+
* @throws {ScheduleMisdeclared} / {UnknownQueue} / {UnknownJobName} for the first one that fails.
|
|
161
|
+
*/
|
|
162
|
+
export const assertSchedules = <C extends Config>(cfg: C): void => {
|
|
163
|
+
const seen = new Set<string>()
|
|
164
|
+
for (const schedule of schedulesOf(cfg)) {
|
|
165
|
+
assertSchedule(cfg, schedule)
|
|
166
|
+
if (seen.has(schedule.id)) {
|
|
167
|
+
throw new ScheduleMisdeclared(`${schedule.id}:duplicate`)
|
|
168
|
+
}
|
|
169
|
+
seen.add(schedule.id)
|
|
170
|
+
}
|
|
171
|
+
}
|
package/src/errors.ts
CHANGED
|
@@ -96,6 +96,20 @@ export class EnvelopeExpired extends QueueError {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* A schedule the broker could not carry out as written: no id, both or neither of `every` and
|
|
101
|
+
* `pattern`, an option that only means something for the other one, or a job option the broker
|
|
102
|
+
* owns for a scheduled run. The message names the schedule id and the reason.
|
|
103
|
+
*/
|
|
104
|
+
export class ScheduleMisdeclared extends QueueError {
|
|
105
|
+
public static override typeName = `${QueueError.typeName}ScheduleMisdeclared`
|
|
106
|
+
|
|
107
|
+
constructor(msg: string) {
|
|
108
|
+
super(`schedule-misdeclared:${msg}`)
|
|
109
|
+
this.type = ScheduleMisdeclared.typeName
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
99
113
|
ResilientError.registerErrorClass(QueueError)
|
|
100
114
|
ResilientError.registerErrorClass(QueueTimeout)
|
|
101
115
|
ResilientError.registerErrorClass(UnknownJob)
|
|
@@ -104,3 +118,4 @@ ResilientError.registerErrorClass(UnknownQueue)
|
|
|
104
118
|
ResilientError.registerErrorClass(QueueNotListening)
|
|
105
119
|
ResilientError.registerErrorClass(JobNotServed)
|
|
106
120
|
ResilientError.registerErrorClass(EnvelopeExpired)
|
|
121
|
+
ResilientError.registerErrorClass(ScheduleMisdeclared)
|
package/src/types.ts
CHANGED
|
@@ -20,6 +20,57 @@ export interface QueueConfig {
|
|
|
20
20
|
* check and the nonce cache, so a job delayed behind a long backlog is still admitted.
|
|
21
21
|
*/
|
|
22
22
|
envelopeTtl?: number
|
|
23
|
+
/**
|
|
24
|
+
* Recurring jobs. Declared beside the queues, in the shared package, because every process that
|
|
25
|
+
* listens to a schedule's queue reconciles the broker against this list when its worker starts —
|
|
26
|
+
* two listeners loading different lists would remove each other's schedules.
|
|
27
|
+
*/
|
|
28
|
+
schedules?: ScheduleDeclaration[]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A job the broker produces on its own, on an interval or a cron pattern.
|
|
33
|
+
*
|
|
34
|
+
* A scheduled run is an ordinary job of a name the queue declares, dispatched to the processor
|
|
35
|
+
* registered for that name — never to a guarded entrypoint, because nobody's credentials travel
|
|
36
|
+
* with it. Only a process that listens to the queue creates the scheduler, and the broker produces
|
|
37
|
+
* each next run when the previous one STARTS processing — a schedule needs a running consumer.
|
|
38
|
+
*/
|
|
39
|
+
export interface ScheduleDeclaration {
|
|
40
|
+
/**
|
|
41
|
+
* The schedule's stable identity: the broker keeps one scheduler per id, so changing any other
|
|
42
|
+
* field updates it in place, while renaming the id removes one schedule and creates another.
|
|
43
|
+
*/
|
|
44
|
+
id: string
|
|
45
|
+
queue: string
|
|
46
|
+
/** A job name the queue declares. */
|
|
47
|
+
name: string
|
|
48
|
+
/**
|
|
49
|
+
* Milliseconds between runs. Exactly one of `every` and `pattern`. Without `startDate` the first
|
|
50
|
+
* run is produced as soon as the scheduler is created.
|
|
51
|
+
*/
|
|
52
|
+
every?: number
|
|
53
|
+
/** A cron pattern (an optional leading seconds field is accepted). Exactly one of the two. */
|
|
54
|
+
pattern?: string
|
|
55
|
+
/** The IANA time zone `pattern` is read in. Pattern only. */
|
|
56
|
+
tz?: string
|
|
57
|
+
startDate?: Date | number | string
|
|
58
|
+
/** Once passed, the schedule is treated as absent and a scheduler left from it is removed. */
|
|
59
|
+
endDate?: Date | number | string
|
|
60
|
+
/** Stop after this many runs. */
|
|
61
|
+
limit?: number
|
|
62
|
+
/**
|
|
63
|
+
* Run once as soon as the scheduler is created or its declaration changes, then follow the
|
|
64
|
+
* pattern. Pattern only, and never together with `startDate`. A restart that finds the
|
|
65
|
+
* declaration unchanged does not run it again.
|
|
66
|
+
*/
|
|
67
|
+
immediately?: boolean
|
|
68
|
+
data?: unknown
|
|
69
|
+
/**
|
|
70
|
+
* Options every run is enqueued with, over the queue's defaults. `id` and `delay` belong to the
|
|
71
|
+
* broker for a scheduled run and are refused.
|
|
72
|
+
*/
|
|
73
|
+
opts?: Omit<JobOptions, 'id' | 'delay'>
|
|
23
74
|
}
|
|
24
75
|
|
|
25
76
|
export interface QueueDeclaration {
|
|
@@ -152,6 +203,11 @@ export interface JobContext<D = unknown> {
|
|
|
152
203
|
attempt: number
|
|
153
204
|
data: D
|
|
154
205
|
signal: AbortSignal
|
|
206
|
+
/**
|
|
207
|
+
* The id of the schedule that produced this run (`ScheduleDeclaration.id`). Absent for a job
|
|
208
|
+
* enqueued any other way.
|
|
209
|
+
*/
|
|
210
|
+
scheduled?: string
|
|
155
211
|
/**
|
|
156
212
|
* Renew the lock. A processor that runs a long loop MUST call it as it goes: the broker judges
|
|
157
213
|
* liveness by the lock, so silence for longer than `lockDuration` is indistinguishable from a
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test'
|
|
2
|
+
import {
|
|
3
|
+
assertSchedule, assertSchedules, declareQueue, declareSchedule, queueOf, schedulesOf
|
|
4
|
+
} from '../src/config.js'
|
|
5
|
+
import { ScheduleMisdeclared, UnknownJobName, UnknownQueue } from '../src/errors.js'
|
|
6
|
+
import type { Config, ScheduleDeclaration } from '../src/types.js'
|
|
7
|
+
|
|
8
|
+
const cfg = (): Config => declareQueue(
|
|
9
|
+
declareQueue({ ready: false, service: 'test', type: 'backend' } as unknown as Config,
|
|
10
|
+
'maintenance', ['reconcile', 'sweep']),
|
|
11
|
+
'work', ['story:code']
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
const nightly: ScheduleDeclaration = {
|
|
15
|
+
id: 'nightly-reconcile', queue: 'maintenance', name: 'reconcile', pattern: '17 3 * * *', tz: 'UTC',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const misdeclared = (schedule: Partial<ScheduleDeclaration>): () => void =>
|
|
19
|
+
() => assertSchedule(cfg(), { ...nightly, ...schedule } as ScheduleDeclaration)
|
|
20
|
+
|
|
21
|
+
describe('declaring schedules', () => {
|
|
22
|
+
test('a declaration is recorded and read back by its queue', () => {
|
|
23
|
+
const config = declareSchedule(cfg(), nightly)
|
|
24
|
+
|
|
25
|
+
expect(schedulesOf(config, 'maintenance')).toEqual([nightly])
|
|
26
|
+
expect(schedulesOf(config, 'work')).toEqual([])
|
|
27
|
+
expect(schedulesOf(config)).toHaveLength(1)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('re-declaring an id replaces rather than appends', () => {
|
|
31
|
+
let config = declareSchedule(cfg(), nightly)
|
|
32
|
+
config = declareSchedule(config, { ...nightly, pattern: '0 4 * * *' })
|
|
33
|
+
|
|
34
|
+
expect(schedulesOf(config)).toHaveLength(1)
|
|
35
|
+
expect(schedulesOf(config)[0].pattern).toBe('0 4 * * *')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A scheduled run of a name the queue does not accept would fail on every tick. Refusing at
|
|
40
|
+
* declaration turns that into one error at boot, and the refused schedule is not recorded.
|
|
41
|
+
*/
|
|
42
|
+
test('a schedule for an undeclared queue or job name is refused and not recorded', () => {
|
|
43
|
+
const config = cfg()
|
|
44
|
+
|
|
45
|
+
expect(() => declareSchedule(config, { ...nightly, queue: 'missing' })).toThrow(UnknownQueue)
|
|
46
|
+
expect(() => declareSchedule(config, { ...nightly, name: 'story:code' })).toThrow(UnknownJobName)
|
|
47
|
+
expect(schedulesOf(config)).toHaveLength(0)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('re-declaring the queue keeps its schedules', () => {
|
|
51
|
+
const config = declareQueue(declareSchedule(cfg(), nightly), 'maintenance', ['reconcile'])
|
|
52
|
+
|
|
53
|
+
expect(queueOf(config, 'maintenance').jobs).toEqual(['reconcile'])
|
|
54
|
+
expect(schedulesOf(config)).toEqual([nightly])
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('asserting a schedule', () => {
|
|
59
|
+
test('exactly one of every and pattern', () => {
|
|
60
|
+
expect(misdeclared({ every: 1_000 })).toThrow(/every-or-pattern/)
|
|
61
|
+
expect(misdeclared({ pattern: undefined, tz: undefined })).toThrow(/every-or-pattern/)
|
|
62
|
+
expect(misdeclared({ pattern: undefined, tz: undefined, every: 0 })).toThrow(/:every$/)
|
|
63
|
+
expect(misdeclared({ pattern: undefined, tz: undefined, every: 1.5 })).toThrow(ScheduleMisdeclared)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('tz and immediately belong to a pattern only', () => {
|
|
67
|
+
expect(misdeclared({ pattern: undefined, every: 1_000 })).toThrow(/:tz$/)
|
|
68
|
+
expect(misdeclared({ pattern: undefined, tz: undefined, every: 1_000, immediately: true }))
|
|
69
|
+
.toThrow(/:immediately$/)
|
|
70
|
+
expect(misdeclared({ immediately: true, startDate: new Date() })).toThrow(/immediately-start-date/)
|
|
71
|
+
expect(misdeclared({ immediately: true })).not.toThrow()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test('the broker owns a scheduled run\'s id and delay', () => {
|
|
75
|
+
expect(misdeclared({ opts: { id: 'fixed' } as ScheduleDeclaration['opts'] })).toThrow(/opts-id/)
|
|
76
|
+
expect(misdeclared({ opts: { delay: 10 } as ScheduleDeclaration['opts'] })).toThrow(/opts-delay/)
|
|
77
|
+
expect(misdeclared({ opts: { attempts: 3 } })).not.toThrow()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('an empty id is refused', () => {
|
|
81
|
+
expect(misdeclared({ id: ' ' })).toThrow(ScheduleMisdeclared)
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('asserting every schedule', () => {
|
|
86
|
+
test('two schedules under one id are refused', () => {
|
|
87
|
+
const config = declareSchedule(cfg(), nightly)
|
|
88
|
+
config.queue?.schedules?.push({ ...nightly, pattern: '0 5 * * *' })
|
|
89
|
+
|
|
90
|
+
expect(() => assertSchedules(config)).toThrow(/duplicate/)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
/** The configuration is plain data; a schedule written into it directly is checked too. */
|
|
94
|
+
test('a schedule written past declareSchedule is still checked', () => {
|
|
95
|
+
const config = declareSchedule(cfg(), nightly)
|
|
96
|
+
config.queue?.schedules?.push({ id: 'orphan', queue: 'maintenance', name: 'gone', every: 1_000 })
|
|
97
|
+
|
|
98
|
+
expect(() => assertSchedules(config)).toThrow(UnknownJobName)
|
|
99
|
+
})
|
|
100
|
+
})
|