# 3-14 Rust Web Tree Realtime WS Push 替代 SSE 轮询 v1 > 创建时间:2026-05-17 > 状态:`done` > 关联 commit:`57ec8322 feat(mnote-web): replace SSE pollMs=1000 polling with WebSocket push for tree realtime events` > > 前置设计稿: > - `design/03-rust-web/process/3-3-rust-web-tree-realtime-event-stream-v1.md` — SSE 事件流原始设计(本稿覆盖其 realtime transport 部分) > - `design/10-review/process/08-kernel-architecture-next-priority-review-and-checklist.md` — 3.4 Tree Realtime Live Cache checklist ## 1. 结论 **`/api/tree/events` 的 SSE 轮询已替换为 WebSocket 推送 + SSE 降级。** 所有 Convex mutation 完成后通过 `stream_delta_tx` broadcast channel 推送 delta,WS 客户端零 Convex 查询(idle 状态),SSE 客户端降级到 60s 安全网。Convex RSS 从 7.7G 降至正常水平,每页 idle 时 0 POST /api/query/min。 ## 2. 问题 ``` Convex backend RSS: 7.7G POST /api/query/min: ~17/min 32h 内累计: 32K+ Convex queries 74% 来自 SSE pollMs=1000 轮询 ``` SSE `/api/tree/events?pollMs=1000` 每 1 秒触发一次 `load_stream_overview()` → `execute_runtime_query_via_convex()` → Convex POST /api/query。无论 workspace 状态是否变化,每个 cycle 都产生一次 Convex 查询。两个浏览器 tab 开同一 workspace 时,查询量翻倍。 ## 3. 方案 ### 3.1 Server 端 | 组件 | 文件 | 改动 | |------|------|------| | **Broadcast channel** | `app.rs` | `AppState` 新增 `stream_delta_tx: broadcast::Sender`(capacity 128) | | **Command hook** | `command_support.rs` | `execute_runtime_command_via_convex_with_artifacts` 签名从 `(&AppConfig, ...)` 改为 `(&AppState, ...)`,mutation 成功后 `stream_delta_tx.send({"kind":"command_committed", ...})` | | **WS handler** | `ws.rs` | `handle_socket` 用 `tokio::select!` 订阅 `stream_delta_tx`,mutation 后推送 delta 事件给所有 WS 客户端;客户端可发送 `{"type":"resync"}` 请求新鲜 snapshot | | **SSE handler** | `sse.rs` | 同时订阅 `block_delta_tx` 和 `stream_delta_tx`;broadcast 可用时进入 push-only 模式(250ms heartbeat,无 Convex query),poll 降级为 60s 安全网 | | **Call site updates** | 17 处 | `documents.rs`、`mindmap_api.rs`、`resource_trash.rs`、`tree.rs`、`hermes_tools/{artifact,block,page}.rs` 全部 `state.config()` → `&state` | ### 3.2 Client 端 | 组件 | 文件 | 改动 | |------|------|------| | **Bootstrap config** | `layout.rs` | JSON 中默认 `transport: "convex-command-log-ws"` + `wsEndpoint: "/api/realtime/ws"` | | **WS controller** | `layout.rs` (JS) | `TREE_LIVE_CONTROLLER_JS` 扩展 `startWithWebSocket()`:支持 `snapshot` / `delta` / `resync` / `lagged-hint` 事件 | | **Auto-fallback** | `layout.rs` (JS) | WS 2s 内无法连接时自动回退到 SSE | ### 3.3 传输对比 | | SSE poll(旧) | WS push(新) | |---|---|---| | Convex 查询 | 每 1s 一次,idle 不降 | 0(仅 mutation 后 push delta) | | 延迟 | max 1s | 近实时(< 50ms) | | 连接 | HTTP long-poll | 单 WebSocket | | Convex RSS 影响 | ~17 Q/min → 7.7G | 0 Q/min idle | | 回退 | — | WS 不可用时降级到 SSE(60s 安全网) | ## 4. 对其他设计稿的影响 ### 4.1 需更新口径的文档 | 文档 | 旧口径 | 新口径 | |------|--------|--------| | `3-3-rust-web-tree-realtime-event-stream-v1.md` | SSE `/api/tree/events?pollMs=1000` 是主 transport | SSE 降级为 fallback;WS `wss://.../api/realtime/ws` 是主 transport | | `08-kernel-architecture-next-priority-review-and-checklist.md` | 3.4 中提到 `/api/tree/events` snapshot/delta/resync 覆盖 | 增加 WS push 验证项 | | `AGENTS.md` | `3000` 主壳接入 Rust Web `/api/tree/events` SSE consumer | 更新为 WS consumer(SSE 仅回退) | | `4-24-resource-tree-filetree-pagetree-source-contract-checklist-v1.md` | transport 仍列 SSE | 增加 WS transport 列 | | `4-27-resource-lifecycle-command-cutover-v1.md` | SSE delta 路由描述 | 更新为 WS push delta | ### 4.2 不受影响的文档 | 文档 | 原因 | |------|------| | `4-6-tree-command-protocol-cutover-stage2-v1.md` | 只涉及 command 协议,不涉及 transport | | `05-editor-mainline` 系列 | 编辑器不依赖 tree transport | | `07-ai` 系列 | AI 工具不依赖 tree transport | ## 5. 验证 ```bash # 编译 + 单测 cargo build --manifest-path rust/Cargo.toml -p mnote-web cargo test --manifest-path rust/Cargo.toml -p mnote-web # 浏览器 smoke # 1. 打开 http://localhost:3000 → F12 Console # 2. 检查:transport=convex-command-log-ws, status=connected # 3. 等 2min → Convex 日志 0 新增 POST /api/query # 4. 另一 tab 新建页面 → 首 tab 无需刷新,页面树/文件树自动更新 # 回归 smoke(双浏览器) node scripts/task446-tree-rename-dual-browser-live-smoke.js node scripts/task447-tree-move-order-dual-browser-live-smoke.js node scripts/task448-tree-resync-recovery-dual-browser-smoke.js node scripts/task449-tree-sse-reconnect-snapshot-recovery-smoke.js ``` ## 6. 后续 - [ ] 完全移除 SSE poll 路径(当前仍保留 60s 安全网) - [ ] WS 连接池管理(多 tab 共享一条 WS) - [ ] Client 端 WS reconnect 指数退避