Repository navigation
perf(runtime-host): list recovery Session headers without the duplicate message decode (#4032) - #4033
Conversation
…te message decode listForRecovery() decodes every recoverable Session's messages and discards the result before prepareHostedExecutionRecovery re-decodes the same messages in its per-Session loop, so Host startup paid the message decode twice per Session. List headers directly; the per-Session readMessagesForRecovery remains the single fail-closed validation read, so corrupt durable messages still fail startup for the same Sessions. Refs apache#4032 Generated-by: Kimi k3-256k
Astro-Han
left a comment
There was a problem hiding this comment.
Approve.
Verified the claim that behavior is unchanged, since the swap does move when failures surface. listForRecovery() is listHeaders() plus a discarded readMessagesForRecovery sweep over every Session (session-store.ts:754-760), so dropping it means a corrupt Session no longer fails before the loop starts — it now fails on its own iteration, after earlier Sessions have already run rootAdmissions.recoverSession.
That reordering is not observable: recoverSession (root-admission-owner.ts:63-74) only reads durable admissions and populates in-process maps, with no durable write, and a throw out of prepareHostedExecutionRecovery fails Host startup and takes that memory with it. Fail-closed coverage is genuinely the same set of Sessions, just detected one iteration later.
Also confirmed the change leaves no dead code behind: listForRecovery still has real production callers in execution-composition.ts:1436, stream-graph-coordinator.ts:522, and session-manager.ts:5922, so narrowing this one call site rather than removing the method is correct.
I appreciate that the body stops at the conservative step and flags the per-run readEventsForRecovery/readRuntimeEvents sweep as a separate maintainer decision rather than folding a contract change into a perf PR.
AI-assisted review disclosure: Claude Code traced the call graph and the admission-recovery side effects; I verified the store semantics, the caller set, and the failure ordering against current main myself.
Summary
Refs #4032
prepareHostedExecutionRecoverydecoded every recoverable Session's durable messages twice at Host startup:listForRecovery()performs a discarded per-SessionreadMessagesForRecoverypre-read, and the recovery loop then callsreadMessagesForRecoveryagain per Session (that second read is the one actually used to index and validate admission/message relationships).This PR lists recovery Session headers via
listHeaders()instead, keeping the per-SessionreadMessagesForRecoveryas the single decode. Fail-closed coverage is unchanged: corrupt durable messages still fail startup for the same Sessions, detected at the per-Session read instead of in an upfront sweep.This is intentionally the conservative first step from #4032. The dominant startup cost — the discarded per-run
readEventsForRecovery/readRuntimeEventsvalidation reads over every run of every Session — is left for maintainer discussion, since skipping it for terminal runs changes the startup-validation contract.Verification
biome format/biome lintclean@maka/runtime-hostsuite (npm --workspace @maka/runtime-host run test:dist): 1288 tests, 1279 pass, 0 fail, 9 skipped — includes theexecution-host-recoverycomposition tests that exerciseprepareHostedExecutionRecoveryend to endlistForRecovery()returnslistHeaders()after its discarded pre-read), and the existing recovery suite covers the path. The removed work was a duplicated read whose result no caller consumed.AI use
Tool(s) and scope: Kimi k3-256k — change, verification, and this PR description. The commit carries a
Generated-bytrailer.Checklist
Does this PR entail a change in behavior?
Performance-only: identical recovery outcomes and identical startup failure coverage, one less full message decode pass per Host start.