feat: abandon a staging that cannot finish - #85
Merged
Merged
Conversation
A staging could reach a state no resume repairs - another operation's barrier on a source table, a changed runtime login, a target that refuses the table, somebody else's object under a copy's name - and then blocked the customer's operator until its state was repaired by hand. Before its decision, LocalCutover.abandon / sde-operator abandon now ends it: the decision abandoned is recorded first, then only this staging's own tables are dropped - the ones under a copy's name that carry its exact creation marker and, once recorded, its native identity, including a table created just before a crash whose identity was never recorded. Anything else under the name is left alone. On ClickHouse the runtime logins' grants are revoked too: measured on 24.8.14.39, they outlive DROP TABLE. The map in force, the watermarks and every process stay as they were, and abandonment needs only the same native database - not the logins or a barrier-free source whose absence is why the staging cannot finish. The receipt gains the outcome abandoned (the map in force, per entity the identity of the dropped table or null). A state holding an abandoned staging is storage contract 4, which operators knowing contracts 1 to 3 refuse; the name-reuse check reads names from the stored authorization. After the decision prepared, abandonment is refused and resume publishes. The in-place index budget test gets a longer signed budget: its resume must fit a DROP and a CREATE INDEX CONCURRENTLY into it, which 2.5 s did not always leave on a loaded two-core machine. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…repares Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A mutation that read reused names from the receipt survived: the test abandoned only a staging whose table had been created, whose receipt names an identity. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A staging that cannot finish can now be abandoned before its decision:
LocalCutover.abandon()/sde-operator abandonremoves only that staging's own copy and keeps the map in force.Why
A staging can reach a state no resume repairs - another operation's barrier on a source table, a changed runtime login, a target that refuses the table, somebody else's object under a copy's name. Until now such a staging blocked the customer's operator (and, through the pending reservation, every issuance in the control plane) until its state was repaired by hand. The in-place index build (#83) shipped with abandonment from the start; this gives staging the same shape.
How
abandonedis recorded first. Then only this staging's own tables are dropped: a table under a copy's name that carries its exact creation marker and, once recorded, its native identity - including a table created just before a crash whose identity was never recorded (found by the marker). Anything else under the name is left alone (a foreign table; a recreated object carrying the marker). Indexes and generation constraints go with the table.DROP TABLEandREVOKEon the dropped table is accepted (docs/qualification/staging-abandon/). PostgreSQL drops a table's privileges with it.abandoned: the map in force, and per entity the identity of the dropped table ornull. The authorization is spent:stagewith the same packet returns the abandonment.42091bf: exit 2,local cutover state is missing or corrupt). The name-reuse check reads names from the stored authorization, because an abandoned receipt may name no identity.prepared,abandonis refused andresumepublishes; the copy then leaves through its cutover's abort.Also: the in-place index budget test gets a 6 s signed budget - its resume must fit a
DROPand aCREATE INDEX CONCURRENTLYinto the same budget, which 2.5 s did not always leave on a loaded two-core machine (observed once in a combined run; passes alone).Evidence
make checkwith both live engines: Python 1956 passed, 10 skipped (the orderbook slice); TypeScript 936 passed.test_staging_abandon_live.py(both engines): abandonment after every checkpoint before the decision, refusal after it, an interrupted abandonment finished byresumeor a secondabandon, a staging blocked by another barrier, a foreign table and a recreated object under the copy's name, a SIGKILL right afterCREATE, the next staging after an abandonment, the CLI.TypeError. Test fixed; rerun: killed.The control-plane side (receipt acceptance, envelope 4, guard, Weather driver) is in the private repository.
🤖 Generated with Claude Code