What
scheduling_supply is a single flat table keyed by supply_id, with a kind
column discriminating pool from window:
CREATE TABLE IF NOT EXISTS scheduling_supply (
supply_id text PRIMARY KEY,
kind text NOT NULL CHECK (kind IN ('pool','window'))
);
Two independent publish paths write it, and nothing refuses an id used by both.
Pool ids come from the policy package; window ids come from the environment
records. The two namespaces are authored separately and there is no check that
they are disjoint.
The two writes are also asymmetric. The pool anchor inserts with
ON CONFLICT(supply_id) DO NOTHING, while the window anchor inserts with no
conflict clause, after the facts replacement deletes every row with
kind='window'.
Two failures
Publish a window whose id matches a live pool. The window anchor insert
hits the primary key and aborts the whole facts-replacement transaction. The
operator gets an opaque failure rather than a named refusal naming the
collision.
Publish a pool whose id matches a live window. ON CONFLICT DO NOTHING
silently skips the anchor, so no pool row is created and the existing row keeps
kind='window'. Commitments still serialize, because lock_supply matches on
supply_id and finds the row. Then the next facts replacement runs
DELETE FROM scheduling_supply WHERE kind='window' and removes the anchor the
pool also depended on. From that point lock_supply returns fewer rows than it
asked for and raises StoreError::Corrupt, so every commitment against that
pool answers 503 until the policy is republished.
The second is the worse one: a silent, self-inflicted, persistent outage on one
pool, with no refusal at authoring time and no signal at publish time.
Where
crates/registry-scheduling/migrations/0001_scheduling.sql, the
scheduling_supply definition
crates/registry-scheduling/src/store.rs, the pool anchor insert, the
DELETE ... WHERE kind='window' and window anchor insert in the facts
replacement, and lock_supply
Why it is filed rather than fixed
It needs an operator id collision to trigger, and #1092 is an MVP with no
adopters. The fix is small but it is a schema and authoring question rather
than a one-line guard, so it wants its own change.
Done when
- Authoring refuses a pool id that collides with a window id, and the reverse,
with a named diagnostic rather than a database error.
- The two anchor writes agree on conflict handling.
StoreError::Corrupt from lock_supply is distinguishable from a genuine
ledger inconsistency, or cannot be reached this way at all.
- A test publishes a colliding id both ways and asserts the refusal.
What
scheduling_supplyis a single flat table keyed bysupply_id, with akindcolumn discriminating
poolfromwindow:Two independent publish paths write it, and nothing refuses an id used by both.
Pool ids come from the policy package; window ids come from the environment
records. The two namespaces are authored separately and there is no check that
they are disjoint.
The two writes are also asymmetric. The pool anchor inserts with
ON CONFLICT(supply_id) DO NOTHING, while the window anchor inserts with noconflict clause, after the facts replacement deletes every row with
kind='window'.Two failures
Publish a window whose id matches a live pool. The window anchor insert
hits the primary key and aborts the whole facts-replacement transaction. The
operator gets an opaque failure rather than a named refusal naming the
collision.
Publish a pool whose id matches a live window.
ON CONFLICT DO NOTHINGsilently skips the anchor, so no pool row is created and the existing row keeps
kind='window'. Commitments still serialize, becauselock_supplymatches onsupply_idand finds the row. Then the next facts replacement runsDELETE FROM scheduling_supply WHERE kind='window'and removes the anchor thepool also depended on. From that point
lock_supplyreturns fewer rows than itasked for and raises
StoreError::Corrupt, so every commitment against thatpool answers 503 until the policy is republished.
The second is the worse one: a silent, self-inflicted, persistent outage on one
pool, with no refusal at authoring time and no signal at publish time.
Where
crates/registry-scheduling/migrations/0001_scheduling.sql, thescheduling_supplydefinitioncrates/registry-scheduling/src/store.rs, the pool anchor insert, theDELETE ... WHERE kind='window'and window anchor insert in the factsreplacement, and
lock_supplyWhy it is filed rather than fixed
It needs an operator id collision to trigger, and #1092 is an MVP with no
adopters. The fix is small but it is a schema and authoring question rather
than a one-line guard, so it wants its own change.
Done when
with a named diagnostic rather than a database error.
StoreError::Corruptfromlock_supplyis distinguishable from a genuineledger inconsistency, or cannot be reached this way at all.