Skip to content

[#3743] [Store] Stop HA leader monitor spinning on an empty master view - #65

Merged
lihenga merged 1 commit into
v0.3.13.post1_l4from
lzb/fix-ha-leader-monitor-spin
Oct 17, 2026
Merged

lihenga merged 1 commit into
v0.3.13.post1_l4from
lzb/fix-ha-leader-monitor-spin

Conversation

@lzb16

@lzb16 lzb16 commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

Description

修复禅道 Bug 2816432 中的 client 空转部分:HA 模式下,master 全部退出后,或新 leader 处于预热窗口期间,client 监视 leader 的线程会无休眠地空转。

现象(v0.3.13.post1_l4 现网包): master 全部退出后,每个 client 进程的 LeaderMonitorThreadMain 线程:

  • 不停调用 WaitForViewChange,每轮都重新挂一次 etcd watch、读一次 master_view;
  • 每轮打一条 ERROR:etcd_helper.cpp:75] key=.../master_view, error=key not found in etcd,每个进程约每秒 1,000 多条;
  • 1P1D 实测时,P 的两个 TP 进程加上独立 mooncake_client,每秒约 3,500 次 etcd 读、3,500 行 ERROR,mooncake_client 的 CPU 约 180%。

正常切主时也会空转:新 leader 先写“预热中”的占位值,RPC 就绪后才发布地址。这个窗口里 Get 能成功,所以不打日志,但空转照样发生。

原因:

  1. ReadCurrentView() 在 key 不存在或值为预热占位值时,都返回空视图。
  2. WaitForViewChange(旧版本) 因此返回 changed=true, current_view=空。
  3. 循环直接 continue,known_version 仍是旧版本,下一轮立即再次返回。

上游 kvcache-ai#3743(d1b9c565)在这里加了处理。#59 回移 kvcache-ai#3743 时,带进了预热占位值的逻辑,却漏掉了 LeaderMonitorThreadMain 中的这一段,所以空转的触发范围从“master 全部退出”扩大到“每次切主或启动的预热窗口”。

改动:

与 2816432 中“推理卡住”的关系:

Module

  • Transfer Engine (mooncake-transfer-engine)
  • Mooncake Store (mooncake-store)
  • Reshard (mooncake-reshard)
  • Mooncake EP (mooncake-ep)
  • Mooncake PG (mooncake-pg)
  • Integration (mooncake-integration)
  • P2P Store (mooncake-p2p-store)
  • Python Wheel (mooncake-wheel)
  • Common (mooncake-common)
  • Mooncake RL (mooncake-rl)
  • CI/CD
  • Docs
  • Other

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Breaking change
  • Documentation update
  • Performance improvement
  • Other

How Has This Been Tested?

说明: 测试基于 3cae469b。之后变基到当前 v0.3.13.post1_l4(350b6f54),无冲突;client_service.cpp 在这两个提交之间没有变化。

场景测试(独立 mooncake_client,单节点):

  • 环境:etcd 3.5.21 单节点 + 3 个 mooncake_master --enable_ha + 1 个 mooncake_client(etcd:// 地址,tcp)。
  • 流程:
    1. 稳定 30 s;
    2. SIGKILL leader,观察 45 s;
    3. SIGKILL 其余 master,观察 60 s;
    4. 重启一个 master,观察 45 s。
  • 三个版本:
    • 官方包 g3cae46;
    • 从 3cae469b 编译的基线,打开 USE_ETCD、STORE_USE_ETCD;
    • 基线加本提交,增量编译。基线与修复版的 mooncake_master 字节相同,只有 mooncake_client 不同。

Test commands:

cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DUSE_HYGON=ON -DUSE_HTTP=ON -DUSE_SHCA=ON \
  -DUSE_ETCD=ON -DSTORE_USE_ETCD=ON -DWITH_STORE_RUST=OFF -DBUILD_UNIT_TESTS=OFF
ninja mooncake_master mooncake_client

Test results:

阶段 指标 官方包 g3cae46 基线 3cae469 本修复
正常切主 45 s client CPU 8.61 s 8.39 s 0.03 s
etcd Range 请求 7,056 7,327 175
master 全挂 60 s client CPU 105.1 s 103.2 s 0.11 s
“key not found” 行数 60,334 61,604 60
etcd Range 请求 60,353 61,619 62
重连 杀 leader 后 ping 恢复 8.5 s 8.4 s 8.5 s
重启 master 后 ping 恢复 4.6 s 4.6 s 4.2 s
  • 修复后,master 全挂期间每秒仍有 1 行 “key not found”,来自心跳线程每秒一次的 ReadCurrentView。这是原有行为,上游也一样。
  • 三个版本在新 leader 上都显示 Clients: 1,client 的 segment 重新挂载成功。

未覆盖:

Checklist

  • I have performed a self-review of my own code
  • I have formatted my code using ./scripts/code_format.sh(没有运行这个脚本;改动的行已用同版本 clang-format 20.1.8 检查,无需调整)
  • I have run pre-commit on the files changed in this PR and all hooks pass
  • I have updated the documentation (if applicable)
  • I have added tests to prove my changes are effective(见上文“未覆盖”)
  • For changes >500 LOC: I have filed an RFC issue

AI Assistance Disclosure

  • No AI tools were used
  • AI tools were used (specify below)

使用 Claude Code 协助完成:定位空转原因、对比 #59 与上游 kvcache-ai#3743、回移代码,以及编写和执行上述场景测试。

… master view

Backport the LeaderMonitorThreadMain hunk of upstream kvcache-ai#3743 that was left
out when #59 (hy/ha-rpc-staged) backported the rest of that change.

When master_view is absent (every master is gone and the 5 s view lease
has expired) or holds a warming placeholder (a newly elected leader that
has not yet published its RPC endpoint, introduced by #59),
ReadCurrentView() returns an empty view. WaitForViewChange() then reports
changed=true with no view, and the monitor loop used to `continue` with
the stale known_version, so every iteration returned immediately: one
thread per client busy-looped, re-armed the etcd watch and re-read
master_view each time, and EtcdHelper::Get logged "key not found" at
thousands of lines per second.

Clear current_master_view_ when the empty observation still refers to the
view this iteration started from, so the next wait uses an empty
known_version and blocks on the watch until a ready view is published.

The function body now matches upstream d1b9c56 and current upstream main.
No other part of kvcache-ai#3743 or kvcache-ai#3943 is included.

(cherry picked from commit d1b9c56,
LeaderMonitorThreadMain hunk only)
@github-actions github-actions Bot added the store label Oct 17, 2026
@lihenga
lihenga merged commit c1d2c89 into v0.3.13.post1_l4 Oct 17, 2026
2 checks passed
@lihenga
lihenga deleted the lzb/fix-ha-leader-monitor-spin branch November 4, 2026 09:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants