Skip to main content

Module job_queue

Module job_queue 

Source
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 Done means.

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.