Skip to content

scheduling: hold expiry closes the claim without advancing its revision #1252

Description

@jeremi

Found during review of #1092.

Every other terminal transition advances the revision

close_claim in crates/registry-scheduling/src/store.rs is what release and
cancellation go through:

UPDATE scheduling_claims SET state=$2, reason=$3,
 revision=$4 + 1, closed_at=now(), changed_at=now()
 WHERE claim_id=$1 AND state='active' AND revision=$4

Its callers then write the history event at that incremented revision.

The expiry sweep does not

expire_due_holds runs its own update:

UPDATE scheduling_claims SET state='expired', closed_at=now(), changed_at=now()
 WHERE claim_id IN (...) RETURNING claim_id, revision

revision is absent from the SET list, so the RETURNING value is the revision the
row already had, and the loop below writes the expired history row at that value.

Result

An expired hold has its held event and its expired event at the same revision,
while a released hold has them one apart. Anything reading scheduling_history and
treating the revision as the ordering key, or as the identity of a distinct state,
sees the two events as one state. scheduling_history has no unique constraint on
(claim_id, revision), so nothing catches it at write time.

This is a ledger consistency wart rather than a capacity fault: capacity is already
correct because the snapshot query discounts expired holds by hold_expires_at
without waiting for the sweep, as the comment on expire_due_holds says.

Fix

Add revision=revision + 1 to the update and let RETURNING revision carry the new
value into the history insert, so the sweeper matches the lifecycle every other
closing writer follows.

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

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions