Repository navigation
sandbox-store: move the node drain's sandbox row writes into the store - #1328
Merged
Merged
Conversation
|
Thanks for opening this pull request! A SPACE maintainer will take a look. CONTRIBUTING.md explains how we review outside contributions. |
ac-pplx
force-pushed
the
ac/lifecycle-move-drain
branch
from
November 2, 2026 20:48
a54232b to
51f4827
Compare
This was referenced Nov 2, 2026
ac-pplx
force-pushed
the
ac/lifecycle-move-drain
branch
from
November 2, 2026 22:23
51f4827 to
0beb4a0
Compare
ac-pplx
force-pushed
the
ac/lifecycle-move-drain
branch
2 times, most recently
from
November 2, 2026 23:25
482258a to
1e3f5d3
Compare
This was referenced Nov 3, 2026
ac-pplx
marked this pull request as ready for review
November 3, 2026 03:51
ac-pplx
marked this pull request as draft
November 3, 2026 03:52
ac-pplx
force-pushed
the
ac/lifecycle-move-drain
branch
from
November 3, 2026 06:00
1e3f5d3 to
2396dcd
Compare
ac-pplx
marked this pull request as ready for review
November 3, 2026 06:29
kumarpallav-pplx
requested review from
kumarpallav-pplx and
nikhilbirmiwal-pplx
November 3, 2026 06:30
nikhilbirmiwal-pplx
approved these changes
Nov 3, 2026
The cluster controller's node drain and drained-node terminator wrote the control-plane `sandboxes` row from inline SQL in their own store files. Sixteen of those statements, the node drain's and its pod-less give-up's, move into a new `drain` module of `pplx-space-sandbox-store`, one function per transition that runs on the executor or transaction its caller passes: - the evacuation suspend claim, its revert, and the suspend commit; - the evacuation restore finalize and the restore retry backoff; - the stopped evacuation claim and its artifact pin; - the stranded-suspend redrive; - the give-ups: mark lost (artifact missing and gone node), mark failed on node, fail residents on a gone node, the stale-transient abandon, and both remote-snapshot adoptions; - the pod-less give-up's revert to `suspended`, which takes the caller's transaction because it relies on the precheck the caller re-ran under that transaction's locks. The terminator's unrecoverable mark-lost moves into the existing `lost` module instead. When it flips rows, the terminator's transaction then releases their placement through `lost::release_lost_placement`, and `lost::mark_started_lost` is the same give-up for `started` rows, which `lost_started_sandboxes` runs in its own transaction. It also takes the caller's transaction, because its node guards hold only under the locks that transaction took. Statement text is unchanged byte for byte, and each statement keeps its construction. The binds are the same values: the drain-phase bind is `NodeDrainPhase::Draining`, which encodes the same text as the drain's phase constant, and the three lifecycle sets the statements bind are passed in as wire strings (two are the drain's, the resident set is `pplx-space-common-svc`'s). The candidate lists, the node row lock and lock timeout, the standalone FOR UPDATE and FOR SHARE reads and the transactions stay with the callers, as do the pod-less give-up's precheck, its execution and port-exposure writes and its observed-state purge. The in-statement FOR UPDATE SKIP LOCKED of the failed-on-node flip, the stale-resident abandon and the gone-node resident failure moved with their statements. The gone-node identity guard fragment moves with the statements that use it and is public, because the drain's in-flight snapshot give-up (a `snapshots` write) still interpolates it. The statement builders are public only because the Postgres tests run them directly. Docs elsewhere that named the old locations of the moved statements now name the store functions. The moved statements' `sql!` locators now name `pplx_space_sandbox_store::drain`, and `pplx_space_sandbox_store::lost` for the terminator's statement. All three caller files leave the `sandbox_writes` ratchet.
ac-pplx
force-pushed
the
ac/lifecycle-move-drain
branch
from
November 3, 2026 21:25
2396dcd to
8932644
Compare
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.
Summary
The node drain, its pod-less give-up and the drained-node terminator wrote
sandboxeswith inline SQL. 16 statements move into a newpplx-space-sandbox-storemodule,drain, and the terminator's mark-lost moves into the existinglostmodule. When it flips rows, the terminator's transaction then releases their placement throughlost::release_lost_placement;lost::mark_started_lostis the same give-up forstartedrows, whichlost_started_sandboxesruns in its own transaction. Each is one function per transition, on the caller's executor or transaction, and its doc names the fact it writes.Moved (
node_drain/store.rs→sandbox_store::drain, same name unless shown):claim_evacuation_suspend,revert_evacuation_suspend_claim,claim_stopped_evacuation(withStoppedEvacuationClaim); the suspend commit inevacuate_sandbox→commit_evacuation_suspend; the finalize inrestore_evacuated_and_finalize→finalize_evacuation_restore;schedule_restore_retry_query→schedule_restore_retry.record_stopped_evacuation_artifact.redrive_stranded_suspending_query,mark_sandbox_lost_query,mark_sandbox_lost_on_gone_node_query,adopt_remote_snapshot_on_gone_node_query,mark_sandbox_failed_on_node_query,adopt_remote_snapshot_for_drain_query,abandon_stale_resident_sandboxes_query,fail_resident_sandboxes_on_gone_node_query(each builder keeps its name and gains a binding function).node_drain/store/pod_less_giveup.rs::revert_to_suspended→drain::revert_pod_less_resident_to_suspended(inference,&mut Transaction).drained_node_terminator/store.rs::mark_unrecoverable_sandboxes_lost_query→lost::mark_unrecoverable_sandboxes_lost_query+lost::mark_unrecoverable_sandboxes_lost(inference,&mut Transaction).gone_node_identity_guardsis public (thesnapshotsgive-up uses it).Unchanged:
main); constructions kept.PHASE_DRAINING→NodeDrainPhase::Draining(same text). The three lifecycle sets are passed as wire strings; two are the drain's, the resident set ispplx-space-common-svc's.FOR UPDATE/FOR SHAREreads, candidate lists, node lock, lock timeout, transactions, and the pod-less precheck and its other writes stay with the callers. The in-statementFOR UPDATE SKIP LOCKEDof the failed-on-node flip, the stale-resident abandon and the gone-node resident failure moved with their statements. No new dependency edges.Thin guards, documented, unchanged: the suspend commit's match is unchecked; the claim revert re-checks neither node nor marker; the finalize guards only on
restoring; the pod-less revert has no lifecycle/node guard and relies on the precheck under the caller's locks.sql!locators change. They now namepplx_space_sandbox_store::drain, andpplx_space_sandbox_store::lostfor the terminator's statement, inpg_stat_activityand query monitoring.Docs that named the old locations now name the store functions.
sandbox_writesentries removed:node_drain/store.rs15,pod_less_giveup.rs3,drained_node_terminator/store.rs1.node_drain/store.rs'sfile_sizecap (1893) stays.Validation
cargo nextest run -p pplx-space-sandbox-store -p pplx-space-cluster-controller-server -p pplx-space-db-test-fixtures --all-features --no-fail-fast(Postgres running): 1257 passed, 0 skipped.AND NOT {durable}fromlost::mark_unrecoverable_sandboxes_lost_queryfailsmark_unrecoverable_sandboxes_lost_guardsandmark_lost_yields_to_concurrent_snapshot_finalize;OR TRUEon the claim'sdrain_modeguard failsevacuation_claim_refuses_a_node_that_turned_gentle;AND FALSEon the pod-less revert fails 7pod_less_giveup_tests.cargo clippy --all-targets --all-features -- -D warnings(store, controller server, portability, api),cargo fmt --check,RUSTDOCFLAGS="-D warnings" cargo doc --no-deps(store, controller server): clean.cargo archagainstmain: 0 required findings (110 advisory, as onmain).uselines (3 files, +9/−2).up.sh6m59s, every image at this commit except the Kind-only STS bridge helper):local.forced_drain_evacuates_sandboxpassed in 34s,local.gentle_drain_leaves_sandbox_residentin 55s. Postgres statement logging and the controller's logs and sandbox events show that the forced drain changed rows through the evacuation suspend claim, the suspend commit and the restore finalize. The stranded-suspend redrive and the stale-resident abandon ran but matched no row. Stopped evacuation is off on Kind. The give-ups, the retry backoff, the pod-less revert and the terminator's mark-lost matched no row there, so only the Postgres tests cover them. Forschedule_restore_retryand the two remote-snapshot adoptions, the Postgres tests bind the builders themselves, so their production binds were checked againstmainby reading. The full typed suite was not rerun this round.Severity and risk
Severity: does not apply (refactor). Risk: a moved statement binding differently; SQL and binds checked one by one. Rollback: revert.
Rollout
Old and new controllers issue identical statements and binds; only the locator differs.
Open PRs that will need to rebase
claim_stopped_evacuation(new$6,format!, docs) andrecord_stopped_evacuation_artifact(docs, call site):node_drain/store.rs→sandbox/store/src/drain.rs.lost.rs.AI assistance: Claude Code wrote the code, ran the validation and drafted this description.
After the wave-2 restack
This PR is one layer of a 10-PR stack, restacked onto main
c5fdf0c7(which includes #826) and then rebased mechanically onto42a99002, which changed only baseline context lines, in order #1329 → #1336 → #1338 → #1339 → #1342 → #1347 → #1333 → #1335 → #1328 → #1332. The validation earlier in this description ran at pre-restack heads; this layer's head is8932644f.claim_stopped_evacuation: a newreused_durable_artifactparameter, the$6predicate built from the portability crate's durable-backup fragment, and two doc rewrites. All of it is carried intodrain::claim_stopped_evacuation, byte-identical to main's statement and construction, and the call site passes the new argument as on main. For every file [cluster-controller] re-place an evacuated stopped sandbox as stopped on another node, materialized by the shim without a VM #826 and this stack share, each SQL line [cluster-controller] re-place an evacuated stopped sandbox as stopped on another node, materialized by the shim without a VM #826 added was checked to be present verbatim at the top head, in place or in the store module it moved to.cargo fmt --all -- --check,cargo check --workspace --all-targets --all-featuresandcargo arch(0 required findings, with main's layers and table-owner rules), and the ratchet and caps this layer changes are recounted exactly.e90aa284before that rebase; nextest passed 4370 tests again at77fbae1e):cargo clippy --workspace --locked --all-targets --all-features -- -D warningsis clean, andSPACE_CI_REQUIRE_TEST_DEPS=1 cargo nextest run --all-featurespassed 4355 tests across the store, api, internal-api server, cluster-controller server, metrics, db-test-fixtures, common-core, common-svc and portability crates, [cluster-controller] re-place an evacuated stopped sandbox as stopped on another node, materialized by the shim without a VM #826's suites included.scripts/tests/smoke-kind-typed.sh allgave ECW 91/91 and native 42/42, including [cluster-controller] re-place an evacuated stopped sandbox as stopped on another node, materialized by the shim without a VM #826'snative.forced_drain_re_places_stopped_sandboxand its re-drain and restarted variants. The Direct substrate lane'sallgave ECW 51/51 and native 27/27. No flakes and no reruns.+/-lines and its SQL string literals match its reviewed head (range-diff plus a literal multiset compare).