Skip to content

fix(daemon): bind-before-heavy-init single-flight for concurrent spawns (#1010)#1021

Merged
zackees merged 1 commit into
mainfrom
fix/1010-daemon-spawn-single-flight
Jul 9, 2026
Merged

fix(daemon): bind-before-heavy-init single-flight for concurrent spawns (#1010)#1021
zackees merged 1 commit into
mainfrom
fix/1010-daemon-spawn-single-flight

Conversation

@zackees

@zackees zackees commented Jul 9, 2026

Copy link
Copy Markdown
Member

Fixes #1010. N concurrent fbuild invocations with no daemon running (common CI / parallel-build pattern) all spawned a full fbuild-daemon, each paying for the embedded zccache service + DaemonContext + package managers before the LATE port bind finally failed — N heavyweight processes to leave one.

  1. Daemon: bind before heavy init. Moved bind_listener_with_retry to the top of main (after tracing setup), ahead of the zccache service / DaemonContext / app build. A daemon that loses the bind race detects it in milliseconds and exits — no wasted init.
  2. Fast, correct collision handling. The endpoint is now version+identity keyed (daemon: endpoint isolation follow-up to #940 — key the default endpoint by cache-identity/checkout + deterministic version arbitration #1009), so anything listening on the port is a daemon of our own version. New another_daemon_is_listening() distinguishes "a live daemon owns it → yield (exit 0)" from "stale TIME_WAIT → retry". Losers exit 0 cleanly instead of retrying 1.5 s then exiting 1.
  3. CLI: adopt-before-spawn. ensure_direct_daemon_running re-checks health before each spawn so a concurrent invocation's just-started daemon is adopted, not piled onto.

No file locks (banned by ban_file_based_locks) — the OS-exclusive port bind is the election; the daemon makes losing it cheap.

Verified — local + Linux Docker

cargo check --workspace green (14.5s); another_daemon_is_listening test (live listener → yield, free port → retry) passes on Linux Docker. clippy -D warnings + fmt clean.

🤖 Generated with Claude Code

…ns (#1010)

When N `fbuild` invocations cold-start with no daemon running (common CI /
parallel-build pattern) they all spawn a full `fbuild-daemon`, each paying for
the embedded zccache service + DaemonContext + package/framework managers before
the LATE port bind finally fails — so N heavyweight processes churn to leave one.

Two changes:

1. **Daemon: bind before heavy init.** Move `bind_listener_with_retry` to the top
   of `main` (right after tracing setup), ahead of the zccache service /
   DaemonContext / app build. A daemon that loses the bind race now detects it in
   milliseconds and exits — no wasted init.

2. **Fast, correct collision handling.** Since the endpoint is version+identity
   keyed (#1009), anything actually listening on the port is a daemon of our own
   version. New `another_daemon_is_listening()` distinguishes "a live daemon owns
   it → yield (exit 0)" from "stale TIME_WAIT kernel state → retry". Losers exit
   0 cleanly instead of retrying 1.5 s then exiting 1.

3. **CLI: adopt-before-spawn.** `ensure_direct_daemon_running` re-checks health
   before each spawn attempt so a concurrent invocation's just-started daemon is
   adopted rather than piling on a redundant spawn.

No file locks (banned by `ban_file_based_locks`) — the OS-exclusive port bind is
the election, and the daemon makes losing that election cheap.

Tests: `another_daemon_is_listening` detects a live listener / ignores a free
port. Verified daemon + workspace compile; clippy -D warnings + fmt clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@zackees zackees merged commit c3e9333 into main Jul 9, 2026
@zackees zackees deleted the fix/1010-daemon-spawn-single-flight branch July 9, 2026 23:57
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@zackees, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 39 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6f56936c-8d0c-4071-a2cf-a331542d7146

📥 Commits

Reviewing files that changed from the base of the PR and between 0bb7252 and dab2dff.

📒 Files selected for processing (2)
  • crates/fbuild-cli/src/daemon_client.rs
  • crates/fbuild-daemon/src/main.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1010-daemon-spawn-single-flight

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

daemon: concurrent fbuild invocations stampede daemon spawn — add single-flight + bind before heavy init

1 participant