# gizweb:jobs

An app's durable jobs: a queue of one-off jobs and named recurring ones in one record. A job names a path of the app that runs it. Delivery can repeat, so a handler uses the job's id to stay idempotent. A job has MAX_ATTEMPTS attempts, retried after 1 s, then 2 s; a claim holds a LEASE_MS lease, so the job of a worker that died is claimed again. Every change is a compare-and-set on the record, so two workers never settle one claim.

```gizlang
import * as Jobs from "gizweb:jobs";
```

## Types

### `Job`

```gizlang
type Job = { @Key id: string; path: string; payload: string; due: i64; @Column("interval_ms") intervalMs: i64; attempts: i64; state: string; lease: i64; error: string; }
```

One job. `due` is when it may run (ms); `lease` is when the running claim ends; a recurring job has `intervalMs`.

- `id`: `string`
- `path`: `string`
- `payload`: `string`
- `due`: `i64`
- `intervalMs`: `i64`
- `attempts`: `i64`
- `state`: `string` — pending, running, done or failed.
- `lease`: `i64`
- `error`: `string`

## Functions

### `install`

```gizlang
function install(d: Data.Db): bool throws Model.Rejection
```

Makes the jobs table when it is not there.

Effects: fail, read, write, console, io.

### `enqueue`

```gizlang
function enqueue(d: Data.Db, id: string, path: string, payload: string, due: i64): bool throws Model.Rejection
```

Queues a job of PATH with PAYLOAD under the fresh ID, due at DUE.

Effects: fail, read, write, console, io.

### `schedule`

```gizlang
function schedule(d: Data.Db, name: string, path: string, interval: i64, due: i64): bool throws Model.Rejection
```

Runs PATH every INTERVAL ms under the job id "schedule:NAME", first at DUE. A second call changes its path and
interval and keeps its state.

Effects: fail, read, write, console, io.

### `next`

```gizlang
function next(d: Data.Db): Option<i64> throws Model.Rejection
```

When the next job is due or its lease ends, so a worker sleeps until then; none when no job waits. A running job whose
attempts are spent counts too: its claimant finalizes it.

Effects: fail, read, write, console, io.

### `claim`

```gizlang
function claim(d: Data.Db, now: i64): Option<Job> throws Model.Rejection
```

Claims the job that is due first at NOW, if any, with a fresh lease. A running job whose lease ended and whose
attempts are spent ends here: failed, or, when it recurs, due again one interval later.

Effects: fail, read, write, console, io.

### `finish`

```gizlang
function finish(d: Data.Db, job: Job, error: string, now: i64): bool throws Model.Rejection
```

Settles the claim JOB at NOW: done, or, with an ERROR, due again until its attempts are spent. A recurring job is
due again one interval after its last due time. False when JOB is no longer the current claim.

Effects: fail, read, write, console, io.

## Constants

### `MAX_ATTEMPTS`

```gizlang
const MAX_ATTEMPTS = 3
```

The runs a job gets before it fails for good.

### `LEASE_MS`

```gizlang
const LEASE_MS = 30000
```

How long a claim holds a job before another worker may take it again.

