Spot Capacity Orchestrator

Architecture Views

32 views, in reading order. Every view ships three ways: an HTML page, an SVG that re-opens in diagrams.net fully editable, and draw.io source.

A platform service that runs interruptible work on capacity the organisation's own private cloud can take back, and keeps that work running well enough to be trusted. It chooses which pools of reclaimable capacity each workload runs on, drains and checkpoints work inside a 120-second notice, replaces what was lost, falls back to guaranteed capacity when a workload's declared contract would otherwise be breached, and reports the saving net of everything it cost. It serves 220 workloads on 12,000 to 40,000 vCPU across three regions the organisation owns, built from open-source software: OpenStack as the capacity provider, a node agent and partitioned controllers built in Go, Kubernetes and Slurm as the schedulers that keep placing the work, Ceph for checkpoints, and Kafka, ClickHouse and PostgreSQL for state and evidence. Read the set in order. Acts 1 and 2 fix the boundary and the people it serves. Act 3 shows the two planes and the contract that drives both. Act 4 separates four classes of data by what losing each costs. Act 5 walks placement, a notice, four drain paths, a checkpoint, a hall-wide reclamation, a fallback, a control-plane outage and a pool's exclusion. Acts 6 and 7 cover deployment, change, measurement, money, authority and every named failure. One rule governs every page: the control plane decides where capacity comes from; the node decides what happens when it goes away.

1 · Context and scope

What the orchestrator owns between the schedulers that place work and the private cloud that supplies and reclaims capacity, and what it leans on without owning.

2 · People and journeys

Who it serves, and the three moments where it either disappears or becomes the incident: moving a pipeline, riding out a hall-wide reclaim, and pricing the reliability floor.
03 People who run work on it Data engineer nightly Spark pipelines Goal — Run my pipeline for a third of the price without learning what a reclaim notice is. Core journeys Move a pipeline onto reclaimable capacity Tell a reclaim from a bug ML engineer multi-day training Goal — Lose an hour of a three-day run to a reclaim, never the whole run. Core journeys Resume from the last checkpoint Hit a deadline through a shortage CI platform owner build runners Goal — Keep build queues short on capacity that costs a third. Core journeys Requeue a build that was reclaimed People who pay for it and set its limits Capacity and FinOps lead rate card · floor Goal — Know what each point of reliability costs before I sign for it. Core journeys Put a price on the tier-1 floor Move a workload off reclaimable capacity Budget owner spend authority Goal — Approve fallback spend only when a deadline actually needs it. Core journeys Approve fallback beyond the cap People who keep it running Platform SRE on call Goal — Get through a hall-wide reclaim without waking anyone but me. Core journeys Ride out a correlated reclamation Recover the control plane slowly Private cloud team the provider Goal — Give capacity back to guaranteed tenants inside three minutes, every time. Core journeys Reclaim a pool with notice Publish prices and forward capacity Machines that act without being asked Schedulers Kubernetes · Slurm Goal — Place work on nodes that tell me before they go away. Core journeys Stop placing on a draining node Reclamation exercise daily · 1% of nodes Goal — Prove the drain path works on a day nothing is wrong. Core journeys Reclaim a sample through the real path Actors — Who It Serves and What Each of Them Gets to Do Person or role Journey / task External / third party Security / platform v 1.0 · owner Compute Platform Architecture · date 2026-09 Actors and Their Journeys Who runs work on reclaimable capacity, who pays for its reliability, who keeps it running, and the machines that act without being asked. HTML page SVG draw.io

3 · Structure

The layers, the split between the control plane and the node plane, the controllers inside one region, the agent on one node, every interface, and the contract that drives all of it.

4 · Data

Four data classes with four recovery obligations, the model that ties contracts to pools, plans, instances and evidence, and the two pipelines that turn events and prices into decisions.
14 contract contract_id, version PK tier T1..T5 | bespoke drain_budget_s, checkpoint_mode restart_semantics declared max_interrupt_per_h, floor_pct deadline, authority_id FK repo_commit workload workload_id PK owner_team NOT NULL partition_id contract_id, version FK eligible_attrs arch, gpu, nvme placement_plan plan_gen PK workload_id FK ranking_id FK units_by_pool vCPU, GiB, GPU max_pool_share, max_group_share depth_premium recorded pool_ranking ranking_id PK partition_id, computed_at inputs_ref prices, rates, signal scores per pool scorer_version spend_authority authority_id PK owner, approver named cap_per_24h signed_by governance fallback fallback_id PK workload_id FK trigger rate|shortfall|deadline spend_reserved, approved_by started_at, ended_at, cost instance instance_uuid PK pool_id FK workload_id, plan_gen FK purchase_model launched_by identity pool pool_id PK sku_family, size region, hall purchase_model group_id FK attributes arch, gpu, nvme, nic lifecycle_event event_id PK instance_uuid FK type notice|drained|overrun|no_notice cause reclaim|defect|operator|drill occurred_at, notice_at wasted_vcpu_s correlation_group group_id PK basis aggregate|hall|learned co_reclaim_phi computed_at 1 : N 1 : N 1 : N 1 : N 1 : N 1 : N 1 : N 1 : N 1 : N Data Model — Contracts, Pools, Plans, Instances and Their Evidence There is no checkpoint entity. Checkpoints belong to the workload; the platform records only the wasted work that a missing one caused. v 1.0 · owner Compute Platform Architecture · date 2026-09 Data Model Contracts govern workloads; rankings and workloads produce plans; plans produce instances; instances produce the events every report is built from. HTML page SVG draw.io

5 · Runtime

Placement, a hard notice, four drain paths, a checkpoint and resume, an 800-node reclamation, fallback and return, a control-plane outage, and one pool's exclusion.

6 · Operations

Where it runs, how contracts and code change, what is measured, what the discount really saved, and the order it is built in.

7 · Assurance

Who holds the power to acquire, terminate and spend, and each of the thirteen failure classes with what detects, contains and recovers it.
32 Detected by Contained by Recovered by Single-pool reclaim Exclusion watcher ≤ 15% per pool Replace in other groups Correlated reclaim Group reclaim rate ≤ 30% per group Floor holds · fallback No-notice termination NotReady · lease expiry Periodic checkpoint Unit requeued Drain overrun Agent at T+118 s Counted · contract flagged Shorter interval proposed Checkpoint store down Client write error Lease released, recorded Resume from last generation No capacity at any price Shortfall ≥ 5 min Floor already running Fallback within authority Fallback storm Arbiter queue depth 40 per min, by priority Floor was pre-provisioned Acquisition thrash Lost within 15 min Back-off doubles Probation return Quota exhaustion Headroom ≤ 20% Alarm before it binds Quota raised by cloud team Price inversion Rate ≥ 85% guaranteed Not a discount pool Move-off recommended Control-plane outage Leader lease lost Fail static Read-only rebuild first Poison work item Attempts ≥ 4 Quarantine queue Owner notified Hall loss Provider hall status Hall excluded · caps rescaled Floor rebuilt ≤ 15 min Failure Modes — Thirteen Classes, Each Detected, Contained, Recovered Every row is exercised: the daily drill covers the first four, the quarterly game day the hall loss and the control-plane outage together. v 1.0 · owner Compute Platform Architecture · date 2026-09 Failure Modes The thirteen failure classes from the requirement, each with the signal that detects it, the mechanism that contains it and the path that recovers from it. HTML page SVG draw.io

Architecture One-Pager

The problem, the shape of the answer, the decisions that carry it, and why it should still be right in ten years.

Every allocation is made on the assumption that it will be taken back, and nothing that has to happen in the next 120 seconds depends on a service that might not be there.

Most private clouds are sized for the peak of their guaranteed tenants and sit well below it most of the time. That idle headroom can run batch pipelines, training, CI and stateless services at a fraction of the price, provided it can be taken back the moment a guaranteed tenant wants it. The cheap part is lending the capacity. The expensive part is everything that makes borrowed capacity trustworthy: knowing which workloads can survive losing a node and which cannot, draining inside a window measured in seconds, not losing hours of work, noticing when a pool is being reclaimed far more often than its price suggests, falling back to guaranteed capacity without turning a shortage into an unbounded bill, and proving afterwards that the discount was larger than everything it cost. Teams that attempt this one workload at a time build five slightly different drain scripts, infer interruption tolerance from luck, and report a gross discount that ignores the redone work and the fallback spend. This platform exists to make the reliability of interruptible work a declared contract rather than an accident of which pool it landed in, and to make the saving a net, attributed number.

Every workload admitted to reclaimable capacity declares an interruption contract in a reviewed Git repository: its drain budget, how it checkpoints, its restart semantics, the interruption rate it can tolerate, its guaranteed floor, its deadline and who may authorise fallback spend. Most owners pick one of five tiers. A compiler written in CUE and Go rejects contracts the platform cannot honour and publishes signed bundles, and Kyverno and a Slurm job_submit filter refuse any work that arrives without one. In each of three regions a partitioned Go control plane, running on guaranteed capacity, turns pending work from Kubernetes and Slurm into normalised capacity claims, ranks about 140 OpenStack pools every five minutes by forward capacity, observed interruption and rate, excludes misbehaving pools within a minute, and places each workload's floor on committed capacity and the rest across at least eight pools, with no pool above 15% and no correlation group above 30%. Instances boot already holding the drain-relevant part of their contract. When the private cloud's reclaimer issues a 120-second notice, a node agent built in Go acts within two seconds from what it already holds: it taints the node, evicts through the scheduler or, if the scheduler does not answer, drains locally, and verifies the drain rather than assuming it. Workloads checkpoint through a platform client into a Ceph RGW bucket encrypted with their own key; Spark, PyTorch and Slurm jobs get this from integrations built once. The control plane learns of the loss from the lifecycle event and replaces it elsewhere. When a workload's contract would be breached, a fallback arbiter restores its floor, spends within its declared authority, and pages a named approver beyond it. Every placement, drain, loss and fallback becomes an event with a cause in Kafka and ClickHouse, and the saving is reported per workload net of wasted work, fallback, headroom, checkpoint storage and the orchestrator's own footprint.

What it is, and what it is not

Reliability declared per workload in a reviewed contracttolerance inferred from a workload that happened to survive last month's reclaims.
A drain decided on the node from state it already holdsa notice forwarded to a central service that decides what the node should do.
Diversification measured across groups that reclaim togethera spread across pool names that share a host aggregate.
Fallback bounded by a named person's spend authorityautomatic on-demand capacity with no ceiling, discovered on next month's bill.
A saving reported net of every cost of the discountthe gap between two rate cards multiplied by hours.
Fail static when the control plane is gonea recovery that reconciles a day of drift in one burst.

The decisions that are the architecture

01Split the planes at the notice

The control plane decides where capacity comes from; the node decides what happens when it goes away. No interruption is handled by a call to a central service.

ADR-09

02The contract is the interface

Placement, drain, checkpoint and fallback are functions of a declared, reviewed contract. Changing behaviour means changing a file, never tuning the platform.

ADR-01

03The provider is held to a written contract

On premises, notice, advisory, rate card and forward capacity are built by the private cloud team to a specification, with the no-notice case as the safe floor when any of them fails.

ADR-05

04Diversify against known topology first

Correlation groups start from host aggregates and halls, which cause reclamation on premises, and are refined by observed co-reclamation rather than trusted to pool names.

ADR-06

05The agent owns the deadline, the scheduler the mechanics

Eviction goes through Kubernetes and Slurm when they answer inside 800 ms, and around them when they do not.

ADR-14

06Checkpoints belong to the workload

Application checkpoints through a platform client, in a bucket the orchestrator cannot read, with Spark, PyTorch and Slurm integrations built once.

ADR-18

07The floor is bought and it works

Guaranteed floors run real work on committed capacity; fallback beyond them spends only inside a named authority.

ADR-22

08Savings are net or they are not savings

Useful work at guaranteed prices minus every cost of running on reclaimable capacity, each charged to the workload that caused it.

ADR-26

Why it holds up over time

The instance types, the private cloud release, the schedulers and the checkpointing frameworks will all change several times over the life of this platform, and it will be run by people who were not in the room for any of these decisions. The design keeps its promises in a contract format, in events and in a small number of invariants that are tested, and keeps every product behind a seam narrow enough to replace without asking a workload team to change anything.

The contract outlives every mechanism

A drain budget, a restart semantic and a floor mean the same thing whether the notice comes from OpenStack, a Kubernetes-native harvester or a public cloud's spot service. The contract file format is versioned and owned by this platform, so a change of provider changes an adapter, not 220 declarations.

The plane split is a law of physics, not a preference

A notice window is short and a control plane can be down. That stays true whatever the window is and whatever runs the control plane, so the boundary drawn at the node does not age.

Pools are data, not code

A new hardware generation, a fifth hall or a GPU family is a row in the pool catalogue with attributes the eligibility filter already understands. Nothing in scoring or placement names a SKU.

Scale grows partitions, not loops

A new region or scheduler cluster adds partitions with their own leaders and token budgets. No component's workload grows with the size of the whole estate, so the architecture that serves 5,000 nodes serves 50,000 without a redesign.

The ledger is built from events with causes

Rate cards, charging models and finance rules will change. Because every lost minute, fallback and placement is kept as a costed event with a cause, a new accounting rule is a new query over the same history, not a new system.

The provider is replaceable behind a contract

The five provider commitments (notice, advisory, rate card, forward capacity, reclaim API) are written down. The same orchestrator can run against a different private cloud, or against a public cloud's spot service, by implementing those five interfaces.

The invariants are exercised every day

Drain within budget, fail static without a control plane, no termination outside owned projects: the daily drill and the quarterly game day test these against production, so they survive team turnover and the well-meaning refactor that would otherwise erode them.

The eligible population is the metric

The platform recommends moving work in both directions, off reclaimable capacity when the saving does not justify the risk and onto it when a guaranteed workload behaves as if it could tolerate interruption. It is judged by how much of the estate can safely use it, which keeps it useful as the estate changes shape.

Non-functional targets

The requirement's targets, how this design meets each one, and the view where the mechanism is drawn. Where a figure is a planning estimate, it is the first number the proof phase replaces.

QualityTargetHow it is metView
Notice to drain signal ≤ 2 s p99; ≥ 115 s left Per-hall NATS relay; decision from a local signed bundle; scheduler API capped at 800 ms before the local path 18
Final checkpoint ≤ 45 s p99 for 2 GB Multipart writes to an in-region RGW sized for 20 GB/s; manifest written last 20
Replacement serving ≤ 180 s cold; ≤ 20 s warm Replacement requested from the notice event in parallel with drain; warm headroom per contract in Phase 2 21
Placement decision ≤ 10 s p99 Incremental planner over changed claims only; normalised units; pre-computed ranking 17
Ranking and exclusion ≤ 5 min refresh; exclusion ≤ 60 s Batch scorer every 5 minutes; streaming exclusion watcher on lifecycle events 16
Control plane availability ≥ 99.9% monthly Three replicas over three halls, leader per partition, Patroni with a synchronous replica 25
Node-local handling ≥ 99.99%; ≥ 12 h without control plane systemd agent with no central dependency; bundles never expire; local drain fallback 23
Guaranteed floor ≥ 30% of tier-1 target Floor filled first from committed reservations, spread over ≥ 3 halls, rebuilt within 15 minutes of a hall loss 22
Discount share ≥ 70% of eligible hours Tiers T2–T5 carry low or no floor; move-on recommendations grow the eligible population 28
Net saving ≥ 55% blended 59% planned: €199k of €335k counterfactual, net of all costs 28
Wasted work ≤ 3% of reclaimable hours Final write on notice; 60-minute periodic insurance; requeue units bounded by rate × length ÷ 2 20
Drain overruns ≤ 0.5% of reclamations Feasibility check at compile time; overruns counted and contracts flagged 12
Capacity shortfall ≤ 0.1% of capacity-hours; tier 1 > 15 min is an incident Floor on committed capacity; capped automatic fallback; shortfall measured apart from interruption 22
Orchestrator cost ≤ 2% of realised saving Shared estate Kafka, ClickHouse and Prometheus; 20 m CPU agent; 1.6% planned 28
State recovery RPO ≤ 30 s, RTO ≤ 10 min; rebuild ≤ 5 min Synchronous replica; provider tags as reconstruction key; read-only rebuild before acting 13
Retention Events 400 d; rankings 90 d / 13 mo; cost 7 y; checkpoints 3 gen or 7 d ClickHouse TTLs; monthly Object Lock export; RGW lifecycle rules per bucket 13

Scope

In scope

  • Interruption contracts, tiers, feasibility checks and admission refusal in each scheduler
  • The pool model, eligibility, correlation groups, ranking, exclusion and concentration caps
  • Acquisition against normalised capacity, replacement on notice, quota tracking and provider rate limits
  • The node agent: notice handling, cordon, drain through or around the scheduler, verification and overrun records
  • The checkpoint client, framework integrations and per-workload checkpoint buckets
  • Guaranteed floors, capped automatic fallback, escalation, staggering and return
  • Lifecycle events, net savings accounting, recommendations, audit of capacity actions and production drills

Explicitly out of scope

  • How much capacity a workload needs, which remains with the schedulers and their autoscaling
  • Placing work onto nodes, which remains with Kubernetes and Slurm
  • The reclaim decision, the notice relay and the rate card, which the private cloud team builds to the provider contract
  • Checkpoint contents and workload correctness beyond declared restart semantics
  • Any managed cloud service; everything runs on hardware the organisation owns

Prove it before production depends on it

Eight weeks, one region, one Kubernetes cluster and one Slurm cluster of 800 reclaimable nodes between them, and three volunteer workloads: a Spark pipeline, a PyTorch training job and a CI runner pool. Each item replaces an assumption in this record with a measurement.

  1. Deliver per-instance notices through the NATS relay to 5,000 simulated subscribers and to 800 real agents at once, and record p99 from reclaimer to agent
  2. Reclaim all 800 nodes in one wave with the API server under normal load, and time taint, eviction and verified drain per node; repeat with the API server made unreachable
  3. Write 320 concurrent 1.4 GB checkpoints through the client into RGW with SSE-KMS and record p99 commit time and gateway saturation
  4. Decommission Spark executors holding the largest shuffle in the estate and record migration time against the 110-second budget
  5. Kill the control plane for 12 hours during normal reclaims and confirm drains, overrun counts and the absence of any mass termination or acquisition on recovery
  6. Run the ranking and planner on a replay of three months of reclamations and compare wasted work and concentration with a price-first planner
  • Pass: four weeks of daily 1% drills and at least one hall-scale wave with drain overruns under 0.5%, wasted work under 3% and no tier-1 shortfall over 15 minutes.
  • Pass: the net saving on the three volunteer workloads, measured by the ledger, is above 50% after every cost.
  • Fail and rethink: the relay cannot deliver inside a second at fleet scale, which moves notice delivery onto a per-hypervisor channel exposed to the guest, or RGW cannot absorb the burst, which changes tier-1 checkpointing from final-write to periodic plus requeue.

Open risks, carried rather than hidden

RiskIf it landsResponse
The provider components are late or weaker than specified Without a timely notice, every reclamation is a no-notice termination The design is safe in the no-notice case by construction; tier contracts fall back to periodic checkpoints and requeue, at a higher wasted-work cost that the ledger reports (ADR-05, ADR-17)
Workload owners over-declare tolerance Breaches, redone work and fallback spend land on workloads that should not be on reclaimable capacity Breach alarms from measured rates, feasibility checks at compile time, fallback and waste charged to the owner, and contract-priced chargeback in Phase 3 (ADR-01, ADR-27)
Correlation is worse than the declared groups A reclaim takes more than 30% of a workload's reclaimable capacity at once The floor bounds the damage; learned co-reclamation groups in Phase 2 tighten the caps; the quarterly game day tests a hall-scale wave (ADR-06)
The checkpoint burst saturates RGW Final writes miss the window during the largest reclaims Store sized for twice the planned burst and measured in the proof; write failure releases the lease rather than losing work silently (ADR-19, ADR-20)
Rate-card savings never reach a purchase order The platform reports savings that capacity planning does not act on The ledger's reclaimable hours are published as an input to guaranteed-capacity planning, and the saving is labelled as rate-card until procurement uses it (ADR-26)
Built components become a bespoke burden A small team owns an agent, a controller, a compiler and three client libraries Each is small and tested by the daily drill; everything stateful and every protocol is adopted rather than built (ADR-10, ADR-13)

Architecture Decision Record

Why every component and every technology on these 32 views is what it is, and what each choice costs.

Thirty-three decisions make up this architecture. Everything else on the thirty-two views is convention, and convention needs no defence. Each record opens with the question that forced a decision, says what was chosen and how it is built on hardware the organisation owns, lists the options that lost and why, and states what the choice costs. It then names the conditions under which a different organisation should choose differently, and explains why the choice should still hold after the products, the team and the estate have changed. Read the one-pager first. Read a record when you want to argue with it.

Status of this document. This is a design, not a report on a running system. Rates, latencies, volumes and costs are the requirement's stated assumptions or planning estimates derived from them, and the proof phase replaces each with a measurement. The design relies on behaviours that must be confirmed on the exact releases and hardware chosen before production depends on them: a NATS notice relay delivering a per-instance message to 5,000 subscribers inside one second at p99, including during a hall-wide reclaim; the Kubernetes Eviction API and taint updates completing for 800 nodes inside the drain budget without API-server throttling; kubelet graceful node shutdown honouring a systemd inhibitor delay of 110 seconds on the node images in use; a Kubernetes ValidatingAdmissionPolicy restricting a node-bound service-account token to taints and evictions on its own node; Spark executor decommissioning migrating shuffle to an S3-compatible fallback store within 90 seconds for the largest executors in the estate; Ceph RGW absorbing about 10 GB/s of multipart checkpoint writes from 320 concurrent writers with SSE-KMS through OpenBao; OpenStack application credentials and project-scoped roles preventing any other identity from deleting instances in the orchestrator's projects; and Nova multi-create and quota enforcement behaving under a token budget of 10 mutating calls a second per region. The provider-side components (the reclaimer, notice relay, rate card and reservation feed) do not exist in upstream OpenStack; this design specifies them and the private cloud team builds them.

How to read a record

QuestionThe forcing question: why a decision was needed at all.
ContextThe requirement, the scale and the constraint that make it hard.
DecisionWhat this architecture does, stated so it can be checked.
How it works on premisesThe concrete mechanism: which package, configured how, on whose hardware.
Options weighedChosen, rejected, deferred, or right elsewhere, with the reason for each.
ConsequencesWhat the choice buys and what it costs, both kept visible.
Choose differently whenThe conditions that would flip the decision for your system.
Why it holds up over timeWhat keeps the decision right as scale, staff and technology change.
LessonThe principle that transfers beyond this platform.

Decision map

Contract and admission 4

What a workload must declare before it runs on capacity that can be taken back, how that declaration is checked, and where work without one is refused.

ADR-01A declared interruption contract is the only input to placement, drain, checkpoint and fallback ADR-02Five tiers cover most workloads, with a reviewed bespoke contract for the rest ADR-03Contracts live in Git, are compiled and checked for feasibility, and ship as signed bundles ADR-04Placement without a contract is refused inside each scheduler's admission path

Provider and pools 4

What the private cloud promises, what a pool is, how pools that reclaim together are recognised, and how they are ranked and chosen.

ADR-05The capacity provider is the organisation's OpenStack cloud, held to a written provider contract ADR-06A pool is SKU family × size × hall × purchase model, and diversification is measured across correlation groups ADR-07Pools are ranked on a five-minute clock and excluded on a sixty-second stream, and every ranking is kept with its inputs ADR-08Placement prefers capacity depth over price, fills the floor first, and records what the preference cost

Control plane 4

Where the interruption boundary is drawn, how the deliberative part of the system is built and partitioned, where its state lives, and how it is kept from becoming the outage.

ADR-09The control plane decides where capacity comes from; the node decides what happens when it goes away ADR-10The control plane is partitioned, level-triggered controllers built in Go on a guaranteed management cluster ADR-11PostgreSQL holds fleet state for speed; tags on the provider's instances hold it for truth ADR-12Actuation is bounded: regional token budgets, termination breakers and gradual recovery

Node and drain 5

What runs on every reclaimable node, who owns the drain, what the node remembers when nothing else answers, and what happens when no notice comes at all.

ADR-13The node agent is a static binary under systemd, baked into images and configured at launch ADR-14The agent owns the trigger and the deadline; the scheduler's own eviction does the work, with a local fallback ADR-15The node caches only interruption-critical fields, signed, and never lets them expire ADR-16Proactive drain on an advisory is enabled per contract, and its false positives are charged to that contract ADR-17Notices arrive by stream with a metadata poll behind them, and the no-notice case is covered by leases, not by the agent

Work preservation 4

Who checkpoints, where checkpoints live and who can read them, how often to take them, and how a resumed unit avoids doing anything twice.

ADR-18Checkpoints are application-level through a platform client, with frameworks implemented once ADR-19Checkpoints live in a Ceph RGW bucket per workload, encrypted with the workload's key and unreadable by the platform ADR-20The periodic checkpoint interval is set as a loss budget, not a habit ADR-21Restart semantics are declared, effects are published only on completion, and poison units are quarantined

Floor and fallback 3

How much guaranteed capacity is held, who may spend beyond it, and how fallback and return avoid storms and oscillation.

ADR-22The reliability floor is bought, runs real work, and is drawn from committed capacity first ADR-23Fallback restores the floor without asking, spends automatically within a declared cap, and escalates beyond it ADR-24Fallback is staggered by priority and return waits for sustained health

Evidence and money 4

How every capacity event is recorded, how the saving is calculated honestly, who sees it, and who is woken when something goes wrong.

ADR-25Lifecycle events ride the estate's Kafka and ClickHouse; capacity actions are audited separately ADR-26The saving is computed net against a guaranteed-rate counterfactual, with every cost attributed ADR-27Showback per workload now; chargeback priced by the declared contract later ADR-28Metrics on Prometheus and Thanos, with alarms routed to whoever can fix the cause

Security and authority 3

Who holds the power to acquire, terminate and spend, what a compromised node or workload can reach, and what happens to a reclaimed node's credentials.

ADR-29Capacity authority is scoped by OpenStack project, credentials are short-lived, and nodes hold none ADR-30Floors, spend caps and fallback policy need a second, higher authority ADR-31A terminated instance's identity and credentials are revoked, not left to expire

Operations 2

How the platform changes without becoming the cause of the interruptions it manages, and how its promises are tested on an ordinary day.

ADR-32Releases ride the fleet's own churn: canary partitions for contracts, new launches for agents ADR-33Reclamation is exercised daily in production through the provider's own reclaim path

Technology by capability

Every capability on the views, the package or design element that provides it, a credible alternative, and the record that justifies the choice. Everything runs on hardware the organisation owns. The provider-side components are built by the private cloud team to the provider contract, and are listed so the dependency is visible.

Open source This design
CapabilityChoiceOriginCredible alternativeWhy this oneRecord
Interruption contract Versioned YAML contracts, five tiers This design Inferred tolerance; platform knobs Behaviour is a reviewable declaration ADR-01
Contract store and review Forgejo, mirrored to a second region Open source Database behind an API History, review and RPO 0 for free ADR-03
Contract compiler CUE schemas plus Go feasibility rules Open source OPA / Rego Arithmetic over pool history reads clearly ADR-03
Bundle signing cosign with a key in OpenBao Open source Unsigned bundles over mTLS Nodes verify what they act on ADR-03
Admission on Kubernetes Kyverno validating policies Open source Built admission webhook Refusal at submit time, fails closed ADR-04
Admission on Slurm job_submit Lua plugin Open source Submission wrapper script Cannot be bypassed by calling sbatch directly ADR-04
Capacity provider OpenStack Nova, Placement, Ironic, Keystone Open source In-scheduler harvesting; public cloud spot Existing regions, quotas and bare metal ADR-05
Reclaimer, rate card, advisories Built by the private cloud team to contract This design Owned by the orchestrator Lender decides reclaims, borrower reacts ADR-05
Committed capacity and forward calendar OpenStack Blazar reservations Open source Static quota tables Known future demand, already paid-for floor ADR-22
Notice delivery NATS relay per hall, metadata poll behind it Open source Metadata polling; hypervisor channel Sub-second push with a slower fallback ADR-17
Pool model and correlation groups SKU × size × hall × model; aggregate and hall groups This design Independent pools Diversify against known causes ADR-06
Scoring and exclusion 5-minute scorer; streaming exclusion watcher This design Single fast loop Judgement and reflex at their own speeds ADR-07
Portfolio planner Depth-first water-filling with caps This design Price-first; global optimiser Fewer interruptions, premium recorded ADR-08
Controllers Go, partitioned, level-triggered, Kubernetes leases This design Temporal workflows; Karpenter fork Rebuildable from the provider, scales by partition ADR-10
Management cluster Kubernetes on guaranteed capacity, Argo CD Open source VMs under systemd Standard operations for stateless controllers ADR-10
Fleet state and spend ledger PostgreSQL 16 with Patroni and pgBackRest Open source etcd Transactions, outbox and relational ledger ADR-11
Node agent Static Go binary under systemd This design DaemonSet; kubelet extension Works on every node type, without the scheduler ADR-13
Node images diskimage-builder per SKU family Open source Packer Native to OpenStack image pipelines ADR-32
Drain through Kubernetes Eviction API; kubelet graceful node shutdown Open source Direct pod deletion Native semantics with a local fallback ADR-14
Drain through Slurm scontrol drain, requeueable jobs, USR1 Open source Kill and resubmit Jobs resume rather than restart ADR-14
Checkpoint client Go, Python and JVM libraries This design Per-team implementations One manifest protocol and notice callback ADR-18
Framework checkpoints Spark decommissioning, PyTorch Distributed Checkpoint Open source CRIU process snapshots Small, fast and semantically correct ADR-18
Checkpoint storage Ceph RGW, bucket per workload, SSE-KMS Open source CephFS; node-local NVMe Durable, isolated, lifecycle rules per bucket ADR-19
Workload encryption keys OpenBao transit, key per workload Open source One RGW-wide key Orchestrator cannot read contents ADR-19
Floor and fallback Floor keeper and fallback arbiter in the controller This design Manual fallback Automatic within named authority ADR-23
Lifecycle events Kafka (estate cluster), Vector on nodes Open source Direct writes to ClickHouse Buffered, ordered per instance, shared cost ADR-25
History, ledger and reports ClickHouse, Grafana Open source PostgreSQL for everything Columnar history at 13-month and 7-year horizons ADR-26
Cost archive Ceph RGW with Object Lock, monthly Parquet Open source ClickHouse only Finance retention outside the query store ADR-25
Capacity action audit Outbox to the estate audit log service This design Kafka telemetry Independent of the telemetry pipeline ADR-25
Metrics and alerting Prometheus, Thanos, Alertmanager Open source VictoriaMetrics Estate standard, 13-month queries ADR-28
Workload and node identity SPIRE, node-bound service-account tokens Open source Static node certificates Revocable per node on termination ADR-31
Provider credentials OpenStack application credentials via OpenBao Open source Long-lived service user password One-hour, project-scoped, audited ADR-29
Human authentication Estate OIDC provider with WebAuthn Open source Passwords Phishing-resistant approval of spend ADR-30
Production drills Exercise job calling the provider's reclaim API This design Chaos Mesh pod kills Tests the real notice path, not a simulation ADR-33

The decisions, and the alternatives that lost

Contract and admissionWhat a workload must declare before it runs on capacity that can be taken back, how that declaration is checked, and where work without one is refused.

ADR-01

A declared interruption contract is the only input to placement, drain, checkpoint and fallback

Accepted

How does the platform know what a workload can tolerate, and what decides how it is placed, drained and protected?

Context
Interruptible capacity is only useful if workloads that cannot survive interruption stay off it, and workloads that can survive it are handled in the way they need. The tempting shortcut is to infer tolerance: a job that has run on reclaimable capacity for a month without complaint is assumed to tolerate it. That confuses luck with design. A pool that happened to be quiet teaches nothing about the correlated reclaim that has not happened yet. The requirement asks for a declared contract, refused placement without one, re-evaluation against observed reality, and the contract as the single input to placement, fallback and drain.
Decision
Every workload on reclaimable capacity has a versioned contract stating drain budget, checkpoint mode and interval, restart semantics, maximum tolerable interruption rate, guaranteed floor, deadline if any, and the spend authority for fallback. Placement, eligible pools, drain grace, checkpoint trigger, fallback triggers and spend caps are computed from it and from nothing else. Observed behaviour is compared with the contract and raises a breach alarm; it never silently changes the contract.
How it works on premises
Contracts are YAML files in a Forgejo repository, one per workload, referencing a tier and overriding only permitted fields. The compiler resolves tiers and publishes a signed bundle per partition. The contract id and version travel as a Kubernetes label or a Slurm job comment, are stamped on every instance's tags and every lifecycle event.
Options weighed
  • ChosenDeclared contract as the only input: Behaviour is reviewable, reproducible and changed by editing one file. Owners must think once about their workload.
  • RejectedInfer tolerance from observed survival: No onboarding effort. Treats a quiet quarter as evidence and fails at the first correlated reclaim.
  • RejectedPer-workload tuning knobs on the platform: Flexible. Behaviour spreads across platform configuration nobody reviews, and changing it needs the platform team.
  • RejectedOne global policy for all interruptible work: Simplest. A deadline pipeline and a CI runner need opposite trade-offs.
Consequences
What it buys
  • Every behaviour can be explained by pointing at a line in a reviewed file
  • Breach alarms compare measured reality with an explicit promise
  • A change of provider or scheduler does not change what a workload declared
What it costs
  • Onboarding is a pull request, not a checkbox
  • Owners can declare optimistically, which needs alarms and eventually pricing to correct
  • The contract schema becomes an interface that must be versioned carefully
Choose differently when
If nearly all interruptible work were one homogeneous kind, such as CI runners with short, idempotent jobs, a single platform policy with an opt-out would be simpler and nearly as safe.
Why it holds up over time
What a workload can tolerate is a property of the workload, not of the capacity it runs on. Whatever supplies the capacity in ten years, the same declaration still drives the same decisions, and the history of breaches against it remains meaningful.
LessonSurviving interruption once is an observation. Being designed to survive it is a declaration. Only the second can be relied on.
Shown on views04 12 14
ADR-02

Five tiers cover most workloads, with a reviewed bespoke contract for the rest

Accepted

How much of the contract should a workload owner have to compose themselves?

Context
A contract with seven fields and their interactions is too much to expect of 220 teams. The requirement asks for a small set of tiers so that most owners choose one, while permitting a bespoke contract where no tier fits. The tiers must be few enough to explain on one page and distinct enough that the choice matters.
Decision
Five tiers. T1 deadline: 30% floor, checkpointing required, automatic fallback within a spend cap. T2 checkpointed long-running: 10% floor, checkpointing required, fallback on breach within a cap. T3 requeue: no floor, no checkpoint, units bounded in length by the wasted-work budget. T4 stateless service: 30% floor, connection drain inside 60 s, replacement before loss. T5 opportunistic: no floor and no fallback, for CI and exploratory work that waits rather than spends. A bespoke contract starts from the nearest tier and is reviewed by the platform team.
How it works on premises
Tiers are CUE definitions in the contract repository. A workload file names a tier and may override only fields that tier allows; for example T3 may shorten its maximum unit length but cannot add a floor. Every tier change needs the same review as a bespoke contract.
Options weighed
  • ChosenFive tiers plus reviewed bespoke contracts: One page to explain, with an escape hatch that is visible when used.
  • RejectedThree tiers: Easier. Forces services and deadline batch into one tier with the wrong drain semantics for one of them.
  • RejectedFully composed contracts only: Maximum precision. Most owners copy someone else's contract without understanding it.
Consequences
What it buys
  • Most onboarding is choosing a word
  • Tier reports make fleet-level trade-offs visible
  • Bespoke contracts are rare enough to review properly
What it costs
  • Some workloads sit uneasily between tiers
  • Tier definitions change rarely and need a migration when they do
Choose differently when
An estate with a very large number of teams and a strong platform-engineering culture might prefer composable profiles with policy checks instead of fixed tiers.
Why it holds up over time
The tiers describe kinds of work that have existed for decades: deadline batch, long computation, short idempotent units, stateless services and best-effort jobs. The numbers inside each tier will be tuned; the five kinds will not disappear.
LessonA choice between five named options gets made carefully. A form with seven numeric fields gets filled in with defaults.
Shown on views04 06 12
ADR-03

Contracts live in Git, are compiled and checked for feasibility, and ship as signed bundles

Accepted

Where do contracts live, and how does the platform refuse a contract it cannot honour?

Context
The requirement asks that a drain budget longer than the notice window be treated as a request for proactive draining, that a checkpoint which cannot be written inside the drain budget be rejected, and that the contract and policy store have an RPO of zero. A contract that is syntactically valid can still be impossible, and finding that out during a reclaim is the most expensive way to learn it.
Decision
Contracts are files in a Git repository and nowhere else. A compiler validates them against the schema and runs feasibility rules: drain budget above 115 s requires advisory drain; the declared final checkpoint must fit the budget at measured store throughput; a requeue unit must satisfy rate × length ÷ 2 ≤ wasted-work budget; at least eight eligible pools must exist. It replays 30 days of pool history to show expected placement and fallback frequency in the pull request, then emits a bundle per partition signed with cosign.
How it works on premises
Forgejo holds the repository, mirrored to a second region. Forgejo Actions runs the compiler, written as CUE schemas plus Go feasibility rules, on self-hosted runners. Bundles are signed with a key held in OpenBao and served by the contract distributor over HTTPS.
Options weighed
  • ChosenGit, compiled with feasibility checks, signed bundles: Reviewable history, RPO 0, and impossible contracts rejected before they reach a node.
  • RejectedContracts in a database behind an API: Easy to build a UI for. Loses review, diff and history unless all of Git is rebuilt.
  • RejectedKubernetes custom resources: Native for Kubernetes workloads. Slurm jobs have no home, and cluster etcd becomes the system of record.
  • Right elsewhereOPA policies instead of a compiler: Good for yes/no checks. Feasibility needs arithmetic over pool history, which reads poorly in Rego.
Consequences
What it buys
  • A rejected contract says exactly which rule failed and how to fix it
  • Contract history is the audit trail of reliability decisions
  • Nodes verify what they run
What it costs
  • Feasibility rules must be kept in step with measured store and API performance
  • Owners need Git access and some fluency with YAML
Choose differently when
If contracts changed many times a day under automation rather than by people, an API with the same compiler behind it would fit better than pull requests.
Why it holds up over time
Git, a schema and a signature are the most stable interfaces in software. The compiler's rules will evolve, but the pattern of reviewing a declaration and refusing what cannot be honoured does not depend on any product.
LessonThe cheapest place to find out a promise cannot be kept is before anyone relies on it.
Shown on views07 12 26
ADR-04

Placement without a contract is refused inside each scheduler's admission path

Accepted

Where is work without a contract stopped from reaching reclaimable capacity?

Context
The platform does not place work; the schedulers do. If the only guard were in the orchestrator, any pod that tolerated the reclaimable taint or any job submitted to a reclaimable partition could land there directly, contract or not, and nobody would know until it was reclaimed.
Decision
Reclaimable nodes carry a taint in Kubernetes and belong to dedicated partitions in Slurm. Kubernetes admission refuses any pod that tolerates the taint without a contract label referencing a published contract version. Slurm's job_submit filter refuses jobs to reclaimable partitions without a contract comment. Both check against the current bundle, which they hold locally.
How it works on premises
Kyverno validating policies in every Kubernetes cluster, reading a ConfigMap synchronised from the bundle. A Lua job_submit plugin on each slurmctld, reading the same bundle from disk. Both fail closed for reclaimable targets and do not affect guaranteed workloads.
Options weighed
  • ChosenAdmission in each scheduler: Refused at submission, with a message, even when the orchestrator is down.
  • RejectedOrchestrator refuses to create nodes for unknown work: No scheduler changes. Does nothing about work landing on nodes that already exist.
  • RejectedDetect and evict after placement: Permissive. The workload is already running on the wrong kind of capacity by the time anyone acts.
Consequences
What it buys
  • No path onto reclaimable capacity bypasses the contract
  • Owners get the refusal at submit time, not at the first reclaim
What it costs
  • Two admission implementations to keep in step
  • A bundle sync failure blocks new reclaimable work, by design
Choose differently when
If a single scheduler ran all interruptible work and exposed a native quota or flavour mechanism tied to policy, admission could be expressed there instead of in a separate policy engine.
Why it holds up over time
Every scheduler that will ever run this work has an admission hook of some kind. The rule, no contract no placement, is small enough to port to whatever replaces Kyverno or Slurm.
LessonA rule enforced only by the system that is not in the path is a suggestion.
Shown on views07 10 12

Provider and poolsWhat the private cloud promises, what a pool is, how pools that reclaim together are recognised, and how they are ranked and chosen.

ADR-05

The capacity provider is the organisation's OpenStack cloud, held to a written provider contract

Accepted · proof required

On hardware the organisation owns, what plays the part of a cloud's spot market, and who builds the signals the requirement treats as provider mechanics?

Context
The requirement was written for EC2 Spot, where reclamation notices, rebalance recommendations, prices and capacity signals exist. On premises none of these exists until someone builds it. The private cloud already has regions, halls as availability zones, flavours, projects with quotas, host aggregates, bare metal through Ironic and reservations through Blazar. What it lacks is a way to lend unreserved headroom and take it back with notice when guaranteed tenants need it.
Decision
The private cloud team runs reclaimable capacity as a purchase model on its existing aggregates and commits, in writing, to five interfaces: a hard notice at least 120 s before deletion, delivered to the instance within one second through a per-hall NATS relay and mirrored in instance metadata; an advisory when guaranteed demand is forecast to reclaim a pool, when it can be forecast; a rate card per pool every five minutes; reservation and forward-capacity data from Blazar; and a reclaim API that the exercise job may call. The orchestrator consumes these and does not decide what to reclaim.
How it works on premises
OpenStack Nova, Placement, Ironic, Keystone and Blazar as the substrate. The reclaimer is a service owned by the private cloud team, following the pattern of CERN's preemptible-instance work, which selects victims when a guaranteed request cannot be placed and publishes notices. The guaranteed tenants' promise, capacity back inside three minutes, sets the 120-second window.
Options weighed
  • ChosenOpenStack with a provider contract: Uses the estate's existing regions, quotas and bare metal; separates the lender from the borrower.
  • Right elsewhereHarvest idle capacity inside each Kubernetes cluster by priority preemption: Simpler and right for an estate with one scheduler. Here three schedulers share hardware, so harvesting must happen below them.
  • RejectedThe orchestrator also owns the reclaimer: One team, fewer interfaces. The borrower would then decide when lenders get their capacity back.
  • Right elsewherePublic cloud spot capacity: The requirement's own answer. Excluded by the on-premises constraint; the provider contract maps onto it directly.
Consequences
What it buys
  • The signals are better than a public cloud's: aggregates and reservations are known facts
  • The lender's and borrower's promises are written down separately
  • The same orchestrator can run against another provider that meets the five interfaces
What it costs
  • Five provider components have to be built by another team, on their roadmap
  • Notice delivery at fleet scale is unproven until measured
  • The 120-second window is a negotiated number, not a law
Choose differently when
If the estate ran one scheduler over all its hardware, harvesting inside that scheduler with priority preemption would remove the need for instance-level reclaim entirely.
Why it holds up over time
A borrower of reclaimable capacity always needs notice, a price and a view of future supply. Writing those as interfaces rather than as calls to a product means the orchestrator survives a change of private cloud, a move to a public cloud, or both at once.
LessonWhen the platform you depend on is run by colleagues, write the contract anyway. It is the only thing that survives a reorganisation.
Shown on views01 08 11
ADR-06

A pool is SKU family × size × hall × purchase model, and diversification is measured across correlation groups

Accepted

What is the real unit of diversification, and can correlation between pools be known at all?

Context
Pools are only useful for spreading risk if they fail independently. On a public cloud, correlation is unpublished and has to be learned slowly from events that may be too rare to learn from. On premises the cause of reclamation is known: guaranteed demand landing on a host aggregate, or a hall losing power or cooling. Pools that share an aggregate or a hall reclaim together by construction, whatever their flavour names say.
Decision
A pool is the narrowest unit reclaimed independently: server SKU family, flavour size, hall and purchase model. Pools are grouped into correlation groups by shared host aggregate and by hall from provider topology in the MVP. In Phase 2, observed co-reclamation (the phi coefficient of two pools being reclaimed in the same ten-minute window over 90 days) splits or merges groups. Each workload keeps at least eight pools, no more than 15% of its reclaimable capacity in one pool and no more than 30% in one group.
How it works on premises
The pool catalogue is synchronised from Nova flavours, Placement resource providers and aggregate membership. Group membership and phi values are stored with each ranking so a placement can be explained by the groups in force when it was made.
Options weighed
  • ChosenDeclared topology groups, refined by observed co-reclamation: Starts from known causes; learns what topology does not show.
  • RejectedAssume pools are independent: Cheapest spread. Wrong for every pool sharing an aggregate, and discovered during the incident.
  • DeferredLearn correlation only from events: Honest where topology is unknown. Needs history the estate does not have yet; becomes the refinement in Phase 2.
  • RejectedDiversify only across the widest attributes: Safe. Leaves most of the price and depth advantage on the table.
Consequences
What it buys
  • A reclaim of one aggregate is bounded at 30% of any workload's reclaimable share
  • Correlation evidence is kept with every decision
What it costs
  • Aggregate changes by the cloud team change groups and can force re-planning
  • Caps sometimes exclude the cheapest capacity
Choose differently when
On a public cloud, where aggregate membership is invisible, observed co-reclamation and conservative spreading by instance family and zone become the only tools and should be the default from day one.
Why it holds up over time
Hardware generations, flavours and halls will change; the rule that two pools which fail together count as one does not. Groups are data refreshed from topology and events, so new hardware joins the model without code.
LessonTwo pools that are reclaimed together are one pool, whatever the catalogue calls them.
Shown on views16 21 24
ADR-07

Pools are ranked on a five-minute clock and excluded on a sixty-second stream, and every ranking is kept with its inputs

Accepted

How quickly must the platform react to a pool going bad, and how can a bad placement be explained afterwards?

Context
The requirement asks for rankings refreshed at least every five minutes, exclusion within 60 seconds of a threshold, automatic return after a cool-off, and a versioned record of scoring inputs so that a bad decision can be traced to the signal that produced it. A single loop cannot do both jobs well: ranking needs history and is expensive; exclusion needs to be immediate and is simple.
Decision
A scorer runs every five minutes per region and writes a ranking with its inputs: rate card, 30-day interruption rates, forward capacity and quota headroom. An exclusion watcher consumes lifecycle events continuously, trips a pool on three reclamation waves in 30 minutes or on acquisition thrash, excludes it immediately, and applies a two-hour cool-off that doubles on repeat, then an hour of probation capped at 5% of any workload. Rankings are retained 90 days in full and 13 months aggregated.
How it works on premises
The scorer is a Kubernetes CronJob reading ClickHouse and the provider's feeds and writing to PostgreSQL and ClickHouse. The exclusion watcher is a Kafka consumer group per region with state in PostgreSQL. Both are part of the controller codebase in Go.
Options weighed
  • ChosenTwo clocks: batch ranking and streaming exclusion: Each loop does one job at the speed that job needs.
  • RejectedRank every 30 seconds: One loop. Expensive, and still slower than an event-driven trip.
  • RejectedPermanent exclusion list: Simple. Only grows, and concentrates the fleet in the pools nobody has excluded yet.
Consequences
What it buys
  • A pool going bad is out within a minute
  • Any placement can be replayed with the data available at the time
What it costs
  • Ranking history is the largest data set the platform keeps
  • Thresholds need tuning against real reclamation patterns
Choose differently when
A small estate with a few pools and rare reclaims could rank hourly and exclude by hand without losing much.
Why it holds up over time
The split between slow judgement and fast reflex is independent of the signals used. Predictive scoring in Phase 3 replaces the scorer's formula and leaves the exclusion reflex and the record untouched.
LessonKeep the decision and the evidence together. A ranking without its inputs cannot be argued with, only obeyed.
Shown on views09 16 24
ADR-08

Placement prefers capacity depth over price, fills the floor first, and records what the preference cost

Accepted

When a cheaper pool and a deeper pool are both eligible, which should the planner prefer, and how is that choice kept honest?

Context
On premises a pool is cheap because guaranteed tenants are not using it yet; the forward calendar often shows they are about to. Choosing by price alone steers work towards the pools most likely to be reclaimed. The requirement asks for allocation strategies that favour depth, with the cost of that preference recorded so it can be reviewed.
Decision
The planner fills a workload's claim in a fixed order: guaranteed floor from committed reservations; then reclaimable units by composite score, where forward capacity and observed interruption outweigh rate; water-filled under the pool and group caps. Pools priced at or above 85% of the guaranteed rate are not treated as discount pools. For every plan the planner records the depth premium: the difference between what it chose and the cheapest eligible placement under the same caps.
How it works on premises
Implemented as a greedy water-filling allocation in the controller, which is fast enough for 60 to 80 eligible pools per workload in milliseconds. The depth premium is written to the plan row and summed monthly in the ledger.
Options weighed
  • ChosenDepth first, with a recorded premium: Fewer interruptions; the price of the preference is visible and reviewable.
  • RejectedLowest price first: Maximum gross discount. Concentrates work where reclaim is imminent.
  • DeferredGlobal optimisation across all workloads: Better packing. Expensive to run, hard to explain, and needed only when floors are contended (Phase 3).
Consequences
What it buys
  • Interruption rate and wasted work fall
  • Reviewers can see whether depth is still worth what it costs
What it costs
  • A measurable premium over the cheapest placement
  • Per-workload greedy fills can be locally suboptimal
Choose differently when
Workloads that are nearly free to interrupt, such as T5 opportunistic jobs, can reasonably use price-first placement; the tier can say so.
Why it holds up over time
The trade between price and depth exists in every market for interruptible capacity. Recording the premium means the preference can be re-weighted from evidence as the market changes, instead of being argued from memory.
LessonA preference whose cost is not written down becomes a habit nobody can justify or remove.
Shown on views16 17 14

Control planeWhere the interruption boundary is drawn, how the deliberative part of the system is built and partitioned, where its state lives, and how it is kept from becoming the outage.

ADR-09

The control plane decides where capacity comes from; the node decides what happens when it goes away

Accepted

Should the response to a reclaim notice be decided centrally, with global knowledge, or locally, from what the node already holds?

Context
A notice arrives, and every choice in response to it is better made with global knowledge: how long to allow, whether to checkpoint, where the replacement goes. So the tempting design reports the notice to the orchestrator, which decides and instructs the node. That sequence must complete inside 120 seconds for thousands of nodes at once during a correlated reclaim, quite possibly while the same event is degrading the control plane. It works perfectly in the steady state and fails exactly when it is needed, which is the failure this platform exists to prevent for its workloads.
Decision
Split the two planes on the interruption boundary. Placement, scoring, acquisition, replacement and fallback are global and deliberative, live in the control plane, and their unavailability costs convergence. Cordon, drain, checkpoint trigger, verification and lease release are local and immediate, and are executed by the node agent from a contract it received before any work landed. No reclaim notice is ever routed through a central service before the node acts. Replacement is requested from the lifecycle event, in parallel, and nothing on the node waits for it.
How it works on premises
The node agent receives notices from the provider's per-hall relay and holds a signed contract bundle on local disk. The control plane runs per region on a guaranteed management cluster and learns about each notice from Kafka. The two share no synchronous call on the notice path.
Options weighed
  • ChosenSplit planes at the notice: Drain works with the control plane down; the control plane's failure costs replacement speed only.
  • RejectedCentral decision per notice: Globally optimal in the steady state. A round trip inside 120 s for thousands of nodes during the event that is most likely to break it.
  • RejectedNode decides everything, including replacement: Fully independent. Thousands of nodes acquiring capacity at once is a request storm with no view of caps or spend.
Consequences
What it buys
  • Fail static is a property of the architecture, not an aspiration
  • Drain scales with the number of nodes, because each node does its own
  • The control plane can be upgraded or lost without affecting a single drain
What it costs
  • The node's decision uses cached, possibly stale, contract fields
  • The drain cannot use global knowledge, such as a better-than-usual replacement being imminent
Choose differently when
If notice windows were hours rather than seconds and reclaims never correlated, a central decision with global knowledge would be both safe and better.
Why it holds up over time
The argument rests on two facts that do not age: a notice window is short, and any central service can be down. Every future change either honours the boundary, with the interruption path ending at the node, or contradicts it by drawing an arrow from a notice into a central service, which makes the rule easy to review against.
LessonPut each decision where it can still be made on the worst day, not where it would be made best on an average one.
Shown on views02 07 08 18
ADR-10

The control plane is partitioned, level-triggered controllers built in Go on a guaranteed management cluster

Accepted

How is the deliberative part of the system built, and how does it scale without becoming a single global loop?

Context
The requirement asks for scaling by fleet partition rather than one global reconciliation loop, cost of evaluation proportional to change, bounded provider API use, and addition of a region or account without redesign. The provider's view of what exists is authoritative, and the state the orchestrator holds must be reconstructible from it.
Decision
One controller binary per region, with a leader per partition, where a partition is one scheduler cluster in one region. Each controller reconciles desired plan against observed instances and acts on the difference; there is no long-running workflow state. The controllers run on a management Kubernetes cluster on guaranteed capacity across three halls, never on the capacity they manage.
How it works on premises
Go, using controller-runtime-style reconciliation with Kubernetes Lease objects for per-partition leadership. The OpenStack Go SDK (gophercloud) for Nova, Ironic, Placement and Blazar. Deployed with Argo CD to the regional management cluster.
Options weighed
  • ChosenPartitioned level-triggered controllers: Stateless between cycles, rebuildable from the provider, scaled by adding partitions.
  • RejectedA workflow engine such as Temporal: Durable multi-step flows. The authoritative state is the provider's, so workflow history is a second truth to reconcile.
  • RejectedExtend Cluster Autoscaler or Karpenter: Familiar. Kubernetes only, decides quantity as well as kind, and has no model of contracts or Slurm.
  • RejectedOne global controller: Simplest to write. Its cycle time and blast radius grow with the whole estate.
Consequences
What it buys
  • A stuck partition affects one scheduler cluster's capacity
  • A controller restart loses nothing
  • New regions and clusters are new partitions
What it costs
  • A bespoke controller codebase to own
  • Cross-partition decisions, such as contended floors, need explicit coordination later
Choose differently when
An estate with only Kubernetes and a single cluster might reasonably build on Karpenter's provisioning model with a custom OpenStack provider and a contract admission layer.
Why it holds up over time
Level-triggered reconciliation against an authoritative external state is the pattern that has outlived every orchestration fashion since Borg. Partitioning by scheduler cluster follows the estate's own seams, which change more slowly than any product.
LessonWhen someone else holds the truth, do not keep your own copy of the journey. Look again, compare, and act on the difference.
Shown on views09 25
ADR-11

PostgreSQL holds fleet state for speed; tags on the provider's instances hold it for truth

Accepted

Where does the orchestrator keep what it knows about the fleet, and what happens when that store is lost?

Context
The requirement asks for continuous reconciliation against the provider's authoritative state, reconstruction of all fleet state after a total loss of the orchestrator's store within five minutes and without operator input, an RPO of 30 seconds and an RTO of 10 minutes.
Decision
Plans, rankings in force, exclusions, the spend ledger and the event outbox live in PostgreSQL per region. Every instance the orchestrator launches carries tags naming workload, pool, partition and plan generation. On a total loss, a new database is rebuilt by listing instances in the orchestrator's projects and reading their tags; plans are recomputed from contracts and rankings. Termination requires the tags and the database row to agree.
How it works on premises
PostgreSQL 16 managed by Patroni across three halls, one synchronous and one asynchronous replica, pgBackRest to Ceph for point-in-time recovery. Instance tags through Nova server tags and metadata.
Options weighed
  • ChosenPostgreSQL plus provider tags as the reconstruction key: Transactional outbox, familiar operations, and a truth that survives its loss.
  • Rejectedetcd: Built for leases. Poor fit for the ledger's relational queries and history.
  • RejectedNo store; recompute everything each cycle: Nothing to lose. The spend ledger and exclusion state cannot be recomputed from the provider.
Consequences
What it buys
  • Losing the database loses speed and history, not ownership
  • A mistaken termination needs two independent records to be wrong
What it costs
  • Tags are part of the provider contract and must not be edited by others
  • The spend ledger's recent entries depend on the synchronous replica
Choose differently when
If the provider could not tag instances reliably, the orchestrator would need its own durable inventory with a stricter RPO and a periodic diff against provider listings.
Why it holds up over time
Any capacity provider worth using can label what it runs. Using those labels as the key to rebuild state keeps the orchestrator's store a cache of convenience across any change of database.
LessonStore your own view for speed, but make sure the world can tell you what you own when that view is gone.
Shown on views13 14 23
ADR-12

Actuation is bounded: regional token budgets, termination breakers and gradual recovery

Accepted

How is the orchestrator prevented from becoming the outage, through a request storm, a mass termination or a catch-up reconciliation after it returns?

Context
The requirement asks that the orchestrator rate-limit its own acquisition, bound provider API calls under all conditions including recovery, fail static with no mass termination or acquisition, and recover gradually because a control plane that reconciles a large drift at once is indistinguishable from an outage of its own. The provider's API is shared with every guaranteed tenant, including the ones reclaiming capacity.
Decision
Each region has a token budget of 10 mutating provider calls a second, split statically between partitions, with Nova multi-create to launch many instances per call. Terminations are capped at 2% of a partition's fleet per 10 minutes; exceeding it halts termination and pages. After any restart the controller rebuilds state read-only for two cycles, then acts on at most 5% of a partition per 5 minutes, tier 1 first. An apparently orphaned instance is terminated only after its scheduler reports it idle on three observations.
How it works on premises
Token buckets in the controller keyed by region and partition; breaker state in PostgreSQL; limits agreed with the private cloud team and recorded in the provider contract.
Options weighed
  • ChosenStatic budgets, breakers and staged recovery: Predictable load on a shared API and no burst on recovery.
  • RejectedRetry with backoff on throttling: Adaptive. The throttle is only discovered once guaranteed tenants are already affected.
  • RejectedA shared global rate limiter service: Fair sharing. Adds a central dependency to every action.
Consequences
What it buys
  • The orchestrator's worst case is a known number of calls a second
  • Recovery cannot become a second incident
What it costs
  • Replacement during a very large reclaim is slower than the provider could serve
  • Static splits leave budget idle in quiet partitions
Choose differently when
A provider with per-tenant API isolation and generous limits would allow dynamic budgets without risk to other tenants.
Why it holds up over time
Shared control-plane APIs are a commons in every cloud, public or private. Limits that are explicit, agreed and enforced by the caller remain correct whatever the provider's own throttling does.
LessonAutomation that can act on the whole fleet needs a speed limit it enforces on itself.
Shown on views17 21 23

Node and drainWhat runs on every reclaimable node, who owns the drain, what the node remembers when nothing else answers, and what happens when no notice comes at all.

ADR-13

The node agent is a static binary under systemd, baked into images and configured at launch

Accepted

What runs on each reclaimable node to handle notices, and how does it get its configuration before work arrives?

Context
The agent must work on Kubernetes nodes and on Slurm nodes, keep working when the scheduler it drains through is unavailable, and never be in a state where work is running and the drain budget is unknown. A DaemonSet is the usual way to run a node component on Kubernetes, but it depends on the kubelet and scheduler it may need to work around, and it does not exist on Slurm nodes.
Decision
A single static Go binary, running as a systemd unit, is baked into every reclaimable image. The acquirer passes the contract subset for the workloads the instance is intended for in cloud-init user-data, so the agent holds a valid drain configuration from boot. The agent then refreshes the signed bundle every 60 seconds. It has a budget of 20 millicores and 40 MiB.
How it works on premises
Images built with diskimage-builder in the release pipeline for each SKU family, one for Kubernetes nodes and one for Slurm nodes, with the same agent binary. Metrics on a local Prometheus endpoint; events to a bounded disk spool shipped by Vector.
Options weighed
  • Chosensystemd static binary configured at launch: Independent of any scheduler, identical on every node type, configured before work lands.
  • RejectedKubernetes DaemonSet: Native deployment tooling. Missing on Slurm, and starts after the kubelet has already accepted pods.
  • RejectedExtend kubelet and slurmd: No extra process. Forks two upstream projects for one feature.
Consequences
What it buys
  • The same drain logic serves every workload type
  • No window between boot and a known drain budget
What it costs
  • Agent upgrades need new images or in-place restarts
  • Image builds become part of the platform's release
Choose differently when
An estate that ran only Kubernetes with immutable node images might run the agent as a static pod, gaining Kubernetes tooling while keeping independence from the scheduler.
Why it holds up over time
systemd and a statically linked binary will run on whatever operating system these nodes use for the next decade. Configuration at launch through user-data is supported by every provider, public or private.
LessonThe component that must work when everything else is failing should depend on as little of everything else as possible.
Shown on views10 07 17
ADR-14

The agent owns the trigger and the deadline; the scheduler's own eviction does the work, with a local fallback

Accepted · proof required

Who owns the drain: the orchestrator's node agent or the workload's existing scheduler?

Context
Kubernetes and Slurm already have cordoning, eviction and graceful termination. Draining through them reuses semantics workloads understand and prevents two systems evicting the same pod, but couples the notice path to a scheduler control plane that may be slow or unavailable inside 120 seconds at fleet scale. Draining purely from the node is fast and independent but races the scheduler, which may place new work on the node or read the disappearance as a failure.
Decision
The agent decides and keeps the deadline; the scheduler executes. On notice the agent marks the node unschedulable, then evicts or drains through the scheduler with a grace period equal to the contract's drain budget, then signals the workload. If the scheduler API does not answer within 800 ms, the agent drains locally: on Kubernetes through a systemd shutdown inhibitor and the kubelet's graceful node shutdown by pod priority, on Slurm by signalling the job's cgroup. A PodDisruptionBudget is honoured until the drain budget is spent. The same path serves reclaims, voluntary rebalances, operator drains and drills.
How it works on premises
Kubernetes: a node-bound service-account token with a ValidatingAdmissionPolicy allowing taint and eviction on its own node only; kubelet GracefulNodeShutdown configured with priority-based grace periods. Slurm: scontrol drain through slurmctld, requeueable jobs with a USR1 signal, local cgroup signalling as fallback. Spark: executor decommissioning with fallback storage on Ceph.
Options weighed
  • ChosenAgent-owned deadline, scheduler-executed drain, local fallback: Native semantics when the scheduler answers; a guaranteed drain when it does not.
  • RejectedScheduler only: No second eviction path. Fails whenever the API server or slurmctld is slow during a large reclaim.
  • RejectedNode-local only: Fast and independent. Races new placements and confuses the scheduler's failure handling.
Consequences
What it buys
  • Workloads see the same termination semantics they already handle
  • Drain completes with the scheduler unavailable
  • One implementation for containers and batch jobs
What it costs
  • Two drain paths to test, which the daily drill exercises
  • In the local path the scheduler learns late and may briefly show the node as failed
Choose differently when
If the scheduler's eviction path could be shown to complete within the budget for thousands of nodes during an API-server brownout, the local fallback would be insurance rarely used and could be simplified.
Why it holds up over time
Any future scheduler will have its own eviction semantics and its own availability. Owning the deadline while delegating the mechanics keeps the agent's contract the same for whichever scheduler comes next, needing only a new adapter.
LessonUse the system that knows the work to stop the work, but never give it the power to miss the deadline.
Shown on views18 19
ADR-15

The node caches only interruption-critical fields, signed, and never lets them expire

Accepted

Where does interruption state live so the fleet survives the orchestrator, and how stale may it be?

Context
The agent has to act alone, so it holds a copy of contract fields that can be stale, and a stale copy is exactly what will be used during an outage. The options are a thin agent with a hard dependency, a fat agent with a full copy, or a middle position. The requirement also asks whether an agent with no control plane should drain more conservatively or exactly as it always does.
Decision
The node caches only the fields the notice path needs: drain budget and signal, checkpoint mode, restart semantics, advisory-drain enablement and scheduler adapter settings. Placement, eligibility, floors, spend and fallback fields are never cached, so a stale cache cannot affect them. The cache is a signed bundle with no expiry; staleness is reported as a metric, not enforced. With no control plane, the agent drains exactly as it always does.
How it works on premises
Bundle on local disk under /var/lib/sco, verified against the bundle-signing public key embedded in the image, refreshed every 60 seconds from the contract distributor. Initial copy from cloud-init user-data.
Options weighed
  • ChosenInterruption-critical fields only, no expiry: The fields that change rarely and must never be missing are always present.
  • RejectedThin agent asking the control plane on notice: Always fresh. A central dependency inside the window.
  • RejectedFull contract copy with expiry: Consistent with the centre. An expired cache during a long outage leaves the node unable to drain.
  • RejectedDrain more conservatively when disconnected: Cautious-sounding. Changes behaviour in exactly the situation nobody has tested.
Consequences
What it buys
  • A 12-hour control-plane outage does not change a single drain
  • Stale fields are the ones reviewed and changed least often
What it costs
  • A shortened drain budget reaches old nodes up to 60 seconds late, or much later during an outage
  • Bundle signing keys must be rotated with image rebuilds in mind
Choose differently when
If contracts changed drain behaviour often, for example daily, bounded staleness with a conservative default would be worth its complexity.
Why it holds up over time
Which fields a disconnected node needs is determined by the notice path, which is stable. Keeping the list short keeps the reasoning about staleness short, whatever the contract grows to include.
LessonCache what you need to act, not everything you know. What is not cached cannot be stale.
Shown on views10 12 23
ADR-16

Proactive drain on an advisory is enabled per contract, and its false positives are charged to that contract

Accepted · Phase 2

Should the platform drain on an early advisory, or only on the hard notice?

Context
The advisory arrives a median of six minutes before a reclaim when it arrives at all, and it is absent for 35% of reclaims. Acting on it gives long-draining workloads the time they need, but the advisory is a probability: draining on it interrupts work that would have survived, and at a high false-positive rate the platform becomes the main source of interruption in its own fleet.
Decision
Hard notice only in the MVP; contracts with drain budgets above 115 seconds are rejected. In Phase 2, advisory drain is an opt-in contract field. The platform measures advisory precision per pool, drains opted-in workloads when an advisory arrives, and charges the compute interrupted by advisories that were not followed by a reclaim to that workload's ledger as a false-positive cost.
How it works on premises
Advisories arrive on the same NATS relay as notices, on a separate subject. Precision is computed in ClickHouse from advisory and reclaim events per pool over 30 days and published with each ranking.
Options weighed
  • ChosenPer-contract opt-in with measured false-positive cost: Only workloads that need the time pay for the imprecision.
  • RejectedPlatform-wide advisory drain: More time for everyone. Interrupts work that did not need it, fleet-wide.
  • RejectedNever use advisories: Honest and cheap. Excludes every workload that needs more than two minutes to drain.
Consequences
What it buys
  • Long-draining workloads become eligible
  • The cost of a noisy signal lands on whoever chose to use it
What it costs
  • More wasted work in pools with poor advisory precision
  • The MVP excludes some workloads it could serve later
Choose differently when
If the provider's advisory proved highly precise, for example driven by reservation start times rather than forecasts, it could become the default for tiers 1 and 2.
Why it holds up over time
Every advisory signal from every provider will be probabilistic. Deciding per contract and pricing the false positives keeps the choice correct as the signal's quality changes.
LessonA probabilistic warning is worth acting on only for those who can pay for being wrong, and they should see that bill.
Shown on views12 29
ADR-17

Notices arrive by stream with a metadata poll behind them, and the no-notice case is covered by leases, not by the agent

Accepted · proof required

How does a notice reach a node inside a second, and what keeps work safe when no notice arrives at all?

Context
The drain budget leaves two seconds from notice to signal. OpenStack's instance metadata service caches responses for 15 seconds by default and polling it every second from 5,000 instances puts heavy load on the Nova API. The requirement also assumes 2% of reclaims arrive with no notice at all, and requires every guarantee to stay safe in that case.
Decision
The provider publishes notices on a per-hall NATS relay, on a subject per instance, to which the agent keeps a subscription. The agent also polls instance metadata every five seconds as a slower second channel. The no-notice case is not handled by the agent: work items are held on leases that expire, periodic checkpoints bound lost work, schedulers requeue units on node loss, and the lifecycle event records the termination as no-notice with its wasted work.
How it works on premises
NATS clusters per hall run by the private cloud team, with per-instance credentials issued at launch. Kubernetes Jobs use a pod failure policy that treats disruption as retryable; Slurm jobs are submitted requeueable; queue consumers use the queue's own visibility timeout.
Options weighed
  • ChosenStream plus poll, leases for no-notice: Sub-second in the normal case, degraded but working without the stream, safe without either.
  • RejectedMetadata polling only: No new provider component. The cache interval alone breaks the two-second budget.
  • DeferredHypervisor-to-guest channel: Independent of the network. More provider work; the named fallback if the relay fails the proof.
Consequences
What it buys
  • Notice delivery does not depend on one channel
  • The no-notice case uses mechanisms that already exist in every scheduler
What it costs
  • The relay is a provider component that must be proven at fleet scale
  • No-notice losses are only as small as the checkpoint interval makes them
Choose differently when
A provider with a local, push-style notice path per instance, as public clouds offer through their metadata services, would remove the need for a separate relay.
Why it holds up over time
Treating the no-notice case as the baseline and the notice as an optimisation means any failure of the delivery channel degrades to a known, measured state instead of an unhandled one.
LessonDesign for the notice that never arrives. Then every notice that does arrive is a bonus, not a dependency.
Shown on views10 18 32

Work preservationWho checkpoints, where checkpoints live and who can read them, how often to take them, and how a resumed unit avoids doing anything twice.

ADR-18

Checkpoints are application-level through a platform client, with frameworks implemented once

Accepted

Is checkpointing generic and platform-provided, or implemented by each application?

Context
A platform checkpoint, a process or memory snapshot taken without the workload's cooperation, onboards workloads that will never be modified, at the cost of large images, writes that may not fit the window, and restore fidelity that is hard to guarantee, especially with GPUs, open sockets and in-flight network state. An application checkpoint is small, fast and semantically correct, but asks each workload to implement it. Most interruptible work in this estate runs on a handful of frameworks the platform can reach: Spark, PyTorch and Slurm-managed jobs.
Decision
Checkpoints are application-level. The platform provides a client library that handles the notice callback, multipart upload, encryption, manifest commit and restore lookup, and integrates the frameworks once: Spark executor decommissioning with fallback storage, PyTorch Distributed Checkpoint with torchrun elastic restart, and Slurm requeue with a signal. Generic process checkpointing with CRIU is deferred to Phase 3, for workloads whose state is small and CPU-only.
How it works on premises
Client libraries in Go, Python and JVM published to the estate's package registries. Framework integrations shipped as base images and Helm chart defaults so a Spark or PyTorch workload gets them by using the platform's image.
Options weighed
  • ChosenApplication checkpoints with a client and framework integrations: Small, correct and fast; most workloads get it without code changes through their framework.
  • DeferredGeneric process snapshots for everything: No workload changes. Large, slow, fragile with GPUs and network state; Phase 3 for narrow cases.
  • RejectedEach team implements its own: No platform code. Five incompatible formats and no shared notice handling.
Consequences
What it buys
  • Checkpoints fit the drain budget
  • Restore correctness is the workload's own logic, testable by its owner
  • Framework users need no code
What it costs
  • Bespoke applications must call the client
  • The platform owns three client libraries and three integrations
Choose differently when
If most interruptible work were unmodifiable third-party binaries with small in-memory state, generic snapshots would become the main path rather than the exception.
Why it holds up over time
Frameworks already expose checkpoint hooks because failure is normal in distributed computing. Building on those hooks, rather than on process internals, keeps checkpoints working across kernel, runtime and accelerator changes.
LessonThe cheapest checkpoint is the one written by the code that knows which state matters.
Shown on views19 20 07
ADR-19

Checkpoints live in a Ceph RGW bucket per workload, encrypted with the workload's key and unreadable by the platform

Accepted · proof required

Where are checkpoints stored, who can read them, and how is storage kept from growing with the interruption rate?

Context
The requirement asks for checkpoints on durable storage independent of the instance, in the workload's own storage boundary, encrypted with its key, unreadable by the orchestrator, isolated so one workload's restore cannot read another's state, bounded to three generations or seven days, and never on instance-local storage unless declared disposable. The worst load is not steady state but a correlated reclaim, when hundreds of final writes land at once.
Decision
Each workload has its own RGW bucket, with a bucket policy granting access to the workload's identity only and SSE-KMS using a per-workload key. Generations are written as parts plus a manifest written last; a lifecycle rule keeps the newest three manifests and expires anything older than seven days, including orphaned parts. The orchestrator's identity has no read grant and sees usage statistics only. Each region's RGW is sized for 20 GB/s of checkpoint ingest, twice the planned burst of an 800-node reclaim.
How it works on premises
Ceph RGW in each region with SSE-KMS backed by OpenBao transit, workload access through STS AssumeRoleWithWebIdentity using the workload's SPIFFE JWT. Gateways on dedicated nodes with 100 GbE, placed in the same halls as the reclaimable pools they serve.
Options weighed
  • ChosenRGW bucket per workload with its own key: Isolation, encryption and retention enforced by the store, not by the client.
  • RejectedCephFS shared file system: POSIX semantics. Weak per-workload isolation and metadata server load under bursts.
  • RejectedNode-local NVMe with replication: Fastest write. Dies with the node, which is the event being protected against.
Consequences
What it buys
  • A compromised orchestrator cannot read any workload's state
  • Storage growth is bounded by generation count, not by interruption rate
What it costs
  • Key management per workload
  • RGW capacity is sized for rare bursts and idle most of the time
Choose differently when
Workloads with very large state, tens of gigabytes, cannot write a final checkpoint in 45 seconds on any store and should rely on periodic checkpoints and a lower tier, or a parallel file system near the compute.
Why it holds up over time
S3-compatible object storage, bucket policies and lifecycle rules are supported by every object store the estate is likely to adopt. The ownership rule, the workload's data in the workload's boundary, stays true whoever runs the storage.
LessonThe platform that moves your work around should not be able to read it.
Shown on views13 20 21
ADR-20

The periodic checkpoint interval is set as a loss budget, not a habit

Accepted

How often should work be checkpointed when most reclaims come with notice?

Context
The requirement forbids relying on the final write alone and caps wasted work at 3% of reclaimable hours. With notice, most reclaims end in a final checkpoint; only no-notice terminations, overruns and failed final writes lose work since the last periodic checkpoint. Checkpointing too often wastes compute and storage; too rarely makes the rare loss large.
Decision
The expected unprotected loss rate is computed from the assumptions: 4% reclaimed an hour × 3.5% of reclaims without a successful final write ≈ 0.14% an hour, an effective mean time between unprotected losses of about 714 hours. With a 45-second checkpoint, the Young–Daly interval √(2 × C × MTBF) is about four hours. The default interval is capped at 60 minutes, because the no-notice share is an assumption and one lost hour is the largest loss owners said they would accept. For requeue workloads the compiler enforces rate × unit length ÷ 2 ≤ 3%.
How it works on premises
Interval and unit-length rules live in the compiler. The ledger measures actual wasted work per workload and per cause, and the tier defaults are revisited quarterly against it.
Options weighed
  • ChosenLoss-budget interval with a 60-minute cap: Grounded in arithmetic, bounded by what owners accept.
  • RejectedFixed 10-minute checkpoints: Feels safe. Spends several percent of compute writing state that notice already protects.
  • RejectedFinal write on notice only: Cheapest. Every no-notice termination loses the whole run.
Consequences
What it buys
  • Checkpoint overhead stays well under 1% of compute
  • The interval changes when measured rates change, not when someone worries
What it costs
  • A no-notice loss costs up to an hour
  • Relies on the no-notice assumption being revisited against data
Choose differently when
If no-notice terminations turned out to be 10% of reclaims, the effective MTBF falls to about 250 hours and the interval should drop towards 30 minutes.
Why it holds up over time
The formula is independent of any product and has held since the 1970s. Updating its inputs from the ledger keeps the interval right as hardware, providers and failure rates change.
LessonCheckpoint frequency is a budget decision. Write down the budget and the frequency follows.
Shown on views12 20 28
ADR-21

Restart semantics are declared, effects are published only on completion, and poison units are quarantined

Accepted

How does a resumed or requeued unit avoid duplicating externally visible effects, and how does the platform stop paying for a unit that never finishes?

Context
Losing capacity may slow a workload down but must never produce a partial or duplicated result. A unit that writes output, sends a message or updates a record before it is reclaimed can do so again after it resumes. Separately, a unit that consistently needs longer than the time between interruptions is re-executed on ever-shorter leases indefinitely.
Decision
Every contract declares restart semantics: idempotent, output-commit or not restartable (not admitted to reclaimable capacity). For output-commit, the client writes results to a staging prefix keyed by unit and attempt, and publishes them atomically only when the unit completes. Each unit carries an attempt counter; after four attempts ended by reclaim it is moved to a quarantine queue and its owner notified, in Phase 2.
How it works on premises
Staging prefixes in the workload's own RGW bucket, with publish as a copy to the final key followed by a completion marker. Spark and PyTorch integrations use their frameworks' commit protocols. Attempt counters on lifecycle events, quarantine as a Kafka topic per partition.
Options weighed
  • ChosenDeclared semantics, output commit, quarantine: Duplicate effects are prevented by construction; waste on hopeless units is capped.
  • RejectedAssume all workloads are idempotent: No work for owners. The assumption fails silently in exactly the cases that matter.
  • RejectedRetry indefinitely: Eventually succeeds on a quiet day. Pays for failures in the meantime without limit.
Consequences
What it buys
  • No duplicated result from a reclaim
  • A pathological unit costs four attempts, not a month
What it costs
  • Output commit adds a copy per unit
  • Quarantine needs an owner to look at it
Choose differently when
Workloads whose outputs go to an idempotent store with natural keys can declare idempotent and skip the staging step.
Why it holds up over time
Exactly-once effects through staged output and atomic publish is how every reliable batch system has worked. The rule outlasts any change of storage or framework.
LessonA retry is only safe if doing the work twice has been made harmless.
Shown on views20 32 12

Floor and fallbackHow much guaranteed capacity is held, who may spend beyond it, and how fallback and return avoid storms and oscillation.

ADR-22

The reliability floor is bought, runs real work, and is drawn from committed capacity first

Accepted

Is the reliability floor bought, or achieved by diversification?

Context
A pre-provisioned floor of guaranteed capacity is simple and always there, and is paid for whether used or not. Diversification can achieve a similar effective floor for less, until a correlated event shows the pools were not independent. A third option uses capacity commitments consumed by baseline work in normal times and reallocated under stress. The requirement sets at least 30% of each tier-1 workload's capacity on non-interruptible capacity at all times.
Decision
The floor is bought, but it is not idle. Each tier-1 and tier-4 workload always runs its floor share of units on guaranteed capacity, drawn first from its business unit's committed Blazar reservations, already paid for, and only then from on-demand guaranteed capacity. The floor is spread across at least three halls with no hall above 40%. Diversification is not trusted to substitute for any part of the floor until learned correlation groups have a year of evidence.
How it works on premises
The floor keeper in the controller maintains floor instances in a separate orchestrator-owned project, sized in normalised units, and rebuilds lost floor within 15 minutes of a hall loss from committed capacity in other halls.
Options weighed
  • ChosenBought floor that runs work, committed capacity first: A total loss of reclaimable capacity slows tier 1 to 30% speed and never stops it.
  • RejectedFloor by diversification alone: Cheapest. Correct only while correlation is understood, which it is not yet.
  • RejectedIdle standby floor: Instantly available. Pays for capacity that does nothing most of the time.
Consequences
What it buys
  • Floor capacity produces output every hour it is paid for
  • Correlated reclaims cannot take a deadline workload to zero
What it costs
  • About €4.3k a month per 10 points of tier-1 floor at planning rates
  • Up to 15 minutes below floor after a hall loss
Choose differently when
Once learned correlation groups have enough history to bound the worst correlated week with confidence, some of the floor can be replaced by stricter diversification caps, and the replay in view 06 is the evidence.
Why it holds up over time
Whatever the capacity market, a guaranteed floor is the one reliability mechanism that does not depend on an estimate being right. Making it do useful work keeps it economically defensible as rates change.
LessonInsurance that also does work is much easier to keep paying for.
Shown on views06 17 25
ADR-23

Fallback restores the floor without asking, spends automatically within a declared cap, and escalates beyond it

Accepted

Is fallback to guaranteed capacity automatic, budget-gated or human-approved?

Context
Automatic fallback protects deadlines and turns capacity loss into cost, which is the trade the platform exists to make, but an unbounded automatic fallback during a broad shortage can multiply the compute bill in an hour when nobody is watching. A budget gate bounds the damage but turns a cost incident into an availability incident when it closes. Human approval alone is safe and too slow. The requirement asks for fallback gated by a spend authority declared in the contract, with escalation beyond it.
Decision
Three steps. Restoring a workload's floor is pre-authorised by its tier and draws on committed capacity, so it is automatic. Fallback above the floor, triggered by an interruption-rate breach, shortfall of five minutes or deadline risk, is automatic up to the contract's 24-hour spend cap, reserved in the ledger before launch. Beyond the cap, the arbiter pages the budget owner named in the contract with priced options; without an answer in 15 minutes it records shortfall and spends nothing more.
How it works on premises
The arbiter in the controller, the spend ledger in PostgreSQL, approval requests through the incident platform, approval by OIDC with WebAuthn, every step to the audit log service.
Options weighed
  • ChosenFloor automatic, capped automatic fallback, escalation: Protects deadlines inside a known worst-case bill; partial fallback is the coherent middle state.
  • RejectedUnbounded automatic fallback: Maximum availability. Worst case is an unbounded bill during an estate-wide shortage.
  • RejectedHuman approval for every fallback: Maximum control. Too slow at 02:00 for a 06:00 deadline.
Consequences
What it buys
  • The worst plausible fallback cost is the sum of declared caps
  • A shortage beyond authority is visible as shortfall, not hidden as spend
What it costs
  • A deadline can be missed because a cap was too low
  • Budget owners must be reachable, or accept that shortfall
Choose differently when
Workloads whose deadline miss costs far more than any compute bill, such as regulatory filings, should carry a high cap and a pre-authorised approver so escalation never gates them.
Why it holds up over time
Spending authority, a pre-authorised baseline and escalation beyond a limit are how organisations have controlled spending for a long time. Encoding them in contracts keeps them stable across changes of price, provider or team.
LessonAutomation should be allowed to spend money, up to the amount a named person agreed it could.
Shown on views05 22 31
ADR-24

Fallback is staggered by priority and return waits for sustained health

Accepted

How are fallback storms and oscillation between purchase models prevented?

Context
When many workloads fall back at once, they all reach for the same guaranteed capacity that guaranteed tenants are also claiming. When capacity recovers, returning immediately can pull the fleet back into a pool that is about to be reclaimed again, producing an oscillation that costs both money and interruptions.
Decision
Fallback launches are limited to 40 instances a minute per region, ordered by deadline risk and then tier. Return starts only when a workload's eligible pools have been healthy for 60 minutes, below half its declared interruption rate, and then moves 20% of fallback capacity an hour, newest fallback first.
How it works on premises
A priority queue in the arbiter with a token bucket shared with the acquirer's regional budget; return controller state in PostgreSQL.
Options weighed
  • ChosenStaggered fallback and hysteretic return: No storm, no oscillation; the most urgent work goes first.
  • RejectedImmediate fallback and return: Fastest reaction. Storms on the way out, oscillation on the way back.
  • RejectedManual return: Careful. Fallback spend continues long after it is needed.
Consequences
What it buys
  • Guaranteed tenants are not crowded out by a fallback wave
  • The fleet does not flap between purchase models
What it costs
  • Lower-priority workloads wait for fallback
  • Some fallback spend continues for an hour or more after recovery
Choose differently when
If guaranteed capacity were plentiful and fallback rare, the stagger could be relaxed; the return hysteresis should stay.
Why it holds up over time
Rate limiting and hysteresis are control-theory basics that apply to any system switching between two states under noisy signals.
LessonLeave in order, come back slowly.
Shown on views22 21 32

Evidence and moneyHow every capacity event is recorded, how the saving is calculated honestly, who sees it, and who is woken when something goes wrong.

ADR-25

Lifecycle events ride the estate's Kafka and ClickHouse; capacity actions are audited separately

Accepted

How is every capacity event recorded durably at low cost, and why is the audit record kept apart from it?

Context
The requirement asks that every placement, reclamation, drain, checkpoint, fallback and replacement be a durable event retained 400 days, that cost attribution be kept seven years, and that every capacity action be recorded with its causing identity in an audit record independent of the telemetry pipeline. It also caps the orchestrator's own cost at 2% of the saving.
Decision
Agents spool events locally and Vector ships them to a Kafka topic keyed by instance; controllers write through a transactional outbox. ClickHouse keeps events 400 days and cost rollups seven years, and exports cost attribution monthly as Parquet to Ceph with Object Lock. Capacity actions (launch, terminate, fallback, approval) are written from the same outbox, separately, to the estate's audit log service.
How it works on premises
The estate's shared Kafka and ClickHouse clusters with dedicated topics, databases and quotas. Vector on each node. Ceph RGW Object Lock in compliance mode for the finance archive.
Options weighed
  • ChosenShared Kafka and ClickHouse, separate audit path: Durable, cheap at this volume, with an audit record the platform cannot edit.
  • RejectedDedicated clusters: Isolation. At 30,000 events on the worst day, their cost alone would breach the 2% ceiling.
  • RejectedAudit in the telemetry pipeline: One path. The record of spend and termination shares failures and administrators with the thing it records.
Consequences
What it buys
  • Any instance's life can be reconstructed for 400 days
  • Finance retention is outside the query store
What it costs
  • Dependence on shared clusters' availability, mitigated by node spools
  • Events from nodes deleted during long outages can be lost; the provider's deletion event records the loss
Choose differently when
An estate without shared streaming and analytics platforms would be better served by PostgreSQL with partitioned tables at this volume.
Why it holds up over time
Events with causes are the most portable form of history. The storage underneath can change; the events and the reports built from them remain.
LessonKeep the record of what the automation did somewhere the automation cannot change.
Shown on views15 13 31
ADR-26

The saving is computed net against a guaranteed-rate counterfactual, with every cost attributed

Accepted

How is the saving calculated so that it is true, and who pays for each cost of the discount?

Context
The gross discount, the difference between two rate cards times hours, overstates the saving. Redone work, warm headroom, fallback spend, checkpoint storage and the orchestrator's own footprint are all costs of running on reclaimable capacity. On hardware the organisation owns, there is a further subtlety: a rate-card saving becomes money only if the organisation buys less guaranteed capacity as a result.
Decision
Net saving per workload equals the useful work delivered, valued at the guaranteed rate, minus all spend: reclaimable hours, fallback hours, warm headroom, checkpoint storage and a pro-rata share of the orchestrator. Wasted work is removed from useful work rather than added as a cost, so it is counted once. Losses caused by workload defects are reported to that workload and not charged to reclaim. The planning figure is €199k a month net, 59%, with the orchestrator at 1.6% of the saving. The ledger labels the saving as rate-card and publishes reclaimable hours to capacity planning.
How it works on premises
Hourly attribution jobs in ClickHouse joining lifecycle events, instance hours, rate cards, RGW usage and the orchestrator's own resource usage; Grafana reports per workload; monthly export to finance.
Options weighed
  • ChosenNet against a guaranteed-rate counterfactual: The number an owner or a CFO can defend.
  • RejectedGross discount: Easy and flattering. Hides the costs that decide whether a workload belongs on reclaimable capacity.
  • DeferredMeasured against actual hardware spend: The real money. Only knowable over procurement cycles; the ledger feeds it rather than replacing it.
Consequences
What it buys
  • Workloads that do not benefit are identified and recommended off
  • The orchestrator's own cost is visible against what it produces
What it costs
  • Attribution logic to maintain
  • Numbers that are less flattering than a vendor slide
Choose differently when
Where capacity is bought per hour from a public cloud, the rate-card saving is already money, and the procurement caveat disappears.
Why it holds up over time
Counterfactual minus all attributable cost is the definition of a saving, and it does not change with rates, tiers or providers. New costs join the ledger as new lines.
LessonA saving that ignores what it cost to obtain is a price comparison, not a saving.
Shown on views28 15 06
ADR-27

Showback per workload now; chargeback priced by the declared contract later

Accepted

Who holds the savings, and who holds the risk?

Context
If the workload owner sees the discount, they are motivated to over-declare their tolerance and under-declare their drain budget. If the platform holds the savings and charges a flat internal rate, the incentive to move eligible work onto reclaimable capacity disappears. The requirement asks whether declarations can be validated cheaply enough to make over-declaration self-correcting, and whether a charging model can reward accurate declaration.
Decision
The MVP shows each owner their workload's net saving, including the fallback spend and wasted work their contract caused, and alarms when measured interruption exceeds what they declared. Phase 3 introduces chargeback in which the reclaimable rate paid depends on the declared tier and tolerance: declaring a higher tolerance buys a lower rate but carries the fallback and waste costs of being wrong, so accurate declaration is the cheapest option.
How it works on premises
Showback reports in Grafana from the ClickHouse ledger. Chargeback as a monthly export to the finance system with contract-tier rate multipliers agreed with finance.
Options weighed
  • ChosenShowback now, contract-priced chargeback later: Owners see the trade immediately; incentives follow once the evidence is trusted.
  • RejectedFlat internal rate, savings held centrally: No gaming. No reason for anyone to move onto reclaimable capacity.
  • RejectedPass the full discount to owners from day one: Strong adoption. Rewards optimistic declarations before breaches can be priced.
Consequences
What it buys
  • Owners see both sides of their choice
  • Over-declaration becomes visible and later expensive
What it costs
  • Chargeback needs finance agreement and a year of trusted data
  • Showback alone relies on owners caring
Choose differently when
In an organisation without internal chargeback at all, showback with executive targets for the eligible population may be the whole model.
Why it holds up over time
Tying price to declared risk is how insurance has always been priced. It stays correct whatever the actual rates become.
LessonIf declaring the truth is not the cheapest option, the declarations will drift away from the truth.
Shown on views12 28 29
ADR-28

Metrics on Prometheus and Thanos, with alarms routed to whoever can fix the cause

Accepted

What is measured, and who is woken for which failures?

Context
The requirement asks for interruption rate, time between interruptions and drain overruns per workload and pool; shortfall reported apart from interruption; alarms when realised saving falls below a threshold or the orchestrator's own cost exceeds its ceiling. An alarm sent to the wrong person is noise: a workload owner cannot fix a bad pool, and the platform cannot fix a workload that over-declared.
Decision
Metrics in Prometheus per region with Thanos for 13-month queries; reports in Grafana over Prometheus and ClickHouse; alerts through Alertmanager to the incident platform. Workload owners are alerted only on breaches of their own contract. The platform team is paged for pool behaviour, drain overrun rates, tier-1 shortfall beyond 15 minutes, control-plane health, bundle age and the orchestrator's cost ratio.
How it works on premises
The estate's Prometheus and Thanos stack with dedicated recording rules and retention; Alertmanager routes by label: contract owner or platform.
Options weighed
  • ChosenEstate Prometheus and Thanos, routed by owner of the cause: Standard tools, fewer pages, each to someone who can act.
  • RejectedAlert every workload owner on every reclaim: Transparent. Teaches owners to ignore alerts within a week.
  • RejectedDedicated metrics stack: Isolated. Duplicates estate tooling for no gain at this scale.
Consequences
What it buys
  • Owners hear only about their own promises
  • Platform problems page the platform
What it costs
  • Routing rules must follow contract ownership changes
  • Shared metrics stack availability matters
Choose differently when
A very small estate where the platform team also owns every workload can merge the two routes.
Why it holds up over time
Routing by who can fix the cause survives any change of tooling or team structure.
LessonAn alert is a request for action. Send it to someone who can take the action.
Shown on views27 23

Security and authorityWho holds the power to acquire, terminate and spend, what a compromised node or workload can reach, and what happens to a reclaimed node's credentials.

ADR-29

Capacity authority is scoped by OpenStack project, credentials are short-lived, and nodes hold none

Accepted · proof required

Who can launch and terminate reclaimable capacity, and how is a defect or compromise kept from reaching unrelated capacity?

Context
The requirement asks that capacity permissions sit under an identity distinct from any workload, that no workload can terminate or acquire capacity, and that termination authority be scoped to instances the orchestrator launched and tagged. Tag-based conditions in access policy are fragile: tags are mutable, and anyone who can edit them can move an instance into or out of scope.
Decision
Reclaimable and fallback instances live in dedicated OpenStack projects per region and partition, in which only the orchestrator's identity has a role. The orchestrator obtains a project-scoped application credential from OpenBao for one hour at a time. Before any termination the controller also checks that the instance's tags and its database row agree. Node agents and workloads hold no provider credential of any kind.
How it works on premises
Keystone projects and roles; OpenStack application credentials issued by an OpenBao plugin or rotation job; OpenBao policies bound to the controller's SPIFFE identity.
Options weighed
  • ChosenProject-scoped authority, short-lived credentials: The boundary is structural; nothing outside the projects is reachable.
  • RejectedTag-based policy conditions in a shared project: Flexible. Tags can be edited, and oslo.policy does not express resource-tag conditions cleanly.
  • RejectedLong-lived service user: Simple. A leaked password stays useful indefinitely.
Consequences
What it buys
  • A compromised controller can only affect its own projects
  • Workloads cannot acquire or terminate capacity by any route
What it costs
  • More projects and quotas to manage
  • Credential issuance becomes a dependency of acquisition, not of drain
Choose differently when
A provider with robust, immutable resource ownership in its access model could scope by ownership rather than by project.
Why it holds up over time
Structural isolation holds regardless of how policies are written in future provider releases. The rule, only the orchestrator can act in its projects, is simple to audit.
LessonScope power by where things live, not by labels someone else can edit.
Shown on views30 09 10
ADR-30

Floors, spend caps and fallback policy need a second, higher authority

Accepted

Who may change the settings that determine how much the organisation spends on reliability?

Context
The requirement asks that changes to the guaranteed floor, the spend cap and fallback policy need a separate, higher authority than routine contract edits, and that moving a workload from reclaimable to guaranteed capacity be treated as a spend-authorising action subject to audit.
Decision
Routine contract fields need the workload owner's and platform team's review. Changes to floors, spend caps, approvers and fallback policy additionally need a signature from the capacity governance group, and the distributor refuses a bundle whose governed fields changed without it. Approvals of spend beyond a cap require a named approver authenticating with a phishing-resistant factor. Operators of the orchestrator cannot approve their own spend.
How it works on premises
Forgejo CODEOWNERS and branch protection for governed paths; a second cosign signature by a key held for the governance group in OpenBao; OIDC with WebAuthn from the estate identity provider for approvals.
Options weighed
  • ChosenTwo-tier authority enforced at distribution: A merged change without the governance signature never reaches the fleet.
  • RejectedReview rules in Git only: Familiar. An administrator can override branch protection; the fleet would accept the result.
  • RejectedAll changes by governance: Strict. Governance becomes the bottleneck for every drain budget tweak.
Consequences
What it buys
  • Money decisions have a named, separate owner
  • Enforcement does not rely on repository settings alone
What it costs
  • Slower floor and cap changes
  • A second signing key to protect and rotate
Choose differently when
In a small organisation where the platform lead also owns the budget, a single signature with audit may be proportionate.
Why it holds up over time
Separation between those who operate a system and those who authorise its spending is an organisational control that outlives any tooling.
LessonThe people who can change a system's behaviour should not be the only ones who can change what it costs.
Shown on views06 26 31
ADR-31

A terminated instance's identity and credentials are revoked, not left to expire

Accepted

What happens to the credentials a reclaimed node held?

Context
The requirement asks that a reclaimed instance's credentials and cached secrets be invalidated on termination rather than relying on the instance disappearing. At 4% reclaimed an hour, thousands of nodes a day end with live identity documents and leases that would otherwise stay valid until expiry. A node that is reclaimed but not fully deleted, or whose disk image is retained, is the case that matters.
Decision
On a termination event the controller bans the node's SPIRE agent, so no new SVIDs are issued to it, and revokes OpenBao leases issued to that node's identities by prefix. Node-bound Kubernetes service-account tokens become invalid when the node object is deleted. The agent also revokes its own tokens on a best-effort basis before T+120.
How it works on premises
SPIRE server agent ban API; OpenBao lease revocation by prefix keyed on node identity; Kubernetes node deletion by the cluster's node lifecycle controller.
Options weighed
  • ChosenRevoke on termination event: Credentials end within seconds of the node, whatever happens to its disk.
  • RejectedRely on short credential lifetimes: No extra calls. Up to an hour of valid identity per reclaimed node.
  • RejectedAgent-only self-revocation: Local. Does not happen in the no-notice case.
Consequences
What it buys
  • No credential outlives its node by more than seconds
  • The no-notice case is covered centrally
What it costs
  • Revocation load on SPIRE and OpenBao during large reclaims
  • Revocation is best-effort if those services are down, falling back to expiry
Choose differently when
If credentials were already bound to a hardware key that dies with the node, central revocation would be less important.
Why it holds up over time
Revoking identity when the thing it identifies ends is correct under any identity system.
LessonAn identity should end when the thing it names ends, not when a timer eventually notices.
Shown on views30 18

OperationsHow the platform changes without becoming the cause of the interruptions it manages, and how its promises are tested on an ordinary day.

ADR-32

Releases ride the fleet's own churn: canary partitions for contracts, new launches for agents

Accepted

How do contract changes and platform code reach thousands of nodes without the rollout becoming a source of interruptions?

Context
Upgrading an agent in place on 5,000 nodes means restarting the component that must not be absent when a notice arrives, and draining nodes to upgrade them would add interruptions to a fleet whose whole point is to handle fewer. But reclaimable fleets turn over constantly by design.
Decision
Contract bundles go to one partition for 24 hours, gated on overrun rate, wasted work and breach alarms, then to all. Controller releases canary on one partition. The agent is never upgraded in place except for urgent security fixes on nodes not draining: a new image is used for 5% of launches, then all, and the fleet turns over through reclamation and scale-down, reaching about 95% within 72 hours at the baseline reclaim rate.
How it works on premises
Forgejo Actions for build and signing; diskimage-builder for images, published to Glance; Argo CD for controllers; image selection per pool in the acquirer.
Options weighed
  • ChosenCanary partitions and churn-driven agent rollout: No node drained for an upgrade; rollback by launching the previous image.
  • RejectedIn-place rolling upgrade of agents: Fast. Restarts the notice handler across the fleet.
  • RejectedBlue-green node pools: Clean cut-over. Doubles capacity during rollout.
Consequences
What it buys
  • Rollouts add no interruptions
  • Rollback is choosing the previous image
What it costs
  • Agent versions coexist for days, so the bundle schema must be backward compatible for two versions
  • Rollout speed depends on reclaim rate
Choose differently when
If the fleet were long-lived, with reclaims rare, image-based rollout would be too slow and in-place upgrades with drain-aware ordering would be needed.
Why it holds up over time
Using the system's natural churn as the deployment mechanism works for any ephemeral fleet, and it keeps the version skew rules explicit.
LessonIf your fleet replaces itself every few days anyway, let that be the rollout.
Shown on views26 25
ADR-33

Reclamation is exercised daily in production through the provider's own reclaim path

Accepted

How does the platform know its interruption path still works on a day when nothing is going wrong?

Context
The requirement asks for regular deliberate reclamation in production, because an interruption path tested only by real interruptions is tested least when the fleet is healthiest. It places scheduled exercises in Phase 2. Fail-static behaviour, which must be verified by exercise in the MVP, cannot be verified without them.
Decision
An exercise job reclaims 1% of reclaimable nodes per partition each working day through the provider's reclaim API, using the same notice, relay, agent and drain path as a real reclaim, labelled as a drill in lifecycle events. A quarterly game day reclaims a hall's reclaimable capacity with the control plane stopped. This moves into the MVP.
How it works on premises
A CronJob in the management cluster calling the reclaimer's API with the private cloud team's agreement; drill events excluded from pool scoring but included in overrun and drain metrics.
Options weighed
  • ChosenDaily 1% drill through the real path, quarterly game day: Tests exactly what production uses, at a small, known cost.
  • RejectedPod-kill chaos testing: Easy. Skips the notice, relay and agent, which are the parts most likely to break.
  • RejectedRely on natural reclaims: Free. Quiet weeks test nothing; bad weeks test everything at once.
Consequences
What it buys
  • Regressions in the notice path show up within a day
  • Fail static is proven, not assumed
What it costs
  • About 1% extra churn and its wasted work
  • Needs the provider team's standing agreement
Choose differently when
A fleet with very frequent natural reclaims in every pool might get enough exercise without drills, though not of the control-plane-down case.
Why it holds up over time
Exercising failure paths on a schedule is independent of any tool and remains the only way to keep them working through changes in code and staff.
LessonA recovery path that runs only during incidents is a recovery path you are testing during incidents.
Shown on views03 29 32

Every package used, in one table

Every open-source package named on the views, what it is, the job it does in this design, and what was considered instead.

PackageWhat it isWhat it does hereConsidered instead
OpenStack Nova Compute service of the OpenStack cloud platform Launches and deletes reclaimable, fallback and floor instances; multi-create for batched acquisition Kubernetes-only harvesting
OpenStack Placement Inventory and allocation service for OpenStack Source of pool capacity and headroom per resource provider and aggregate Static inventory
OpenStack Ironic Bare-metal provisioning for OpenStack Supplies bare-metal pools, including accelerator nodes Metal3
OpenStack Keystone Identity service for OpenStack Projects, roles and application credentials that scope capacity authority Shared service user
OpenStack Blazar Reservation service for OpenStack Committed capacity for floors and the forward-capacity calendar Quota tables
Kubernetes Container orchestrator Schedules services, Spark and CI onto reclaimable nodes; eviction and graceful node shutdown for drain; hosts the management cluster Nomad
Slurm Batch workload manager Schedules batch and training jobs; drain, requeue and signals for interruption HTCondor
Kyverno Kubernetes policy engine Refuses pods targeting reclaimable nodes without a contract OPA Gatekeeper
Apache Spark Distributed data-processing engine Executor decommissioning migrates shuffle and cached blocks before a node is reclaimed Restart failed stages
PyTorch Distributed Checkpoint Sharded checkpointing for PyTorch training Writes and restores training state through the checkpoint client Framework-agnostic snapshots
CRIU Checkpoint and restore of Linux processes Phase 3 generic checkpoint for small CPU-only workloads that cannot be changed Application checkpoints
Forgejo Self-hosted Git forge System of record for contracts, reviews and governance approvals GitLab
CUE Configuration and validation language Contract and tier schemas in the compiler JSON Schema
cosign Signing tool from the Sigstore project Signs contract bundles, with a second governance signature for money fields GPG
NATS Lightweight messaging system Per-hall relay delivering notices and advisories to node agents Instance metadata polling
PostgreSQL Relational database Fleet state, plans, exclusions, spend ledger and transactional outbox etcd
Patroni High-availability manager for PostgreSQL Leader election and failover across three halls Stolon
pgBackRest Backup and restore for PostgreSQL Point-in-time recovery of orchestrator state to Ceph Barman
Apache Kafka Distributed event log Lifecycle event stream, ordered per instance NATS JetStream
Vector Observability data pipeline Ships each node's event spool to Kafka when reachable Fluent Bit
ClickHouse Columnar analytical database Event history, ranking history, cost attribution and savings ledger PostgreSQL partitioned tables
Ceph RGW S3-compatible object gateway for Ceph Per-workload checkpoint buckets with SSE-KMS and lifecycle rules; Object Lock finance archive MinIO
OpenBao Secrets and encryption service, fork of Vault Provider credentials, bundle signing keys, per-workload transit keys HashiCorp Vault
SPIRE SPIFFE workload identity runtime Identities for controllers, agents and workloads; banned per node on termination Static certificates
Prometheus and Thanos Metrics system and its long-term store Platform and workload metrics with 13-month queries VictoriaMetrics
Alertmanager Alert routing for Prometheus Routes contract breaches to owners and platform faults to the SRE rota Built alerting
Grafana Dashboards and reporting Workload savings reports, blast-radius board and fleet health Superset
Argo CD GitOps delivery for Kubernetes Deploys controllers to regional management clusters by canary partition Flux
diskimage-builder OpenStack image building tool Builds node images per SKU family with the agent baked in Packer
Open svg/<view>.svg or drawio/<view>.drawio in draw.io Desktop or at app.diagrams.net to edit. The SVG carries the diagram inside it, so it is both the picture and the source. This folder is self-contained — copy it whole and every link still resolves.