ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

qwen-code 安全会话恢复超时机制详解:大规模会话恢复的超时契约、状态仲裁与可观测性设计

qwen-code 安全会话恢复超时机制详解:大规模会话恢复的超时契约、状态仲裁与可观测性设计 qwen-code 安全会话恢复超时机制详解大规模会话恢复的超时契约、状态仲裁与可观测性设计【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code本文基于 qwen-code 仓库中的设计文档 2026-08-07-safe-session-restore-timeout.md 展开并结合 acp-bridge、cli/serve 与 sdk-typescript 等源码实现进行印证。读者可以从中掌握为什么大型会话恢复不能复用旧的初始化超时、服务端/客户端两侧的超时预算如何逐级解析、超时后桥接层如何通过生命周期状态机安全仲裁不可取消的迟到工作以及如何通过结构化错误与阶段级 Trace 定位大规模恢复的性能瓶颈。背景Issue #8678 暴露的恢复超时缺陷qwen-code 的 ACP 桥接层在返回loadSession或unstable_resumeSession响应之前必须先从磁盘重建持久化的 JSONL 会话记录transcript。对于包含大量对话内容的大型会话重建耗时可能远超桥接层原先共享的 10 秒初始化超时即--initialize-timeout-ms的默认值 10000ms从而触发三个连锁问题无法取消底层请求当前 SDK 未暴露 ACP JSON-RPC 请求 ID 与取消操作桥接层无法中断子进程内正在进行的恢复工作迟到工作造成状态污染旧超时路径在超时后停止等待但子进程仍在继续恢复。通道清理可能误杀一个仍持有无关在线会话live sessions的多路复用子进程或者一次迟到的恢复在调用方已经收到错误之后才创建会话与 writer 租约导致调用方收到的错误信息与实际发生的副作用不一致错误分类含糊超时结果被归类为通用初始化超时或裸 500 响应隐藏了具体受影响的会话与恢复阶段排查困难。同时需要澄清一个常见误解已有的8 MiB 实时日志live journal与 4 MiB 压缩回放compacted replay限制并不约束恢复耗时——它们约束的是重建之后的客户端回放client replay规模而不是磁盘上 transcript 的完整读取量。设计文档明确这一差异见 2026-08-07-safe-session-restore-timeout.md 的 Context 一节这也是该问题必须单独解决而非依赖既有限制兜底的原因。设计目标与非目标本次变更要达成四个目标为会话恢复提供独立、可配置的截止时间dedicated configurable deadline让超时错误结构化structured携带会话 ID、动作、预算等字段将不可取消的迟到工作用围栏fence约束在结算settlement之前避免副作用外泄在守护进程daemon与 ACP 子进程两侧提供阶段级 Tracephase-level traces。明确列为非目标的后续工作包括JSONL 解析器流式化、引入 transcript 索引或快照、保证每个大型 transcript 都在 60 秒内完成、以及改变 WebUI 的先分离后加载detach-before-load事务。设计文档还主动披露了两个有意的盲区deliberate gaps值得运维与二次开发者注意Transcript 物化耗时无法单独归因子进程把config_setup记为单一阶段完整的 JSONL 读取与活动链active-chain重建都发生在其内部。JSONL 读取通过argv.resume进入loadCliConfigload 与 resume 处理器都会设置它当聊天记录与 writer 租约启用时config.initialize()还会再做一次权威重载。因此当恢复再次超预算时只会看到config_setup_ms ≈ budget却无法区分成本来自 transcript 还是运行时配置回归。Issue #8678 的 P0 要求把 transcript 读取/索引与活动链重建拆分为独立阶段本次变更只交付了更粗的分割transcript/配置工作 vs 认证、注册与回放大型恢复期间的兄弟会话sibling延迟未被测量文档断言兄弟会话保持可用有测试支撑但未证明在多百兆字节 transcript 于共享子进程事件循环上重建时它们仍能保持响应。所幸现成的仪器已存在——子进程运行事件循环滞后监视器event-loop lag monitor其快照携带 mean、p50、p99 与 max——用一个足够慢、足以跨越截止时间的 fixture 即可在一次运行中同时补齐这项测量与未测试的空通道预算提升。超时契约服务端预算解析顺序服务端qwen serve的会话恢复预算按以下顺序解析实现位于 session-restore-timeout.ts 的resolveSessionRestoreTimeoutMssessionRestoreTimeoutMs/--session-restore-timeout-ms显式配置拥有最终决定权——包括低于默认值的取值。这服务于有意让恢复快速失败的部署场景否则取默认值 60,000 ms当显式提供了更大的initializeTimeoutMs/--initialize-timeout-ms时提升为该值。关键不变量启动预算可以抬高恢复预算但永远不能压低它。二者衡量的工作不同——一个部署为了收紧子进程初始化检查而调低--initialize-timeout-ms绝不能因此静默继承一个低于默认值的恢复截止时间否则就重新引入了 Issue #8678 要消除的问题。这一不变量在 session-restore-timeout.test.ts 中有专门的回归测试never lets an initialize timeout lower the restore budget断言initializeTimeoutMs: 10_000时仍返回 60,000 默认值。参数校验与边界值assertValidTimeoutMs强制校验必须是正整数且不超过2^31-12,147,483,647即 JavaScriptsetTimeout的最大延迟上限MAX_SESSION_RESTORE_TIMEOUT_MS。测试用例的非法值集合包括0、-1、1.5、NaN、Infinity以及MAX 1。边界值本身是承重的scheduled-task-keepalive把ms MAX视为仍然计时而server.ts用MAX 1作为禁用哨兵disable sentinel边界差一就会在启动时拒绝合法的最大setTimeout延迟。守护进程会把生效值作为可选字段limits.sessionRestoreTimeoutMs同时发布到 bootstrap 与运行时 capabilities见 capabilities.ts。CLI 参数一览在 serve.ts 中参数类型默认值说明--initialize-timeout-msnumber10000ACP 子进程请求超时含 initialize 握手--session-restore-timeout-msnumber60000ACP 会话 load/resume 超时显式--initialize-timeout-ms只能抬高此默认值不能压低# 示例把会话恢复预算提高到 120 秒若初始化预算更大则取初始化预算 qwen serve --session-restore-timeout-ms 120000 # 示例让恢复快速失败显式低值优先于初始化预算 qwen serve --session-restore-timeout-ms 5000 --initialize-timeout-ms 90000超时契约TypeScript SDK 与 WebUI 侧的派生逻辑SDK 侧sdk-typescriptTypeScript SDK 在 DaemonClient.ts 中按以下优先级选择恢复超时每请求timeoutMs显式的全局fetchTimeoutMs缓存的服务器 capability 值 10 秒兜底 70 秒DEFAULT_SESSION_RESTORE_TIMEOUT_MS 70_000。注意两个细节每请求传timeoutMs: 0会禁用客户端计时器且不参与序列化调用capabilities()会刷新缓存的静态预算但一次恢复操作不会隐式发起 capability 请求。capabilities()对limits.sessionRestoreTimeoutMs做了合法性过滤正整数、 MAX_TIMER_DELAY_MS才采纳。WebUI 侧web-shellWebUI 的派生逻辑在 actions.ts 中const DEFAULT_RESTORE_SERVER_TIMEOUT_MS 60_000; const RESTORE_REQUEST_HEADROOM_MS 10_000; const RESTORE_WATCHDOG_HEADROOM_MS 15_000; const ATTACH_WATCHDOG_TIMEOUT_MS 30_000; const MAX_TIMER_DELAY_MS 2_147_483_647;SDK load/resume 请求超时 服务器预算 10 秒WebUI 自身看门狗 服务器预算 15 秒capabilities 缺失时分别回退到 70 秒与 75 秒会话附加attach保留既有的 30 秒看门狗。若推导出的延迟超过 JavaScript 计时器上限MAX_TIMER_DELAY_MS客户端计时器会被禁用返回timeoutMs: 0/watchdogTimeoutMs: undefined避免 Node 把超大延迟压缩为约 1 毫秒。恢复所有权与终态仲裁inFlightRestores状态机桥接层的核心数据结构inFlightRestores见 bridge.ts同时拥有两类 Promise 与多项资源公开 Promise返回给调用方结算 Promisesettlement promise代表真实的 ACP 请求加清理工作会话 ID 围栏与恢复容量记账。正常成功时注册会把容量转移给在线会话并可能释放准入admission放弃abandonment之后围栏、准入与 in-flight 容量会一直保留到真实结算完成防止无限超额订阅unbounded oversubscription。生命周期由显式标志lifecycle.phase仲裁截止时间与 ACP 结果——谁先观察到active谁就改变公开终态从而避免在时间相等边界上依赖Promise.race回调顺序。状态含义与允许的迁移active同动作、同形状的请求合并coalesce。ACP 成功可注册会话失败可正常返回。abandoned截止时间胜出。公开调用方收到结构化超时该 ID 从 pending restore 事件中移除并围栏化阻止迟到的子进程通知。同 ID 重试收到restore_in_progress带reason: awaiting_abandoned_cleanup与由恢复预算派生的重试提示——被钳制在至少 5 秒、至多 120 秒——而非固定 5 秒。overdue又经过一个恢复预算仍无结算。既有会话与工作区控制仍可用但新的 spawn/load/resume/branch 工作被拒绝以便通道排空——关闭传输是释放永久挂起请求的唯一杠杆。只要仍有在线兄弟会话通道就绝不会被强制杀死。cleanup迟到的 ACP 成功或失败触发恰好一次子进程qwen/control/session/close。资源不存在resource-not-found本就是干净状态。quarantined清理结果不确定。既有会话与工作区控制可用但新工作被拒绝直至通道排空。settled关闭成功、资源不存在或传输已关闭。恢复准入、容量槽位与 in-flight 条目全部释放通知围栏保留到该 ID 有新的注册尝试取得所有权或通道退出。放弃行为保留所有权但并非无限期overdue限定了挂起恢复可以占据准入槽位、in-flight 条目与仅此可释放的会话 ID 围栏的时间窗宽限期过后通道停止接收新工作由常规排空路径回收子进程。既不能在隐藏工作仍在运行时释放容量会导致无限超额订阅也不能强杀仍持有在线兄弟会话的通道会重新引入本次变更要消除的故障——因此二者都不做。围栏没有 TTL保留至通道退出或新注册尝试显式接管。调用方提供的 spawn 与 restore 会在等待通道设置之前同步预留所有权因此反方向竞争不可能对同一 ID 执行两次 ACP 注册。所有权转移发生在 ACP 注册调用之前以便合法的启动通知可以被缓冲一次失败的尝试会清除这些帧并恢复常规的 60 秒 closed-session 墓碑tombstone。通道安全超时后的排空、隔离与回收策略超时发生时见 bridge.ts 中inFlightRestores与unsettledAbandonedRestores相关逻辑桥接层把 ID 从pendingRestoreIds移除、清除其早期事件并记录到通道的unsettledAbandonedRestores若通道没有在线会话、挂起 spawn、其他 restore 或工作区控制操作则同步标记为 dying并异步杀死子进程——504 响应不等 TERM/KILL 升级外层qwen serve进程继续运行若存在任何兄弟工作子进程存活。迟到的清理失败会设置独立的隔离位quarantine bit而不设置isDying兄弟会话的 attach、prompt、close 与工作区控制照常新工作在容量检查之前被拒绝因此稳定错误是acp_channel_unavailable而非碰巧的会话上限响应。可见工作排空后子进程被回收后续请求可创建干净通道。通道是否被判死刑是派生而非粘性的derived, not sticky当可见工作排空且以下任一条件仍成立时才会被收割——常规的 pending empty reap、非空unsettledAbandonedRestores、或隔离状态。真实结算会从该集合移除 ID因此一个迟到恢复已落地并干净关闭的通道会回到配置的空闲通道策略而不是被一次已恢复的超时强制冷启动。只要恢复确实未决排空就是关闭传输的唯一方式——也是打破永久挂起请求的唯一杠杆。关闭流程会等待每个真实结算 Promise但每个 ACP 请求都与channel.exited竞速——杀死通道即可释放关闭流程即使 SDK 请求 Promise 本身永不结算。所有放弃结果处理器都是拒绝安全的rejection-safe。错误契约结构化超时与通道不可用SessionRestoreTimeoutError恢复超时SessionRestoreTimeoutError继承自BridgeTimeoutError见 status.ts携带sessionId与action: load | resume。它虽然继承自超时错误基类但优先被归类为restore_timeout错误分类映射见mapDomainErrorToErrorKindrestore_timeout是SERVE_ERROR_KINDS枚举成员之一。REST返回 HTTP 504带由预算派生的Retry-After钳制在 5–120 秒见restoreRetryAfterSeconds的实现 session-restore-timeout.ts响应体含code、errorKind、retryable、sessionId、action、timeoutMs字段ACP HTTP / WebSocket返回 JSON-RPC-32603携带相同 data 字段另加httpStatus: 504与retryAfterSeconds。REST 映射实现在 error-response.ts504 restore_timeout分支重试提示的钳制语义有参数化单元测试60,000ms 预算 → 60 秒20ms 预算 → 下限 5 秒300,000ms 或MAX预算 → 上限 120 秒。设计意图是围栏、504 与隔离状态都持续约一个恢复预算远超常规 5 秒节奏若仍通告 5 秒会让客户端对着一个无法自行清除的状态空转。acp_channel_unavailable通道不可用对关闭了新工作的通道请求被拒绝为 HTTP/ACP 503code与errorKind均为acp_channel_unavailablereason区分两种成因restore_cleanup_failed超时恢复的清理失败子进程状态未知restore_settlement_overdue放弃的恢复在截止时间之后又过了一个恢复预算仍未结算。可观测性阶段级 Trace 与失败归因桥接层为每次恢复创建 restore span并始终把其 trace context 注入 ACP load/resume 元数据记录 action、通道 ID、会话 ID、配置预算、公开结果、迟到结果与清理/隔离结果不含 transcript 内容。子进程延续该上下文指标名为qwen-code.daemon.session_restore记录以下阶段的耗时设置加载settings load在线恢复或持久化存在性检查live restore / persistence existence checkconfig setup注意此处同时包含完整 JSONL 读取与活动链重建是文档点名的归因盲区认证authentication文件系统设置filesystem setup会话注册/回放session registration / replay响应构造response construction。首个失败的阶段被记录为failed_stage。这意味着当一次恢复再次超预算时你会在 Trace 中看到类似config_setup_ms ≈ budget的模式——运维者可据此判断成本大概率落在 transcript 读取或运行时配置再结合事件循环滞后监视器的 mean/p50/p99/max 快照进一步定位。验证矩阵单元测试、E2E 与手动基准单元测试fake timers 确定性延迟子进程bridge.test.ts 与 session-restore-timeout.test.ts 覆盖了设计文档承诺的确定性场景空通道收割、兄弟会话存活、同 ID 围栏、迟到关闭恰好一次exactly-once、隔离排空与恢复、容量保留、传输关闭仲裁、挂起请求的关闭释放。独立的断言还包括REST 与 ACP 映射、capability 传播、SDK 传输中止行为、WebUI 计时器派生、Trace 传播与失败阶段归因。E2E 与手动基准E2E 计划使用延迟子进程验证公开截止时间与进程存活。约 80 MiB 的 transcript 只是手动基准与 Trace 夹具——CI 不断言机器相关的延迟指标避免把性能断言绑定到具体硬件。实践要点速查调优入口服务端用--session-restore-timeout-ms默认 60000它优先于--initialize-timeout-ms默认 10000只能抬高不能压低恢复默认值SDK 侧每请求timeoutMs优先于全局fetchTimeoutMs再次是capability 10 秒兜底 70 秒能力发现生效值通过limits.sessionRestoreTimeoutMs发布在 bootstrap 与 runtime capabilitiesSDK/WebUI 据此派生各自的请求超时与看门狗错误识别超时是 504 errorKind: restore_timeoutREST或 JSON-RPC-32603httpStatus: 504ACP清理失败/结算超期导致的新工作拒绝是 503 acp_channel_unavailable用reason区分restore_cleanup_failed与restore_settlement_overdue重试节奏Retry-After与retryAfterSeconds由恢复预算派生并钳制在 5–120 秒别再用固定 5 秒对持久状态空转排障观察qwen-code.daemon.session_restorespan 的阶段耗时与failed_stageconfig_setup阶段偏高且接近预算时需结合事件循环滞后监视器区分 transcript 读取与运行时配置成本。【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表