Skip to content

fix(blaze): keep lifecycle state bound to opened directories - #2329

Draft
WeissonHan wants to merge 3 commits into
alibaba:mainfrom
WeissonHan:fix/blaze/state-directory-ownership
Draft

fix(blaze): keep lifecycle state bound to opened directories#2329
WeissonHan wants to merge 3 commits into
alibaba:mainfrom
WeissonHan:fix/blaze/state-directory-ownership

Conversation

@WeissonHan

@WeissonHan WeissonHan commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Why

Lifecycle records and sandbox work directories were addressed by rebuilding daemon.state_dir/<uuid> paths for later operations. A path says where to look, but it does not keep later persistence, startup compensation, or backend cleanup attached to the directory object the daemon accepted.

Replacing the configured root or a UUID entry during the daemon lifetime could therefore redirect later work to another object. A failed first publication could also leave creation without a trustworthy ownership result.

What changed

Before: production scan, load, persistence, sandbox-directory lookup, and backend startup each carried or reconstructed paths. The daemon did not have one ownership boundary spanning the accepted state root, a sandbox directory, and the backend handle that continued using it.

After: the daemon opens and locks the lifecycle-state root before inventory. Record operations are relative to retained directories, new sandbox directories are published without replacing an existing entry, and nonterminal directories remain owned until their terminal record is committed. The accepted OwnedRunDir is passed into backend startup; both successful and partially started backend handles retain it. Firecracker inherits the required directory descriptor into the child before exec.

The resulting flow is:

opened state root
        |
descriptor-relative UUID directory and lifecycle record
        |
OwnedRunDir passed to backend startup
        |
successful or partial backend handle retains the same object
        |
terminal lifecycle commit releases daemon ownership

Commits

  1. 32aee3e09 refactor(blaze): centralize daemon state access

    • Intent: establish one production access boundary before changing object-lifetime behavior.
    • Implementation: route scan, load, persist, and per-sandbox directory lookup through StateStore.
    • Delivery state: mechanical same-PR foundation; disk layout and runtime behavior remain unchanged.
  2. 83899c248 fix(blaze): own persisted state directories

    • Intent: keep inventory, lifecycle updates, and failure compensation attached to the accepted state-root and UUID directory objects.
    • Implementation: retain and lock the state root, use descriptor-relative record I/O, publish through unique staging with no-replace and durability checks, retain nonterminal owners, and report RecoveryRequired when publication or rollback remains uncertain.
    • Delivery state: live lifecycle-state ownership and recovery behavior; path-based spawners receive a descriptor-backed alias.
  3. 5e740cac7 fix(blaze): retain runtime directory owners

    • Intent: prevent backend startup from reducing an accepted directory object back to a path that can be resolved again.
    • Implementation: pair portable launch parameters with OwnedRunDir, validate sandbox identity, retain the owner in success and partial-failure handles, and let Firecracker inherit the descriptor it uses for runtime artifacts.
    • Delivery state: completes the daemon-to-backend ownership handoff; no daemon HTTP or wire operation is added.

These commits form one PR because they establish one state_dir/<uuid> continuity invariant. The first commit creates the access boundary, the second binds lifecycle records to opened directories, and the third carries that same directory owner into backend lifetime. Splitting the final two into separate PRs would leave records and runtime artifacts with different ownership contracts.

Related issue

closes #2327

Relates to #2254.

User / Agent impact

No new daemon operation, configuration key, or persisted format is introduced. Existing create, restart reconciliation, destroy, and warm paths keep their lifecycle-state and backend-runtime work attached to the directory objects accepted by the daemon. Uncertain publication or cleanup retains recovery responsibility instead of silently dropping ownership.

This PR covers lifecycle-state and backend runtime directories only. Independent storage roots, the earlier startup handoff between peer-root validation and lifecycle-state opening, and checkpoint namespaces remain tracked by #2254. Scan atomicity, directory/record UUID consistency, and staging-cleanup identity checks are tracked by #2328.

Risk and compatibility

  • Public CLI, API, configuration, or documented behavior changed
  • Privileged or security-sensitive behavior changed
  • Cross-component contract changed
  • Migration or rollback guidance is needed

The daemon now retains one state-root descriptor plus one directory descriptor per nonterminal sandbox. Backend handles extend the lifetime of the existing per-sandbox descriptor; Firecracker also inherits it into the child.

The internal Rust contract is intentionally tightened: the workspace-only SpawnRequest no longer carries a duplicate raw run_dir, and blazed uses a daemon-local typed request. The workspace crates are not published. Daemon HTTP/wire operations, configuration, persisted lifecycle records, and the restart-reconciliation protocol are unchanged.

Validation

Validation ran on Linux x86_64, kernel 6.6.102, with rustc/cargo 1.93.1. C1 was tested at its final SHA. C2 and C3 were tested from fresh archives of their exact final source trees; later message-only rewrites changed commit metadata without changing tree OIDs (3d8c8808281bbcabac47252732e58aa649b4c35b and 2ff01a2646acbedb6fe8623fa63ce031a4448029). Default and all-features runs used separate initially empty Cargo targets.

The following passed for every commit:

  • cargo fmt --all --check
  • cargo build --workspace --all-targets --locked
  • cargo clippy --workspace --all-targets --locked -- -D warnings
  • cargo test --workspace --locked
  • cargo build --workspace --all-targets --all-features --locked
  • cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
  • cargo test --workspace --all-features --locked
  • strict rustdoc with RUSTDOCFLAGS="-D warnings"
Commit Default tests All-features tests Result
32aee3e09 56 core + 122 daemon 56 core + 132 daemon PASS
83899c248 56 core + 136 daemon 56 core + 153 daemon PASS
5e740cac7 56 core + 140 daemon 56 core + 157 daemon PASS

At the final commit, a real Firecracker create/destroy run replaced the configured UUID pathname while the sandbox was running. Status and destroy operations continued through the accepted directory object; the terminal record and backend stop marker appeared only there, the replacement directory remained empty, the backend process exited, and the daemon remained healthy.

PR Lint, CLA, and the repository CI workflow passed for the submitted head.

Documentation and rollback

No operator documentation changes are required because the daemon API, configuration, and persisted layout are unchanged. Reverting these three commits restores path-based lifecycle-state and backend-runtime handling; existing lifecycle records remain readable.

Lifecycle record I/O and runtime-directory lookup were spread across
ServerState, SandboxManager, and API handlers. That left no single boundary
where later ownership and recovery rules could be enforced consistently.

This enables lifecycle code to apply one state access contract before any
object-identity behavior changes.

Make StateStore the sole production entry point for scans, loads, writes, and
per-sandbox directory lookup, then route existing callers through it. The
persisted layout and runtime behavior remain unchanged in this commit.

Keeping this mechanical consolidation separate makes the following ownership
changes independently reviewable and reversible.

Signed-off-by: Weisson <Weisson@linux.alibaba.com>
Lifecycle records and sandbox work directories were addressed by rebuilding
state_dir/<uuid> paths for each operation. Replacing the configured root or a
UUID entry after daemon startup could redirect later persistence or cleanup,
while a failed first publication could leave creation without a trustworthy
ownership result.

This enables sandbox creation and startup reconciliation to keep lifecycle
writes, backend preparation, and orphan cleanup attached to the directory
objects accepted by the daemon.

Open and exclusively lock the state root, perform record I/O relative to
retained descriptors, and publish each sandbox directory through a unique
staging directory with no-replace and durability checks. Retain nonterminal
directories until their terminal lifecycle record is committed. If publication
or rollback remains uncertain, keep the owner retained and report
RecoveryRequired instead of discarding the operation.

Path-based spawners receive a descriptor-backed alias, and Firecracker derives
its backend identifier from the sandbox UUID instead of that alias. The manager
uses this protection immediately; transferring the owner into backend instance
handles remains in the following commit. Daemon HTTP and wire operations, and
the persisted layout, are unchanged.

This design retains one state-root descriptor plus one directory descriptor per
nonterminal sandbox to keep lifecycle updates and backend cleanup attached to
their accepted objects.

Fixes: 1f0cfac ("feat(anvil): scaffold local orchestrator crate skeleton")
Signed-off-by: Weisson <Weisson@linux.alibaba.com>
Backend spawners received a runtime path but did not own the opened directory
behind it. The state store could release its copy after a terminal commit while
a successful or partially started backend handle still needed runtime artifacts.
Firecracker also needed the descriptor to survive exec when its paths used
/proc/self/fd.

This enables successful and partially started backend handles to keep runtime
artifacts attached to the directory object accepted for the sandbox, even if
the configured UUID path is replaced during that daemon lifetime.

Keep portable launch parameters in blaze-core and pair them with OwnedRunDir in
a daemon-local BackendSpawnRequest. Validate the sandbox identifier, attach the
owner to handles returned on both success and partial failure, and pass the
typed owner through preparation and orphan cleanup.

Firecracker inherits the directory descriptor into its child before exec. The
manager drops the backend handle only after terminal retention bookkeeping, so
cleanup does not lose its last owner early. Storage-sync test fixtures only
adapt to the typed request; production synchronization behavior is unchanged.

This tightens an internal Rust contract by removing the duplicate raw run_dir
field. It does not change daemon HTTP or wire operations, persisted records, or
the restart-reconciliation protocol. Backend handles extend the lifetime of the
existing per-sandbox descriptor; Firecracker also inherits it into the child.

Fixes: c33b495 ("feat(blaze): own sandbox runtime resources")
Signed-off-by: Weisson <Weisson@linux.alibaba.com>

Copy link
Copy Markdown
Collaborator Author

@codex review

Please review the complete pull request: every commit, the cumulative diff against the base branch, and the implementation and tests as one submitted change. Do not restrict the review to the most recently modified commit.

@github-actions github-actions Bot added the component:blaze src/blaze label Aug 7, 2026
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 5e740cac73

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[blaze] fix(blaze): keep lifecycle state bound to opened directories

1 participant