Skip to content

fix(native-engine): reuse cached server and restore saved sessions#6606

Merged
matthewevans merged 1 commit into
mainfrom
codex/ship-native-engine-cache
Jul 24, 2026
Merged

fix(native-engine): reuse cached server and restore saved sessions#6606
matthewevans merged 1 commit into
mainfrom
codex/ship-native-engine-cache

Conversation

@matthewevans

@matthewevans matthewevans commented Jul 24, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • Performance

    • Improved native server startup by reusing verified locally cached files when available.
  • Bug Fixes

    • Improved restoration of persisted game sessions on Windows, including clearer handling of restoration failures.
    • Preserved eligible lobby registrations when restoring games that have not yet started.
  • Diagnostics

    • Added startup logs to help investigate server launch failures before normal logging becomes available.

@matthewevans
matthewevans enabled auto-merge July 24, 2026 20:32
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Native engine provisioning and diagnostics

Layer / File(s) Summary
Verified binary caching
client/src-tauri/src/native_engine.rs
Cached native binaries are reused only when their executable and matching .minisig verify against the embedded public key; invalid or missing caches are downloaded and rewritten.
Server startup logging
client/src-tauri/src/native_engine.rs
Server startup creates the log directory, passes --log-dir, and redirects child stderr to server-startup.log.

Persisted session restoration

Layer / File(s) Summary
Platform-specific session restoration
crates/phase-server/src/main.rs
Persisted sessions restore on a larger-stack Windows thread and synchronously elsewhere, with restore logging and lobby re-registration based on restored state.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ensure_native_engine_sync
  participant provision_binary
  participant native_server_process
  ensure_native_engine_sync->>provision_binary: provision verified binary
  provision_binary->>provision_binary: validate cached binary and minisign
  ensure_native_engine_sync->>native_server_process: spawn with log directory and startup log
  native_server_process-->>ensure_native_engine_sync: write startup stderr
Loading
sequenceDiagram
  participant persisted_restore_loop
  participant restore_persisted_session
  participant GameSession
  persisted_restore_loop->>restore_persisted_session: restore persisted session JSON
  restore_persisted_session->>GameSession: deserialize and build session
  GameSession-->>persisted_restore_loop: return lobby metadata and started state
Loading

Possibly related PRs

  • phase-rs/phase#6586: Modifies the native engine server spawning flow with another spawn_server parameter.
  • phase-rs/phase#6597: Also changes Windows-specific native server process-spawning behavior.

Suggested reviewers: parthmishra, realdiligent

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the two main changes: reusing the cached native server and restoring saved sessions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/ship-native-engine-cache

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/src-tauri/src/native_engine.rs`:
- Around line 378-385: Scope log paths by NativeEngineKey instead of sharing
base/logs: update log_directory and its callers, including
ensure_native_engine_sync, to use the current key consistently with
key_directory, binary, and data_directory. Extend gc_after_successful_spawn to
prune stale or aged rolling and per-game logs for the current key while
preserving active logs.

In `@crates/phase-server/src/main.rs`:
- Around line 116-139: Update restore_persisted_session to distinguish invalid
persisted-session data from operational failures such as thread creation, join,
or panic errors, using a typed error or equivalent classification instead of
collapsing all failures into String. Adjust its caller to delete the persisted
row only for confirmed-invalid snapshots, while retaining it for recoverable
operational restore failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0cbd306f-b25d-49dd-95e9-a2d153f9b716

📥 Commits

Reviewing files that changed from the base of the PR and between 731cffc and 992d4c7.

📒 Files selected for processing (2)
  • client/src-tauri/src/native_engine.rs
  • crates/phase-server/src/main.rs

Comment on lines +378 to +385
fn log_directory(&self) -> PathBuf {
self.base.join(LOG_DIRECTORY)
}

fn startup_log(&self) -> PathBuf {
self.log_directory().join("server-startup.log")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Log directory isn't keyed by engine version/channel and is never pruned.

log_directory()/startup_log() return a single shared path (base/logs) for every NativeEngineKey, unlike key_directory, binary, binary_signature, and data_directory, which are all scoped per key. This is the first PR wiring --log-dir into the spawned server (previously stderr/stdout were discarded), so it's a newly introduced gap, not pre-existing:

  1. Switching preview/release channels interleaves phase-server.log and games/<code>.log in the same directory, muddying diagnosis across channels.
  2. This directory sits outside gc_after_successful_spawn's scope (which only prunes key_directory entries and cache blobs per the GC test), so the daily-rolling main log and per-game logs accumulate indefinitely with no retention policy — undermining the very "diagnosable without a console" goal this feature is meant to serve once disk fills up.

As per path instructions, "Surface GAPS (missing or wrong behavior)" is exactly this kind of missing lifecycle management for a newly-introduced persistent resource.

♻️ Proposed fix: scope logs per key and add retention
-    fn log_directory(&self) -> PathBuf {
-        self.base.join(LOG_DIRECTORY)
-    }
-
-    fn startup_log(&self) -> PathBuf {
-        self.log_directory().join("server-startup.log")
-    }
+    fn log_directory(&self, key: &NativeEngineKey) -> PathBuf {
+        self.key_directory(key).join(LOG_DIRECTORY)
+    }
+
+    fn startup_log(&self, key: &NativeEngineKey) -> PathBuf {
+        self.log_directory(key).join("server-startup.log")
+    }

Update the call site in ensure_native_engine_sync (already has key in scope) accordingly, and add pruning of stale/aged rolling and per-game log files to gc_after_successful_spawn so the current key's logs don't grow unbounded either.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src-tauri/src/native_engine.rs` around lines 378 - 385, Scope log
paths by NativeEngineKey instead of sharing base/logs: update log_directory and
its callers, including ensure_native_engine_sync, to use the current key
consistently with key_directory, binary, and data_directory. Extend
gc_after_successful_spawn to prune stale or aged rolling and per-game logs for
the current key while preserving active logs.

Source: Path instructions

Comment on lines +116 to +139
#[cfg(windows)]
fn restore_persisted_session(json: &str, db: SharedDb) -> Result<GameSession, String> {
let json = json.to_owned();
let restore = std::thread::Builder::new()
.name("phase-session-restore".to_owned())
.stack_size(PERSISTED_SESSION_RESTORE_STACK_BYTES)
.spawn(move || {
let persisted = serde_json::from_str::<server_core::PersistedSession>(&json)
.map_err(|error| error.to_string())?;
Ok(GameSession::from_persisted(persisted, db.as_ref()))
})
.map_err(|error| format!("could not start restore thread: {error}"))?;
restore
.join()
.map_err(|_| "persisted session restore thread panicked".to_owned())?
}

#[cfg(not(windows))]
fn restore_persisted_session(json: &str, db: SharedDb) -> Result<GameSession, String> {
let persisted = serde_json::from_str::<server_core::PersistedSession>(json)
.map_err(|error| error.to_string())?;
Ok(GameSession::from_persisted(persisted, db.as_ref()))
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the restore helper and its caller.
rg -n "restore_persisted_session|delete_session|PersistedSession|from_persisted" crates/phase-server/src/main.rs

# Show the relevant surrounding lines for the restore helper.
sed -n '100,170p' crates/phase-server/src/main.rs

# Show the caller area around the delete_session logic.
sed -n '1020,1060p' crates/phase-server/src/main.rs

Repository: phase-rs/phase

Length of output: 6075


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1028,1095p' crates/phase-server/src/main.rs

Repository: phase-rs/phase

Length of output: 3727


Don’t delete persisted sessions on generic restore errors. restore_persisted_session collapses JSON parse, thread spawn/join, and panic failures into Err(String), but the caller deletes the row for every Err. A transient restore failure can permanently erase a recoverable session; only delete confirmed-invalid snapshots and keep operational failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/phase-server/src/main.rs` around lines 116 - 139, Update
restore_persisted_session to distinguish invalid persisted-session data from
operational failures such as thread creation, join, or panic errors, using a
typed error or equivalent classification instead of collapsing all failures into
String. Adjust its caller to delete the persisted row only for confirmed-invalid
snapshots, while retaining it for recoverable operational restore failures.

Source: Path instructions

@matthewevans
matthewevans added this pull request to the merge queue Jul 24, 2026
Merged via the queue into main with commit ebe1294 Jul 24, 2026
15 checks passed
@matthewevans
matthewevans deleted the codex/ship-native-engine-cache branch July 24, 2026 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant