Repository navigation
fix(client): reconnect transient stalls instead of replacing the service (#52049) - #52733
JerryLiu369 wants to merge 1 commit into
Conversation
|
The following comment was made by an LLM, it may be inaccurate: |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
|
Could you retarget this to One recovery boundary I checked on unchanged client sources matching current |
|
@r266-tech Good catch, and you're right on both counts: the fix was developed against v2 code (packages/client only exists on the v2 line) but the PR was opened against dev, hence the 7512-file diff. Retargeted base to v2 — the diff is now the intended 7 files / 1 commit. Thanks for verifying the recovery boundaries on v2 (12810e8). |
Issue for this PR
Closes #52049
Type of change
What does this PR do?
On Windows the v2 managed service (
opencode serve --service) was being killed and respawned during normal load, aborting every in-flight session and background subagent. Two client-side defects caused a transient stall to escalate into a full service replacement:Event-stream idle watchdog margin too small. The server writes a keepalive comment on
/api/eventevery 15 s, but the client aborted the stream after 45 s without a byte (defaultIdleTimeout = 45_000) — only three missed keepalives. Under load (watcher re-registration churn, many cold-booting locations) a 45 s gap is easy to hit, so the client aborted a healthy stream withEvent stream stalled.Service eviction threshold too eager. After the stalled stream, the client only tolerated three consecutive probe timeouts (
timeouts.count >= 3) before declaring the service unresponsive and replacing it (Background service is unresponsive; recovery cannot preserve persistent terminals) instead of reconnecting. A few slow probes were enough to tear down a live service and kill its sessions.This PR widens both margins:
defaultIdleTimeout45 s → 90 s (six missed keepalives), so a transient stall triggers the normal abort-and-reconnect path rather than looking like a dead stream. The doc comment now explains the keepalive-derived budget.packages/client/src/effect/service.ts) and Promise (packages/client/src/promise/service.ts) variants. Transient Windows probe stalls now reconnect; only sustained unresponsiveness evicts.No public API or type changes; both margins remain overridable via
idleTimeout/EnsureTiming.How did you verify your code works?
keeps a registered service that recovers after transient probe stalls(both Effect and Promise service suites) asserts a service that stalls on early probes but recovers is kept (same pid/url, process still alive), and updated the eviction test to expect five probes before replacement.the default idle timeout spans several server keepalivesinsolid-connection.test.ts, pinningdefaultIdleTimeout >= 6 * 15_000so the watchdog can never again be tightened below the server's keepalive budget.flakymode.Ran
bun testinpackages/client:The 4 failures are pre-existing in this worktree and unrelated to this change (
exposes every standard HTTP API group,session methods use the public HTTP contract,session methods retain decoded Effect inputs and outputs,public import boundaries— all fail identically on the unmodified parent commit, caused by a stale symlinkednode_modulesin the factory worktree; nobun installwas run). The 174 baseline passing tests still pass, plus the 3 new tests.Screenshots / recordings
N/A — client/service logic and unit tests; no UI surface.
Checklist
bun testfor the affected packages