-
Notifications
You must be signed in to change notification settings - Fork 0
Add detailed StartupReason enum for daemon lifecycle tracking #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Replace the flawed socket-existence-based detection with explicit startup reason communication from client to daemon via CLI arg. New StartupReason variants: - FirstStart: daemon starting for first time - BinaryUpdated: binary was rebuilt (mtime changed) - Recovered: previous daemon crashed/was killed - ForceRestarted: user called restart() Breaking change: DaemonServer::new() now requires startup_reason param. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
WalkthroughAdds a public Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
examples/concurrent.rs (1)
260-283: Edge case in argument parsing for--daemon-name.The
--daemon-namebranch unconditionally skips 2 positions without verifying a value exists. If a user provides--daemon-namewithout a value (e.g.,daemon --daemon-name --root-path /tmp), this would incorrectly skip the--root-pathflag.Consider adding the same guard used for other flags:
"--daemon-name" => { // Skip daemon name - DaemonServer auto-detects it + if i + 1 < args.len() && !args[i + 1].starts_with("--") { + i += 2; + } else { + i += 1; + } - i += 2; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
examples/cli.rs(2 hunks)examples/concurrent.rs(2 hunks)src/client.rs(8 hunks)src/lib.rs(5 hunks)src/server.rs(7 hunks)tests/integration_tests.rs(2 hunks)tests/version_tests.rs(8 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
examples/cli.rs (1)
src/server.rs (1)
new(118-129)
examples/concurrent.rs (1)
src/server.rs (1)
new(118-129)
src/client.rs (2)
src/transport.rs (3)
daemon_socket_exists(82-93)socket_path(49-53)socket_path(153-155)src/error_context.rs (1)
get_or_init_global_error_context(90-103)
🔇 Additional comments (16)
src/lib.rs (2)
119-167: Well-designedStartupReasonenum with clean API.The enum implementation is solid with appropriate derives (
Copy,Default,PartialEq,Eq). Theas_str(),FromStr, andDisplayimplementations are consistent and follow idiomatic Rust patterns.One minor consideration:
FromStr::Err = ()loses error context. If you later need better error messages (e.g., for user-facing CLI errors), consider using a descriptive error type. However, since this is primarily parsed internally from known CLI args, the current approach is pragmatic.
330-338: Good addition with backwards-compatible default.The
on_startupmethod with a default no-op implementation is a clean approach for optional startup hooks. This won't break existingCommandHandlerimplementations.Note: The method is synchronous. If use cases emerge requiring async initialization based on startup reason (e.g., database reconnection on
Recovered), this may need to become async in the future.src/server.rs (3)
72-73: Public field exposes startup reason for inspection.Making
startup_reasona public field allows callers to inspect how the daemon was started. This is consistent with the existing public fields (daemon_name,root_path,build_timestamp).
227-228: Correct placement of startup callback.Calling
on_startupafter PID file creation but before the accept loop is the right time—initialization is complete, but no client connections are being processed yet. This allows handlers to perform startup-specific logging or initialization safely.
118-129: Breaking API change is well-documented.The constructor signature change requires all callers to provide
startup_reason. This is a clean approach that makes the startup context explicit rather than hiding detection logic in the server.src/client.rs (5)
96-134: Clear startup reason determination logic.The logic correctly identifies the startup context:
- Existing socket that responds → use existing daemon (no spawn needed)
- Stale socket exists but daemon unresponsive →
Recovered- No socket →
FirstStartThe socket cleanup before spawn (line 109) ensures the startup reason is determined correctly before any side effects.
161-175: Correct use ofBinaryUpdatedfor version mismatch.When the daemon reports a different build timestamp, the client correctly cleans up and spawns with
StartupReason::BinaryUpdated. This accurately reflects that the binary was rebuilt since the daemon started.
463-520: Newspawn_fresh_daemonhelper encapsulates restart flow.This helper correctly handles the spawn-and-handshake sequence for explicit restarts. There's some similarity with the connection flow in
connect_with_name_and_timestamp, but the separation is justified sincespawn_fresh_daemonalways spawns a fresh daemon with a known startup reason, whileconnect_with_name_and_timestampmay connect to an existing one.
258-270: CLI argument passing for startup reason is clean.The
--startup-reasonargument usesas_str()for serialization, which pairs correctly with theFromStrimplementation insrc/lib.rs. The argument position (after--root-path) is consistent.
439-461: Restart correctly usesForceRestartedand preserves settings.The
restart()method appropriately usesStartupReason::ForceRestartedfor user-initiated restarts and correctly preserves theauto_restart_on_errorsetting across the restart.tests/version_tests.rs (1)
39-46: Test updates correctly useStartupReason::FirstStart.All version tests appropriately use
FirstStartsince each test spawns a fresh daemon instance. The API updates are consistent across all test cases.Consider adding tests for other
StartupReasonvariants to verify theon_startupcallback receives the correct reason. Would you like me to suggest test cases forBinaryUpdated,Recovered, andForceRestartedscenarios?examples/cli.rs (2)
69-98: Basic argument parsing works but has edge cases.The manual argument parsing is straightforward for an example. A few observations:
Line 90:
unwrap_or_default()silently falls back toFirstStartfor invalid--startup-reasonvalues. This is acceptable since the client controls these values, but a warning log would help debug misconfigurations.Missing bounds check: Lines 83-85 check
i + 1 < args.len()before accessingargs[i + 1], which is correct. Lines 89-91 do the same. Good defensive coding.For a production CLI, consider using a proper argument parser (e.g.,
clap), but for an example, this is acceptable.
113-116: Correctly updated to newDaemonServer::newsignature.The example properly passes the parsed
startup_reasontoDaemonServer::new, and the logging at line 111 includes the startup reason for observability.examples/concurrent.rs (1)
296-304: LGTM!The
startup_reasonis correctly integrated into both the tracing output and theDaemonServer::newcall, aligning with the updated API signature.tests/integration_tests.rs (2)
28-35: LGTM!The test helper correctly passes
StartupReason::FirstStartand100for max_connections, matching the expected usage pattern for fresh test daemon instances and aligning with the updatedDaemonServer::new_with_name_and_timestampAPI.
54-61: LGTM!The test helper for custom connection limits correctly incorporates
StartupReason::FirstStartwhile forwarding the user-specifiedmax_connections, maintaining consistency with the other helper.
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
CHANGELOG.md (1)
8-14: Consider adding migration example for StartupReason usage.The 0.7.0 breaking changes are clearly documented, but unlike v0.3.0 and v0.4.0 entries, this section lacks a migration code example. Prior versions show handler/client/server setup before/after patterns, which help users adapt quickly.
Consider adding a brief "Migration" subsection showing how to construct
DaemonServer::new(root_path, startup_reason, handler)and how clients determine and pass the--startup-reasonCLI argument. This would follow the pattern established in v0.3.0 (lines 129–170).Example structure:
### Migration **Before (v0.6.0):** \`\`\`rust let (server, _handle) = DaemonServer::new(root_path, handler); \`\`\` **After (v0.7.0):** \`\`\`rust let reason = StartupReason::FirstStart; // Determined by client logic let (server, _handle) = DaemonServer::new(root_path, reason, handler); \`\`\`
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.github/workflows/ci.yml(1 hunks)CHANGELOG.md(1 hunks)Cargo.toml(1 hunks)
🔇 Additional comments (2)
Cargo.toml (1)
3-3: Version bump aligns with breaking API changes.The semver-major bump (0.6.0 → 0.7.0) is appropriate for the breaking change to
DaemonServer::new()signature..github/workflows/ci.yml (1)
24-24: Verify intent of restricting clippy to ubuntu-latest.The clippy job now runs only on ubuntu-latest instead of the matrix. Given that the codebase has platform-specific dependencies (
nixfor Unix,windows-sysfor Windows), this change may miss platform-specific linting issues.Confirm:
- Is this restriction intentional (e.g., all platform-specific code expected to compile cleanly via conditional compilation on ubuntu-latest)?
- Or should clippy retain the matrix strategy to catch platform-specific warnings?
If platform-specific warnings could occur, consider restoring the matrix for the clippy job.
Summary
StartupReasonvariants:FirstStart,BinaryUpdated,Recovered,ForceRestarted--startup-reasonarg when spawning daemonDaemonServer::new()now requiresstartup_reasonparameterProblem
The previous implementation detected startup reason by checking if socket exists before creating a new one. This was unreliable because the client always cleans up the socket before spawning, so the daemon almost always saw "FreshStart" even when replacing an old daemon due to binary update.
Solution
The client knows why it's spawning the daemon, so it now communicates this via
--startup-reasonCLI arg:first-startrecoveredbinary-updatedrestart()→force-restartedTest plan
cargo checkpasses--startup-reasonarg🤖 Generated with Claude Code