All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 39s
standards workspaces build + test / arl-rs (arl/arl-rs, arl-rs, linux) (pull_request) Successful in 1m9s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m49s
standards workspaces build + test / eoc-rs (eoc/eoc-rs, -p eoc-meter --features cuda, eoc-rs, linux) (pull_request) Successful in 2m10s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m11s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m13s
standards workspaces build + test / pool-rs (pool/pool-rs, pool-rs, linux) (pull_request) Successful in 1m7s
standards workspaces build + test / proof-rs (proof-conformance, proof/proof-rs, proof-rs, linux, true) (pull_request) Successful in 1m28s
standards workspaces build + test / sense-rs (sense/sense-rs, sense-rs, linux) (pull_request) Successful in 1m17s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m21s
standards workspaces build + test / smart-byte-rs (smart-byte/smart-byte-rs, smart-byte-rs, linux) (pull_request) Successful in 3m35s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 3m57s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 3m9s
standards workspaces build + test / joulecontract-rs (joulecontract/joulecontract-rs, joulecontract-rs, linux-arm64, true) (pull_request) Successful in 1m52s
standards workspaces build + test / aac-rs (aac-conformance, aac/aac-rs, aac-rs, linux-arm64) (pull_request) Successful in 6m11s
standards workspaces build + test / map-rs (map-conformance, map/map-rs, map-rs, linux-arm64) (pull_request) Successful in 2m18s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 2m17s
standards workspaces build + test / openpay (openpay, openpay, linux-arm64) (pull_request) Successful in 7m35s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 8m35s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 2m39s
A workload holding accelerators while consuming almost no energy is holding
capacity it is not using. A device ledger cannot see that — a held device is a
held device, saturated or parked at a prompt — so idle reclamation is a question
only an energy-denominated scheduler can ask.
It is also the most dangerous question in this standard, because the obvious
implementation destroys running work. "This workload spent 0 µJ" arrives
identically from a parked workload and from a meter that is not there:
meter unavailable → 0 µJ → "idle" → reclaim → running work destroyed
That is this family's "unknown conflated with a value" defect in its most
destructive form, so the module is built around refusing it:
- **Unknown is not idle.** An `Estimator` figure cannot establish idleness
whatever its value, and the grade is tested **before** the value meets any
threshold — so no threshold setting lets a guess through, including one high
enough to catch everything. A workload is reclaimed on evidence that it is
idle, never on the absence of evidence that it is busy.
- **Idleness is a rate over a stated window, never an instant.** Any workload
is momentarily between steps. An observation shorter than the policy's window
yields no verdict: startup is not idleness. An empty window yields no rate —
`mean_uw()` returns `None`, not zero, because zero is the one value that
reads as idle.
- **Nothing held, nothing to reclaim.**
- **Four verdicts, and only one permits reclamation.** A workload with no
verdict is *absent* from `reclamation_order`, not ranked last in it: it is not
a worse candidate than an idle workload, it is not a candidate.
Check order is normative: nothing held, then the window, then the grade, then
the rate. A later check never rescues an earlier refusal.
`IdlePolicy::default()` reclaims **nothing**. A threshold must sit above the
hardware's idle draw or nothing is ever idle, and below its working draw or
everything is, and both are properties of the device rather than of this
standard — so §2.7 declines to guess and makes the unconfigured failure "nobody
reclaimed" rather than "everybody reclaimed".
This is not preemption. §5.2 ranks who is spending badly under contention; this
asks who is not spending at all. A workload burning energy while completing
nothing is thrashing, not idle, and §5.2 is what catches it.
Conformance gains §7.25–28. Also fixes three broken intra-doc links left by
earlier slices (`WitnessReceipt`, the `derive` function/attribute ambiguity, and
a public doc linking the private first pass), so `cargo doc` is clean.
Co-Authored-By: Claude Opus 5 <[email protected]>
141 lines
7.5 KiB
Markdown
141 lines
7.5 KiB
Markdown
# Pool — Energy-Accounted Multi-Tenant Scheduling
|
|
|
|
**Version:** 0.1.0-draft.1 · **Status:** Draft · **Profile:** `pool/1`
|
|
**Specification text:** CC-BY-4.0 · **Reference code (`pool-rs/`):** Apache-2.0
|
|
|
|
Pool is the open standard for **deciding who gets contended compute, on a
|
|
budget denominated in energy**. An entitlement guarantees microjoules and
|
|
devices together; a gang is admitted whole or not at all; and when capacity
|
|
must be reclaimed, the order follows what workloads have delivered per joule.
|
|
|
|
Pool is one of the [Transaction Science open standards](../README.md).
|
|
|
|
## Why energy is the unit
|
|
|
|
A device count is a proxy for the thing consumed. The quantity that is paid
|
|
for, drawn from a grid and reported under an energy-disclosure regime is
|
|
energy — so a guarantee expressed only in accelerators leaves the energy budget
|
|
enforced nowhere and reconciled after the fact, if at all. In `pool/1` both
|
|
bind: devices describe what is occupied, joules describe what is spent.
|
|
|
|
## Three rules that need the energy unit
|
|
|
|
1. **A guess may not spend a neighbour's capacity** (SPEC §4.4). A claim whose
|
|
estimate is `Estimator` — nobody measured or modelled it — is admitted only
|
|
inside its own guarantee, never against surplus borrowed from a sibling. A
|
|
tenant may bear its own mis-estimate; a sibling's unspent joules are not
|
|
the tenant's to gamble.
|
|
2. **A gang is all-or-nothing in joules, not only in slots** (SPEC §2.4).
|
|
Admitting part of a gang against a budget that cannot finish the whole gang
|
|
spends real energy and delivers nothing.
|
|
3. **Preemption follows evidence, then declaration** (SPEC §5.2). Workloads
|
|
with a measured cost per *completed* unit are reclaimed worst-first.
|
|
Workloads that have completed nothing are unmeasured rather than wasteful,
|
|
and fall back to declared priority — reclaiming them first would destroy
|
|
the work that would have produced the measurement.
|
|
|
|
## Sharing a device, and what it costs a figure
|
|
|
|
Fractional accelerators stop capacity being stranded, and their memory fence is
|
|
enforceable because memory is partitioned. **Energy is not** — a device counter
|
|
reports one number for the whole device and cannot see tenants. So a per-tenant
|
|
figure is a split of a measurement, not a measurement (SPEC §2.5):
|
|
|
|
- **Provenance degrades on division.** A `HwShunt` device total becomes
|
|
`ModelBased` as soon as it is split between two occupants. It never improves:
|
|
dividing an `Estimator` guess does not make it a model. Dividing by one is
|
|
not a division, so a sole occupant keeps the device's grade.
|
|
- **The shares sum to the measurement exactly** — integer microjoules, largest
|
|
remainder, deterministic tie-break. Conservation is what makes a tenant's
|
|
figure reconcilable against the device that produced it.
|
|
|
|
## A guess earns its way out
|
|
|
|
The admission rule that a guess may not spend a neighbour's capacity is only
|
|
fair if a guess can stop being one. The evidence is already in the ledger —
|
|
every completed claim recorded what it actually consumed — so an estimate can
|
|
be derived from what this *kind* of work really cost (SPEC §2.6):
|
|
|
|
- **A guess is not evidence.** `Estimator` observations are excluded, not
|
|
averaged in. A tenant earns an observed estimate by being measured, not by
|
|
having run.
|
|
- **A derived estimate is a model, never a reading** — capped at `ModelBased`
|
|
even where every observation came from a counter, because a figure for the
|
|
*next* run is a prediction.
|
|
- **One run is not a sample.** Below the required count nothing is derived, and
|
|
the counts are reported: history that is all guesses needs a meter, while
|
|
little history needs patience.
|
|
- **The absence of a class is not a class.** Comparability is the operator's
|
|
assertion, so an empty class is refused rather than pooling unlike work.
|
|
|
|
The reservation takes the largest observed figure rather than the mean, because
|
|
under-reserving is the failure the borrowing rule exists to prevent. The limit
|
|
is stated: the maximum of a small sample underestimates the true tail.
|
|
|
|
## Reclaiming what is not working
|
|
|
|
A workload holding accelerators while spending almost no energy is holding
|
|
capacity it is not using — something a device count cannot see, since a held
|
|
device is a held device whether saturated or parked. It is also the most
|
|
dangerous question here, because the obvious implementation destroys running
|
|
work: *"spent 0 µJ"* arrives identically from a parked workload and from a
|
|
meter that is not there (SPEC §2.7).
|
|
|
|
```
|
|
meter unavailable → 0 µJ → "idle" → reclaim → running work destroyed
|
|
```
|
|
|
|
So **unknown is not idle**, and the grade is tested *before* the value meets
|
|
any threshold — no threshold setting can let a guess through. Idleness is a
|
|
rate over a stated window, never an instant; startup is not idleness; an empty
|
|
window yields no rate rather than a rate of zero. A workload with no verdict is
|
|
**absent** from a reclamation ordering rather than ranked last in it: it is not
|
|
a worse candidate than an idle one, it is not a candidate.
|
|
|
|
There is no default threshold, because one has to sit above the hardware's idle
|
|
draw and below its working draw, and both belong to the device. An
|
|
unconfigured policy reclaims nothing — the failure of an unconfigured
|
|
destructive action should be a missed optimisation, not destroyed work.
|
|
|
|
## Usage survives the process
|
|
|
|
A scheduler that restarts with empty usage hands every tenant its whole
|
|
guarantee back. `pool/1` rebuilds every entitlement's usage by replaying its
|
|
own hash-chained ledger (SPEC §6), so the ledger is the durable state rather
|
|
than a report about it. On release the **actual** figure replaces the
|
|
reservation — the hold bounded what could start, not what was spent — and a
|
|
stream that does not verify is refused at the index of its first broken link.
|
|
|
|
Each entry is a **`pool/1` JCR-1 receipt** — canonical det-CBOR under
|
|
COSE_Sign1 with Ed25519, the wire format the rest of this family seals with,
|
|
so it verifies under a tool that has never heard of Pool (SPEC §6.5).
|
|
Authenticity and sequence are checked separately because they fail separately:
|
|
signatures without a chain admit a bag of valid receipts with any subset
|
|
removed, and a chain without signatures admits a consistent stream written by
|
|
anyone. A core that decodes but does not re-encode to the bytes it arrived as
|
|
is refused, so two encodings cannot share one signature (§6.6).
|
|
|
|
A **witness** — a party holding a key the steward does not — can sign the
|
|
ledger's head at a stated length (SPEC §6.7). A steward that later rewrites
|
|
its history produces a different head, and the receipt stops covering it. A
|
|
witness key equal to the steward's is refused outright: a countersignature
|
|
from the party being countersigned establishes nothing.
|
|
|
|
An unwitnessed ledger is still a ledger, graded `self-attested` rather than
|
|
refused (§6.8) — the reader weighs the difference, and the standard's duty is
|
|
to make it legible. What a witness establishes is bounded too: it is evidence
|
|
against revision, not a second opinion on whether an admission was right (§8).
|
|
|
|
## What it does not do
|
|
|
|
`pool/1` decides admission and preemption order. It does not place workloads,
|
|
read counters or execute anything (SPEC §1.1, §8). Energy figures arrive
|
|
already graded from whatever meter produced them, and a verdict is only as
|
|
sound as the figure behind it — which is why the grade is part of the verdict
|
|
rather than a footnote to it.
|
|
|
|
## Reference
|
|
|
|
[`pool-rs/crates/pool-core`](pool-rs/crates/pool-core) — the admission and
|
|
preemption kernel: the entitlement tree, weighted surplus, the §4 admission
|
|
order, and the §5.2 preemption order.
|