Repository navigation
Description
Description
A LogService Append can remain on a TCP connection to an isolated replica until the caller's full 1-minute deadline, even while the other two Raft voters are reachable and advancing the log. Retrying then succeeds, but the first timed-out Append may already have committed and can occupy a second physical LSN.
Environment
- MatrixOne
upstream/mainatd61a7206c16b30baa4efc525eb6067c4364035aa. - Three isolated LogService processes, three voting replicas of data shard 1 and HAKeeper shard 0, distinct data directories and Linux network namespaces on test host
10.222.1.51. No TN/CN was started. - Default
log-store-timeout = "5m". A direct three-address LogService client ran sequential unique-payload Append → Read checks with a 1-minute deadline per attempt and a 3-minute retry continuation window, matching the tested MO WAL retry timing. - Reproducer and analysis tools: test/logservice-chaos.
Reproduction
- Start the three-node isolated cluster and continuous
chaos-client -op cycle -count 0 -try-timeout 1m -retry-window 3magainst all three RPC addresses. - Use
ss -tnpon the test host to identify the replica to which this client currently holds an established TCP connection. Confirm all three voters are present. - Run
netns.sh <cluster-root> down <connected-node-index>for 75 seconds, keeping the other two nodes running and mutually reachable; then runnetns.sh <cluster-root> up <connected-node-index>. - After the three-member view returns, inspect the client's new connection and repeat for other connected replicas. Read back the retained range from each address and compare LSN/payload.
The key trigger is isolating the client's existing connection. Isolating an arbitrary other replica is not equivalent.
Actual Result
Three consecutive injections against connections to node1, node2 and node0 reproduced the same long tail (3/3). The affected seq=79/235/378 Append attempts returned context deadline exceeded after 60001.127 / 60000.663 / 60001.011 ms. The corresponding logical Append → Read cycles completed after 60.505 / 60.520 / 61.325 s on retry. The surviving nodes' latest LSN continued advancing throughout.
All 491 logical cycles eventually succeeded; three-address reads matched and every acknowledged LSN/payload was verified. Each timed-out first Append had in fact committed: the same payload appeared at LSN 3263 and 3267, 3423 and 3427, and 3570 and 3573, respectively. These are physical LogService duplicates; this test did not run TN replay and does not establish duplicate SQL execution.
Expected Result
With two reachable voters, the client should detect an unusable established RPC connection and retry another replica without routinely consuming the caller's entire 1-minute attempt deadline. A timeout must still be treated as an unknown Append result; any fix or retry must preserve reconciliation/idempotency semantics.
Code Evidence and Scope
pkg/logservice/client.go passes read timeout 0 to getRPCClient, which sets morpc.WithBackendReadTimeout(readTimeout). client.request waits on future.Get(); managedClient.Append resets its RPC client only after an error returns. This is consistent with the observed wait, but a connection-level trace is needed to attribute every millisecond.
Full timeline, reproduction commands and retained-data analysis: LogService three-node failure report. Raw evidence is retained on the test host under /home/mo/jxm/logservice-chaos-fresh-20261104/tail-repeat-a-20261104.*.
Related #29687 reports production LogService latency with an undetermined root cause; this issue records a controlled, repeatable connection-isolation mechanism. Search of existing issues for LogService Append 60-second timeout and this exact mechanism found no duplicate.
Activity
jiangxinmeng1 commentedon Nov 4, 2026
Follow-up: Read also waited for the full per-attempt timeout after leader crash (2026-11-04)
On a fresh three-voter cluster with distinct leaders (HAKeeper shard 0 on node0; data shard 1 on node1), I killed the data leader node1 just after its exported snapshot was renamed into the final directory. node0/node2 stayed online and mutually reachable; data leadership moved to node2. A separate client connected only to those two survivors completed 100/100 Append→Read cycles with zero retries and a 0.550-second maximum.
Truncate(150)also succeeded with node1 down.The original three-address client had already acknowledged Append for
seq=46at LSN 158. Its subsequent Read on the existing connection waited 60,000.820 ms before returningEOF; the retry then read and verified the record. That whole Append→Read cycle took 61.623 seconds. This extends the issue's observed one-minute tail to Read. I have not traced this Read's exact MORPC backend state, so I am reporting the matching client-visible failure mode, not claiming a separately proven internal cause.The full run ended 555/555 cycles successful, with zero Append retries and one Read retry. After restart, all three addresses returned the original members and identical retained reads; 347 confirmed retained records matched by LSN and payload. Evidence: test report and
/home/mo/jxm/logservice-chaos-snapshot-leader-20261104/leader-snapshot-crash.loadon the test host.jiangxinmeng1 commentedon Nov 4, 2026
Controlled Read timeout comparison (2026-11-04)
I added a test-only pause after a successful Append and before Read in the same LogService client. For each run,
ss -tnpidentified its established TCP peer; I isolated that peer's test veth, then released Read while the other two voters stayed connected. This removes ambiguity about whether the failure occurred during Append or Read. No production timeout setting was changed.context deadline exceededafter 2000.734 mscontext deadline exceededafter 2000.961 mscontext deadline exceededafter 60000.428 msAll three Appends had succeeded before isolation; each Read succeeded on its second attempt. The cycle's overall time includes the intentional pause and must not be used as the failure latency. A separate healthy 2-second-timeout control completed 150/150 Append→Read cycles with zero retries (maximum cycle 638.310 ms). After network restoration, all three addresses returned identical data, including the three tested payloads at LSN 789, 945, and 949.
Code path: LogService
managedClient.Readresets its connection on RPC error but internally retries only shard-not-found errors;EOF/deadline errors return to the WAL read loop. The TN WALreadFromBackendloop gives each attemptDefaultOneTryTimeout = 1mand retries afterward. MORPC backend read timeout is configured as zero for this data client. Therefore a blackholed established connection can consume the full caller deadline before retrying, as this test observed. The 2-second setting is an experimental caller timeout, not a production recommendation based on a full latency study.Report and reproducible test hook: test report, raw logs under
/home/mo/jxm/logservice-chaos-snapshot-leader-20261104/on the test host.jiangxinmeng1 commentedon Nov 4, 2026
补充真实 TN WAL driver 路径的稳定复现(没有启动 TN/CN):
logservicedriver.NewLogServiceDriver、ReplayForWrite、Append和Entry.WaitDone,按真实默认DefaultOneTryTimeout = 1m写入。测试代码:tn_driver_probe。ss -tnp确认 driver 进程已连接的副本,再将该副本SIGSTOP70 秒,另两台保持运行。第二轮明确确认目标 node1 是普通副本,数据 leader 在 node2。WAL-Commit-Append都记录内部重试 1 次,最终成功。目标副本仍暂停时,driver 已分别继续推进到 DSN 724/848,说明存活两票可以服务。这证明 #32133 的 60 秒长尾也会直接影响 TN WAL driver,而不只是复刻超时配置的测试客户端。
pkg/logservice/client.go建连时传WithBackendReadTimeout(0),已有连接直到请求返回错误才重置;driver 每次 Append 尝试的 1 分钟 deadline 到期后才换连接。当前实现行为可解释,但在两票仍可写入时,单条 WAL 被阻塞一分钟不符合故障切换的延迟预期。修复要处理超时 Append 结果未知、可能已提交的语义。完整时间线和原始证据位置见测试报告。
Ariznawlll commentedon Nov 5, 2026
结论
补充完整 SQL 服务链路的定位证据,不是修复验收结论:约 60 秒等待不等于 Raft 选主耗时,也不宜全部归因于已退出节点上的 TCP 连接。 自然停止 WAL leader 后,存活 LOG 的服务端仍可有
SyncPropose长时间等待;TN WAL 重试完成后提交恢复,部分新连接此前等待 Logtail 读屏障。Raft 内部为何没有及时完成该提案,尚未确定。关键证据
80224b0314c6008ecb4de82d1e37b019d5e355df;实际二进制同 SHA、vcs.modified=false。本地独占 3 LOG / 1 TN / 1 CN,正常停止目标 leader,无分区注入、无生产源码/超时修改;不是多机部署。另有独立测试同机运行,时延不作为独占性能基准。同步采集的调用栈:
TN 在途 payload 为
0x6d9,l1 服务端在途 Append 也是该长度,与 1741 字节 WAL payload 加 12 字节头一致;这是调用栈/长度/时间相关证据,尚无跨进程逐请求 Raft 消息追踪。另一轮在故障后 50.9 秒采样,已当选新 leader 的 l2 也有服务端SyncPropose等待。待解决点
/private/tmp/mo-longpartition-HeXEtq/的switch-results.md、switch-trace.log、switch-repeat.log、switch-persistent-audit.log、switch_*.stack;关键证据已摘录在上面,不依赖读者访问本地路径。Ariznawlll commentedon Nov 5, 2026
结论
继续补充约一分钟等待的内部定位证据,不是修复验收结论:存活 LOG 接收的提案即使已确定无法发送,也可能继续挂在 pending 中,直到超时才让 WAL 重试。选出新 leader,甚至该请求所在节点自己成为 leader,都不会自动结束这个旧等待。 这为前一条评论中的
SyncPropose长时间等待补上了逐 key 证据;不声称原 issue 所有连接隔离样本都只有这一根因。关键证据
80224b0314c6008ecb4de82d1e37b019d5e355df,本地隔离 3 LOG / 1 TN / 1 CN。上轮官方二进制的自然 leader 停止已出现约 60 秒长尾;本轮为定位使用同提交、同 Dragonboat 依赖的仅加日志诊断构建,未改选举、超时、重试语义。新增日志会影响时序,不能将诊断复现频率当作官方概率。同机还有独立长稳,不作为独占性能基准。路径一:传输直接拒绝,但旧提案仍等到超时
该 key 的请求到超时为 57.664602 秒,整个 WAL 尝试及重试为 60.114452 秒,两者起点不同。另一轮 key
6067060892054681742同样accepted=false reason=2,等待 60.040240 秒才 EXPIRE,WAL 用时 60.242908 秒。三 LOG 日志均未见这两个 key 的 leader 接收、入日志、复制或应用事件。路径二:发送入队后连接写失败,原请求节点当选 leader 也没有结束旧等待
同 key 同样未见入日志或应用。
broken pipe的 batch 日志没有逐 entry key,因此批次内容关联仍限于同目标、相邻时间及生命周期证据,不把它写成额外的完整网络包证明。handleFollowerPropose向当前 leader 转发;sendMessage未处理transport.Send的 false 返回。传输层的目标解析失败立即返回,异步写失败则走无 proposal key 的 Unreachable;这些路径不会直接完成原 pending。processLeaderUpdate只更新 leader 信息。pending 可由 apply/drop/close/expiry 结束,不是无限等待或已证明的内存泄漏。store.propose在SyncPropose返回前无法进入重试;WAL 单次默认一分钟。依赖副本上的临时组件测试分别验证正常转发、丢失转发后换届、Unreachable、apply/drop/close/timeout、取消后晚到应用/超时清理,以及 gossip leave/rejoin 地址解析,均重复 3 次;没有修改仓库测试。/private/tmp/mo-proposal-trace-hfswkf/(overlay、组件测试、proposals-all.json、combined-summary.json、results.md)和/private/tmp/mo-longpartition-HeXEtq/(diagnostic-switch.log、transport-switch.log、raft-proposal-persistent-audit.log、三 LOG 日志及 fsync 账本)。关键逐 key 原始记录已摘录于上,不依赖读者访问本地目录。待解决点
Ariznawlll commentedon Nov 5, 2026
结论
补充同一等待机制的定位证据,不是修复验收结论:已将一组
unknownTarget串到具体 gossip 地址删除事件;另捕获了失败发送批次中的 proposal key。两类发送失败都未立即结束旧 pending。本轮没有发现确认提交的数据缺失,也不另拆重复 issue。关键证据
80224b0314c6008ecb4de82d1e37b019d5e355df,本地隔离 3 LOG / 1 TN / 1 CN。诊断构建只增加日志,未改选举、超时或重试;正常停止 leader 6 轮、突然停止 leader 3 轮,加无故障和两种 follower 停止共 12 组。沿用隔离集群、每组新建库,不是独立新集群 3/3;日志和同机任务会影响时延,不能据此比较故障概率。Resolve已经越过 replica→NodeHost 映射,失败在 gossip 地址层;与源码中 NodeLeave 删除地址、保留 replica 映射相符。另一轮突然停止 leader 后,key13831047832024122340返回reason=1 (circuitBreakerNotReady),仍等待 59.846349 秒 EXPIRE,WAL 重试完成用 60.221947 秒。不同拒绝原因仍落到同一 pending 等待机制。另一 key
12755473332290716539同样入队→批次失败→4.804758 秒后超时。这两个是 12 字节短请求,不是前述一分钟 WAL 样本;此前大请求的相邻 broken pipe 仍不能直接认作包含该 key 的失败批次。/private/tmp/mo-proposal-trace-hfswkf/delivery-results.md、delivery-summary.json、delivery-proposals-all.json、gossip-delivery-links.json、v3 overlay,以及/private/tmp/mo-longpartition-HeXEtq/proposal-delivery.log、delivery-persistent-audit.log和三 LOG 逐 key 日志。关键记录已摘录如上,不要求读者访问本地目录。待解决点
fix(logservice): bound stalled RPCs and stale HAKeeper startup (#32147)
XuPeng-SH commentedon Nov 5, 2026
合并范围补充:#32147 已合入 main,merge SHA d2dab99,最终 head 4c901ec。它完成了 LogService data RPC 的 8 秒响应不活动窗口;超时 Append 仍是结果未知,未增加 managed-client 自动重投。原连接黑洞场景可以据此做有界回归,但本 issue 暂保留研发归属。
原因是 6651454890 明确提出的开发后续仍未完成:如何将确定的传输发送失败安全反馈给 pending proposal 调用方,同时区分已入日志/未知结果并保留 WAL 去重与核对语义。6651970743 延续了这一定位,尚未给出该后续已完成或被替代的结论。当前合并未改动这条 Dragonboat failure-feedback 路径。
已读全部 6 条评论;这些反例使用旧提交,不能写成 d2dab99 上的已验证失败。保留研发的理由是上述明确未完开发范围,不是长稳结果未公布或未重跑测试。本轮仅远端与源码核对,没有本地实测。最终 head CI 的 shared build 和 PROXY/PESSIMISTIC BVT 成功,UT/SCA/UT Coverage 取消、Coverage 跳过、CI Required 失败,不记为完整通过;作者 focused/owning/race 报告也不替代原三 voter 场景验收。