Skip to content

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

@hrhrng

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions