Repository navigation
feat(daemon): on-demand lifecycle — lazy start, idle auto-stop, 20–30 session bench - #54
Merged
Merged
Conversation
… session bench (#53) Lazy start (client-side): the shared-mode IPC wrapper, on finding no live daemon (or a connect failure against a dead one), runs `ccstatusline daemon start` — serialized by the #47 cold-start lock — and transparently retries the render into the fresh daemon. Bounded to one start per invocation; CCSTATUSLINE_NO_AUTOSTART restores fail-fast, CCSTATUSLINE_DAEMON_START overrides the command. Idle auto-stop (daemon-side): after daemonIdleStopMinutes (settings.json, default 10, 0 disables) with zero requests the daemon releases its endpoints and exits; any request resets the clock, so busy periods keep it alive with no observers or session counting. Opt-in unchanged: the lazy start lives only in the wrapper Claude Code runs after `daemon install`; the one-shot render path never spawns anything (tested). Bench (docs/daemon-53-bench.md, scripts/benchmark-lifecycle-53.py): 20 and 30 concurrent clients over mixed cold/warm caches vs one-shot. Warm bursts: 74.5–76.7% aggregate-CPU reduction and p95 cut roughly in half; the cold wave pays the on-demand start once per idle period (−23…−26% vs one-shot). One daemon served each whole burst (stable pid, 85 bounded-503 retries absorbed by the ladder, zero failures); idle stop verified at 1 minute with a transparent lazy restart. Closes #53 Co-Authored-By: Claude Code <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(daemon): on-demand lifecycle — lazy start, idle auto-stop, 20–30 session bench
Closes #53. Builds on #44 (transport), #47 (cold-start lock / recovery), #49 (providers), #50 (install/gates).
What changes
Lazy start (client-side). The shared-mode wrapper
client/ccstatusline-ipc, on finding no live daemon, runsccstatusline daemon start(serialized by the #47 cold-start lock, so racing clients converge on one server) and retries the render into the fresh daemon. A connect failure against a daemon that died mid-request takes the same path. Bounded to one start attempt per invocation;CCSTATUSLINE_NO_AUTOSTART=1restores fail-fast,CCSTATUSLINE_DAEMON_STARToverrides the start command (tests use it as a seam). The default start command resolves the entry from the wrapper's own install root (dist/ccstatusline.jsvia node,src/ccstatusline.tsvia bun).Idle auto-stop (daemon-side).
daemonIdleStopMinutesin settings.json (default 10,0disables): after that window with zero requests the daemon releases its endpoints and exits. Any request — health included — resets the clock, so busy periods keep it alive with no observers and no session counting. The existing #47 recovery paths (stale socket, upgrade restart, bounded waits) apply to lazy starts unchanged.Opt-in unchanged. The lazy start lives only in the wrapper Claude Code executes after
daemon install; one-shot users never spawn anything (tested end-to-end: a one-shot render leaves the runtime dir empty).Bench (docs/daemon-53-bench.md, raw: docs/daemon-53-results.json, harness: scripts/benchmark-lifecycle-53.py)
20 and 30 concurrent clients, mixed cold/warm caches (sessions alternate 500-row / 10 000-row transcripts and dirty / clean repos), no daemon pre-started:
The cold wave pays the on-demand start once per idle period (every simultaneous first client spawns a starter; one wins the lock). Steady state keeps the #19/#50 win and halves p95.
Burst acceptance: one daemon (stable pid across waves) served each whole scenario; daemon counters
ok=91, busy=85with zero client failures — the bounded queue's 503s were fully absorbed by the client retry ladder. Idle acceptance atdaemonIdleStopMinutes: 1: exited by itself after ~72 s (1 min + one 12 s check interval), discovery cleaned up, next render lazily restarted a fresh daemon (rc 0, new pid).Tests
0disables,onIdleStopfiresbun test(2742 pass) andbun run lintfully greenRisks / notes
🤖 Generated with Claude Code