Fix heartbeat route evicting its own sender (CL-7203) - #477
Conversation
Reproduces CL-7203: a heartbeat that arrives even one second past the 45s timeout is evicted by sweepStale before registry.heartbeat gets a chance to refresh its own lastSeenAt, so the route answers 404 for a client that is actively heartbeating.
sweepStale ran before registry.heartbeat, so staleness was judged against the pre-request lastSeenAt: a heartbeat landing even a moment past the timeout would sweep out the very client that just proved it was alive. Refresh lastSeenAt via heartbeat() first, then sweep.
20ee4b3 to
3963c97
Compare
ReviewConfirmed the fix: Checked the specific risk called out for this reorder: could it let a genuinely stale, different principal survive a sweep it should have been caught by? No — Verified:
No findings. No changes needed. Cross-lane note (not actionable here)CL-7205's |
Summary
POST /rooms/:surface/heartbeatranregistry.sweepStalebeforeregistry.heartbeat, so staleness was judged against the client's pre-requestlastSeenAt. A heartbeat arriving even a second past the 45s timeout window would sweep out the very client that just heartbeated, answering 404 for a principal that is actively alive.Fix: call
registry.heartbeatfirst (refreshinglastSeenAtfor this request), then sweep, then readregistry.states.Test plan
packages/presence/test/routes.test.ts: heartbeat landing 1s past the timeout no longer evicts its own senderpackages/presence/test/routes.test.ts: a heartbeat still sweeps a genuinely stale, different principalWORKBENCH_CHECK_SINCE=origin/main bun run typecheck— cleanWORKBENCH_CHECK_SINCE=origin/main bun run test—@corbits/presence: 70 pass, 0 fail (one unrelated pre-existing flaky timing failure inpackages/chat-ui/test/use-streaming-reply.test.tsx, untouched by this diff)bun run lint— 0 errorsbun run check:structural— all checks ok