Skip to content

Bridge daemon stuck on WS attach 409 — RuntimeRoom holds zombie daemon socket #29

Description

@hrhrng

Symptom

oma bridge daemon 持续打印 WS attach failed: HTTP 409 后退避重连,永远 attach 不上 RuntimeRoom。Daemon 进程活着但从 console / API 角度 runtime 显示 offline。

复现日志:
```
! WS attach failed: unexpected response: HTTP 409
→ reconnecting in 8000ms
! WS attach failed: HTTP 409
→ reconnecting in 16000ms
... (每次 408/409,无限循环)
```

根因(推测)

`apps/main/src/runtime-room.ts:63-82` `attachDaemon`:
```ts
const existing = this.ctx.getWebSockets("daemon");
if (existing.length > 0) {
try {
existing[0].send(JSON.stringify({ type: "ping" }));
return new Response("daemon already attached", { status: 409 });
} catch {
try { existing[0].close(1011, "stale"); } catch { /* already closing */ }
}
}
```

CF Hibernatable WebSocket 的 `send()` 不会真验证 TCP 活性 —— 只要 socket 没收到 close,DO 视角就认为它活着。客户端那侧 TCP 早断了(network blip / 重启),但 DO 永远收不到 close → `send()` 不抛 → 永远 409 reject 新 attach。

影响

  • daemon 重启后无法重新 attach(除非 console 上手动 `oma runtime rm` + 重 setup)
  • 静默服务降级:用户端看 daemon 进程在跑、status 探测 OK,但实际 console 显示 offline,触发 turn 没人响应

修复思路(待讨论)

A. `attachDaemon` 收到 409 前先发 `ping` 等一个 `pong` 超时(比如 3s)才判定 stale;超时则 `close()` 旧 WS、accept 新 WS。
B. Daemon 侧定期 ping,DO 在 `webSocketMessage` 收到的最后时间超过阈值(比如 60s)就认为旧连接死了,新 attach 直接踢掉旧的。
C. 新 attach 一律 evict 旧 WS("last writer wins"),不再 409。最简但失去对 split-brain 的保护。

倾向 B + 后备 C。

Out of scope

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions