Initial Commit
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
# job-dispatcher
|
||||
|
||||
## Purpose
|
||||
|
||||
Encapsulates the "hand a job to the executor" step of the scheduler's tick loop. The scheduler core decides *which* job runs next and *when*; this module is responsible for the *how* — wiring the per-attempt `AbortController`, tracking the in-flight promise, and updating per-job counters (`runs`, `failures`, `lastRun`) on settle. Splitting this out keeps the scheduler core under the 300-line file budget and isolates the small but error-prone bookkeeping work in one focused module.
|
||||
|
||||
## Features
|
||||
|
||||
- Single `dispatch(job, scheduledAt, env)` function — no class, no state of its own.
|
||||
- Captures the in-flight promise into the scheduler-owned `Set<Promise>` for `stop()` drain.
|
||||
- Atomically transitions the job to `running` and registers a fresh `AbortController` on the job record.
|
||||
- Invokes the scheduler's `afterRun` callback in `finally`, regardless of success or failure.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { dispatch } from "@suaveplan/job-scheduler";
|
||||
|
||||
dispatch(scheduledJob, scheduledAt, {
|
||||
clock,
|
||||
logger,
|
||||
events,
|
||||
timers,
|
||||
inFlight,
|
||||
runningSet,
|
||||
afterRun: (job) => {
|
||||
// scheduler decides whether to re-enqueue or remove
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { dispatch, type ScheduledJob } from "@suaveplan/job-scheduler";
|
||||
|
||||
function pump(jobs: Iterable<ScheduledJob>): void {
|
||||
for (const job of jobs) {
|
||||
if (job.status !== "pending") continue;
|
||||
dispatch(job, job.nextRun ?? clock(), {
|
||||
clock,
|
||||
logger,
|
||||
events,
|
||||
timers,
|
||||
inFlight,
|
||||
runningSet,
|
||||
afterRun: (j) => {
|
||||
if (j.kind === "queued") {
|
||||
// one-shot — let the scheduler garbage-collect
|
||||
return;
|
||||
}
|
||||
// re-enqueue cron jobs by computing the next fire
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `dispatch(job: ScheduledJob, scheduledAt: number, env: DispatchEnv): void` — fires the job. No return value; all bookkeeping happens via `env`.
|
||||
- `interface ScheduledJob` — internal record shape mutated in place.
|
||||
- `interface DispatchEnv` — `{ clock, logger, events, timers, inFlight, runningSet, afterRun }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `afterRun` callback is the scheduler's hook for "what happens once the job settles" — for queued jobs the scheduler typically deletes the entry; for cron jobs it computes the next `nextRun` and re-enqueues. Putting this branch in the scheduler core (rather than passing a flag to `dispatch`) keeps `dispatch` polymorphism-free and makes the lifecycle of every cron / queued job obvious at the call site. The promise is tracked in `env.inFlight` so `JobScheduler.stop()` can `Promise.allSettled` over the set and drain the in-flight work cleanly.
|
||||
Reference in New Issue
Block a user