Skip to content

Scheduling: a pool id equal to a window id corrupts the supply anchor table #1242

Description

@jeremi

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions