Skip to content

sandbox-store: move five api writes of sandboxes into the store - #1347

Merged
ac-pplx merged 1 commit into
ac/lifecycle-move-directfrom
ac/lifecycle-move-api-rest
Nov 3, 2026
Merged

ac-pplx merged 1 commit into
ac/lifecycle-move-directfrom
ac/lifecycle-move-api-rest

Conversation

@ac-pplx

@ac-pplx ac-pplx commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Mechanical move, no functional change. The sandboxes row writes listed below move into pplx-space-sandbox-store with their SQL byte-for-byte; callers keep their locks, transactions and error handling. Everything else is doc comments. Part of the wave-2 stack (6 of 10, on #1342); it lands with the stack.

Summary

Five UPDATE sandboxes statements were still inline in the api, though every write of the row belongs to pplx-space-sandbox-store. They move into three new store modules. The SQL text is unchanged except for binds, and each function is named for its transition, with a doc comment on the fact it writes:

  • api::controllers::sandbox::updates set_network_policy → sandbox_store::intent::set_network_policy_override (intent; generation check is the only guard)
  • updates set_snapshots_enabled_desired → intent::set_snapshots_enabled_desired (intent; id only)
  • responses maybe_update_sandbox_lifecycle → action_stamp::stamp_action_lifecycle (operation step)
  • responses maybe_update_sandbox_state → action_stamp::stamp_action_state (operation step)
  • substrate persist_optional_current_local_artifact → artifact_marker::set_current_local_artifact (observed projection; id only, relies on the caller's row lock)

action_stamp holds the lifecycle dispatcher's post-action stamps. Their guard refuses only terminal rows (failed, quarantined, deleted, cleanup_required), so a stamp can overwrite an in-flight lifecycle (deleting, stopping, suspending, suspended). This move keeps that behaviour as it is. The module docs state it, the existing characterization table pins it as a known defect, and a later behaviour change fixes it.

The api functions keep their signatures, error mapping and logging. The FOR UPDATE lock, the deleting-snapshot check, the transactions and the reconcile enqueues stay at the call sites. Store functions take the caller's executor and return sqlx::Error. The marker takes (LocalArtifactId, ArtifactKind) instead of the api's PublishedLocalArtifact and binds the same values.

sql! locators change. The moved statements' locators now name the store modules, and that path is what appears in pg_stat_activity and query monitoring.

architecture-baseline.toml: sandbox_writes goes to 2 for updates.rs and 1 for responses.rs, and the substrate/mod.rs entry is removed. The substrate/mod.rs file_size cap goes from 2089 to 2064.

Not moved

  • crates/libs/placement/machine/src/postgres.rs: completion_sql and two claim files in postgres/sql/ (user_restore_claim.sql, evacuation_restore_claim.sql) write sandboxes, and the third, claim_miss_snapshot.sql, only reads it. None of them has a production caller. They are left as they are for a separate decision; this PR does not touch crates/libs/placement.
  • The rollback deletes in updates.rs and the post-wake update in responses.rs belong to other moves.

Open PRs that will need to rebase

Validation

At this head (rebased on main):

  • cargo clippy --locked -p pplx-space-sandbox-store -p pplx-space-api -p pplx-space-placement --all-targets --all-features -- -D warnings: clean.
  • cargo arch --base-baseline <main baseline> --base-architecture <main ARCHITECTURE.toml>: 0 required findings.
  • cargo nextest run --locked -p pplx-space-sandbox-store -p pplx-space-api -p pplx-space-placement --all-features, with the shared Postgres and Redis setup scripts: 2232 passed, 1 skipped (an #[ignore] local benchmark), no dependency skips. The PR edits no test files.
  • Mutation check: dropping 'quarantined' from the stamp_action_lifecycle guard fails post_action_stamps_write_every_stored_lifecycle_but_the_terminal_ones. Reverted.
  • A fresh Kind cluster from this head (infra/space/kind/scripts/up.sh, 7m12s), then scripts/tests/smoke-kind-typed.sh all (51m48s):
    • ECW: 85 passed, 0 failed, 3 inconclusive.
    • Native: 35 passed.
    • The inconclusive ecw.cold_create_environment and ecw.warm_pool_environment needed SPACE_SMOKE_KIND_COLD_RECEIPTS. Both passed when rerun with it set.
    • local.restarted_btrfs_send_is_rejected_and_retried needs the otel telemetry mode and was not run.

Severity and risk

Severity: does not apply (a refactor with no behaviour change).

Risk: low. Each statement, its binds and its transaction are unchanged, so old and new pods issue the same SQL during a rollout. Only the sql! locator text differs. Rollback: revert this commit.

AI assistance: an AI agent (Claude Code) wrote the code change and 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 onto 42a99002, 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 is 54c3ba53.

@github-actions github-actions Bot added the triage:pending Waiting for a maintainer to triage label Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown

Thanks for opening this pull request! A SPACE maintainer will take a look.

CONTRIBUTING.md explains how we review outside contributions.

@ac-pplx
ac-pplx force-pushed the ac/lifecycle-move-api-rest branch from e6d14ad to 2729fad Compare November 2, 2026 23:25
@ac-pplx
ac-pplx changed the base branch from main to ac/lifecycle-move-direct November 3, 2026 03:50
@ac-pplx
ac-pplx added this pull request to stack #1382 November 3, 2026 03:50
@ac-pplx
ac-pplx marked this pull request as ready for review November 3, 2026 03:51
@ac-pplx
ac-pplx marked this pull request as draft November 3, 2026 03:52
@ac-pplx
ac-pplx force-pushed the ac/lifecycle-move-api-rest branch from 2729fad to 5c487bd Compare November 3, 2026 06:00
@ac-pplx
ac-pplx marked this pull request as ready for review November 3, 2026 06:29
Five UPDATE sandboxes statements were still written inline in the api,
against the store crate's rule that every write of the row belongs to
pplx-space-sandbox-store. They move into three new store modules,
byte-for-byte except for their binds:

- intent::set_network_policy_override and
  intent::set_snapshots_enabled_desired, from
  SandboxController::set_network_policy and set_snapshots_enabled_desired
  (controllers/sandbox/updates.rs).
- action_stamp::stamp_action_lifecycle and
  action_stamp::stamp_started_after_start, from
  maybe_update_sandbox_lifecycle and maybe_mark_started_after_start
  (controllers/sandbox/responses.rs). These are the lifecycle
  dispatcher's post-action stamps. Their guard refuses only terminal
  rows. Nothing else is re-checked, so a stamp overwrites any other
  stored lifecycle, including `deleting`, `stopping`, `suspending` and
  `suspended`, which another intent may have written while the
  substrate call was in flight. The move keeps that behaviour as it is;
  the module docs say so, and a later behaviour change fixes it.
- artifact_marker::set_current_local_artifact, from
  persist_optional_current_local_artifact (controllers/substrate/mod.rs).

The api functions stay where they are, with the same signatures, error
mapping and logging. The FOR UPDATE row lock, the deleting-snapshot check,
the transactions and the reconcile enqueues also stay at the call sites.
The store functions run on the executor the caller passes and return
sqlx::Error, so caller error handling is unchanged. The artifact marker
takes the artifact as a (LocalArtifactId, ArtifactKind) pair, because
PublishedLocalArtifact is an api type; it binds the same UUID and string
values as before.

The sql! locators of the moved statements now name the store modules,
which changes how they appear in pg_stat_activity and query monitoring.
pattern_ratchet.sandbox_writes drops to 1 for responses.rs (the post-wake
update is left for another change), the updates.rs entry is removed (the
create path's change below this one moved its rollback deletes), the
substrate/mod.rs entry is removed, and the substrate/mod.rs file_size cap
is lowered to 2071.
@ac-pplx
ac-pplx force-pushed the ac/lifecycle-move-api-rest branch from 5c487bd to 54c3ba5 Compare November 3, 2026 21:24
@ac-pplx
ac-pplx added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit acdf8e1 Nov 3, 2026
27 checks passed
@ac-pplx
ac-pplx deleted the ac/lifecycle-move-api-rest branch November 3, 2026 22:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage:pending Waiting for a maintainer to triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants