Expand description
One worker, one job at a time, FIFO, deduplicated by subject — the
runner gc_queue and purge_queue are two kinds on, and the hook a third
kind plugs into without editing it.
One worker, one job at a time, FIFO, deduplicated by subject — for any
kind of job the box has to do slowly.
This module is the mechanism crate::gc_queue and crate::purge_queue
were each carrying their own copy of. The two copies were 1165 lines with
the same seven nouns in both — dedup, enqueue, job_id, Queued, Running,
Done, Failed, worker — the same Mutex/Condvar/VecDeque, the same
prune, the same in-flight instrument. What differed was two things and only
two: what the work is, and what has to happen when it ends. Both are
now the kind’s business (JobKind::run, JobKind::settle) and
everything else is here, once.
§Why a queue at all
Repos.Gc and Accounts.Purge exist so a console button can start
something expensive. The moment such a button exists, ten owners can press
ten of them inside one second, and neither job is cheap: a gc seals the live
logs, compacts the archive and unlinks the whole lazily-exploded read cache;
a purge is minutes of unlink I/O across an account. Ten of those at once is
not ten times the work, it is a box that has stopped answering.
And the rule has to live in the server. A console can serialise itself. It cannot serialise the CLI, a second console, a script, or the next client anybody writes — and every one of those holds a credential that reaches the same disk. A rule only one participant obeys is not an invariant; it is a convention that holds until the first person who did not read it.
§One worker per KIND, not one worker for the box
JobQueue is a type, not a singleton: each kind gets its own instance and
therefore its own jera lane (A32-14) and its own line. That is deliberate and it
is the ruling purge_queue.rs already argued for, kept rather than quietly
reversed by this extraction:
The line a job waits in is part of what the job costs. A gc waits holding nothing — it is maintenance, it is re-derivable, and it can sit behind anything without costing anybody anything. A purge waits holding a total lockout on a customer’s account, for the whole time it is queued. Put them in one line and a user is shut out of their account while the box grinds through an hour of somebody else’s repository maintenance, because a gc arrived first. A mirror is a third answer again: it is network-bound, it blocks a repository that is waiting to become usable, and making it queue behind a compaction that is only holding the disk would be the same mistake in a third colour.
So: one runner, three lines. What is shared is the mechanism and the vocabulary; what is not shared is the waiting.
The honest limit of that ruling: two kinds CAN therefore run at once — a gc
and a purge together is possible today and this change does not alter it —
and both are heavy disk I/O. Nobody has measured that hurting, so nothing
here pretends to fix it. If it ever does, the fix is a shared admission
token taken around the JobKind::run call in [work] and nowhere else:
one place, because there is now only one place.
§What a finished job is for
The worker does not drop a job when it finishes it. A console polls, and a
poller that happened to be between requests at the instant the job completed
would otherwise get “no such job” — indistinguishable from “you never
asked”. So a completed job is kept for JobKind::FINISHED_RETENTION, and
beyond that a bounded number of the most recent are kept anyway
(JobKind::FINISHED_KEPT), so a busy server forgets by age and a quiet one
does not forget at all.
§Adding a kind
Implement JobKind: name it, say what an order is, say what a report is,
say which field of the order is the key, and write JobKind::run.
Everything else has a default. tests/job_queue_third_kind.rs is a whole
kind declared in a test crate, which is the strongest available proof
that a new kind needs no edit here — a test crate cannot change this file
even by accident.
Structs§
- Job
- One job of kind
K, queued or completed. - JobQueue
- The queue: one worker, one job at a time, FIFO, deduplicated by subject.
- Reporter
- The handle a running job reports through.
Enums§
- JobState
- Where a job is. One enum for every kind, because all three status RPCs
render the same four words and a per-kind copy of them was three chances to
disagree about what
Donemeans.
Constants§
- DEFAULT_
FINISHED_ KEPT - Finished jobs kept regardless of age, most recent first.
- DEFAULT_
FINISHED_ RETENTION - How long a finished job stays readable unless its kind says otherwise.
Traits§
- JobKind
- A kind of job. The only thing a new kind has to write.