Continuous Client connection recovery
Client 连接持续恢复
A generation source can remain pending without reporting readiness or carrier failure. A warning alone leaves the Client waiting indefinitely. Finite automatic retries also leave a page disconnected after the Host recovers from a longer outage, even though the browser's network status never changes.
English
Problem
A generation source can remain pending without reporting readiness or carrier failure. A warning alone leaves the Client waiting indefinitely. Finite automatic retries also leave a page disconnected after the Host recovers from a longer outage, even though the browser's network status never changes.
Decision
ConnectionController owns both the readiness deadline and the continuous retry schedule. A handshake reports a slow Host after three seconds and aborts after fifteen seconds by default. The warning gives early feedback without discarding a Host that needs several seconds to become ready; the deadline bounds each attempt and logs the timeout when it cancels the generation. Warning and cancellation times are independent, so a shorter hard deadline does not require changing both fields; a warning scheduled after settlement is cancelled. Cancellation reaches the generation source, which must release its resources and settle before another source starts. A cancelled source's late ready callback cannot establish a generation.
Retry caps grow from 500ms through 1s, 2s, 4s, and 8s to 10s, with the existing 50–100% jitter. Failures at the maximum cap continue retrying. Separating a maximum delay from a retry-count limit follows the distinction in Socket.IO's Client options, while DSH retains its existing Remote stream protocol and single scheduler. Gateway replaces the physical socket once for each Controller-requested attempt. Both a pending WebSocket candidate and an open socket without an opening ready frame can recover this way.
The Host Connection plugin validates recovery in its configuration and injects the resolved, non-secret timing into each page through webserver/index-inject. The Client validates that bootstrap input before providing Connection; direct loop options may override it. Timer values must be positive integers within the browser timer range, and the backoff factor must be finite and at least one. The shared resolver explicitly rejects NaN, which range comparisons alone cannot exclude. A factor of one selects continuous fixed-cap retries. Changes to Host timing apply to subsequently loaded pages.
The Settings indicator labels active recovery Reconnecting and keeps Reconnect now available. This decision supersedes the terminal retry policy in Web connection recovery control. That note still owns manual recovery, browser offline suspension, the single-scheduler rule, and indicator presentation. A fresh $events ready frame alone establishes connectivity; domain streams retain their own baseline and cursor recovery.
Alternatives considered
Only reject the readiness wait. The Controller still waits for source settlement before retrying. The deadline must cancel the source as well, otherwise the same pending work blocks recovery.
Cancel every handshake after three seconds. This conflates slow-Host feedback with failure and repeatedly discards legitimate slower handshakes. Separate configurable warning and cancellation times preserve a bounded opportunity to finish.
Stop after a finite retry sequence. A page cannot discover later Host recovery without another user or browser event. Capping the interval limits traffic while preserving automatic recovery.
Start another source without awaiting cancellation cleanup. Overlapping generations can retain listeners and deliver obsolete events. The source's cancellation contract requires settlement; abandoning it does not establish cleanup.
Consequences
Long outages retain one retry schedule and produce bounded-rate connection traffic until recovery, explicit stop, or browser offline suspension. A permanently invalid credential still requires user action; connection retries do not refresh credentials or replay unary mutations. Immediate readiness continues to reset backoff, and browser offline remains authoritative for suspension; stable-connection reset windows and local-transport exceptions are separate policy changes.
Testing
Controller tests cover recovery beyond the former final tier, fixed-cap retries, slow readiness, deadline cancellation, delayed cleanup, late ready callbacks, and manual reconnect or stop during a handshake. Host and Client tests cover timing propagation, invalid input, and injection disposal. Gateway tests use the real Controller and event pump with scripted WebSockets to verify both stalled opening phases, physical replacement, and one recovery reset. The recorded-session Web lifecycle scenario covers automatic recovery past the former stop point, manual replacement of a stalled handshake, and localized recovery presentation.
中文
问题
generation source 可能一直挂起,既不报告就绪,也不报告载体失败。仅告警会让 Client 无限等待。有限次数的自动重试还会使页面在 Host 从较长中断恢复后继续保持断连,即使浏览器网络状态从未变化。
决策
ConnectionController 同时拥有握手就绪期限和持续重试调度。默认情况下,握手在三秒后报告 Host 响应缓慢,在十五秒后中止。告警提供早期反馈,同时保留需要数秒才能就绪的 Host;硬期限限制每次尝试的时长,并在取消 generation 时记录超时。告警与取消时间相互独立,因此缩短硬期限不需要同时修改两个字段;排定在握手结束之后的告警会被取消。取消会传到 generation source,后者必须释放资源并结束,下一 source 才能启动。已取消 source 迟到的 ready 回调不能建立 generation。
重试上限从 500ms 开始,经过 1s、2s、4s、8s 增长到 10s,保留现有 50–100% 抖动。达到最大上限后失败仍继续重试。把最大延迟与重试次数上限分开,与 Socket.IO Client 选项的区分一致;DSH 保留自身的 Remote stream 协议和唯一调度器。Controller 每要求一次尝试,Gateway 就替换一次物理 socket。仍在等待打开的 WebSocket 候选,以及已打开但未收到首个 ready 帧的 socket,都能通过此路径恢复。
Host Connection 插件校验配置中的 recovery,通过 webserver/index-inject 把已解析且不含秘密的时序数据注入每个页面。Client 在提供 Connection 之前校验启动输入;直接传给循环的选项可覆盖这些值。定时器值必须是浏览器定时器范围内的正整数,退避因子必须是至少为一的有限数。共享解析器显式拒绝仅靠范围比较无法排除的 NaN。一表示以固定上限持续重试。Host 时序配置的变更适用于随后加载的页面。
Settings 指示器把活动恢复标为自动重连中,并始终提供立即重连。本决策取代Web 连接恢复控制中的终态重试策略。该记录仍拥有手动恢复、浏览器离线暂停、唯一调度器规则与指示器展示。只有新的 $events ready 帧才能建立连接状态;各域的 stream 保留各自的 baseline 与 cursor 恢复方式。
考虑过的替代方案
仅拒绝就绪等待。 Controller 仍会等待 source 结束才重试。硬期限必须同时取消 source,否则同一个挂起的工作仍会阻塞恢复。
三秒后取消每次握手。 这会把 Host 响应缓慢的反馈等同于失败,反复丢弃合法的较慢握手。分别配置告警与取消时间,可在有界时长内保留完成机会。
有限序列结束后停止重试。 没有后续用户或浏览器事件,页面就无法发现 Host 已恢复。对间隔封顶可以限制流量,同时保留自动恢复。
不等待取消清理就启动另一个 source。 重叠的 generation 可能保留 listener 并投递过时事件。source 的取消契约要求它结束;放弃等待不能证明清理完成。
后果
长时间中断期间保留唯一重试调度,并以有界频率产生连接流量,直到恢复、显式停止或浏览器离线暂停。永久无效的凭据仍需用户处理;连接重试不会刷新凭据,也不会重放一元修改操作。立即就绪仍会重置退避,浏览器离线仍决定暂停;稳定连接后的重置窗口和本地载体例外属于独立策略变更。
测试
Controller 测试覆盖超过原终档后的恢复、固定上限重试、慢就绪、期限取消、延迟清理、迟到 ready,以及握手期间手动重连或停止。Host 和 Client 测试覆盖时序传递、无效输入及注入销毁。Gateway 测试使用真实 Controller 和事件 pump,配合可编排的 WebSocket,验证两个握手挂起阶段、物理替换及唯一恢复 reset。录制 Session 的 Web 生命周期场景覆盖超过原停止点后的自动恢复、手动替换挂起握手,以及本地化恢复展示。