镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

[Store] Port safe client metadata rebuild to v0.3.14. - #8

Open
ShuweiShen772 wants to merge 8 commits into
v0.3.14_xllm_patchfrom
port/v0.3.14-ha-metadata-rebuild
Open

ShuweiShen772 wants to merge 8 commits into
v0.3.14_xllm_patchfrom
port/v0.3.14-ha-metadata-rebuild

Conversation

@ShuweiShen772

@ShuweiShen772 ShuweiShen772 commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Port safe client metadata rebuild from v0.3.12 to the merged v0.3.14 xLLM patch
baseline (ff39d883, PR #7). This updates the existing Mooncake HA PR; it does not
create or replace the xLLM draft PR.

The client retains ownership of committed OFFSET-memory replicas and replays
metadata after master replacement without recreating the TransferEngine or
re-registering surviving buffers. Recovery has bounded retries, owner-local
completion barriers, version/address/segment fences, bounded remote notifications
and idempotent replay. The master gates business operations during HA rebuilding
and has an explicit degraded-serving timeout.

Review fixes preserve old persisted descriptor schemas, logical metadata-mode
ACK identities, checksum/session/residency, resource-safe route replacement,
managed-weight control contracts, completed replay-window retirement, disk-unmount
admission and dynamic-replica side-table keys. Standalone restarts use a durable
incarnation to prevent pre-replay new writes from sharing legacy (0,id) ordering.

Deployment and limitations

  • Enable clients with MC_ENABLE_HA_REBUILD=1; the HA master gate remains opt-in
    (--enable_ha_rebuild_gate=true). Use matching client/master binaries; rolling
    mixed descriptor/schema operation is unsupported.
  • Standalone startup requires a writable, retained incarnation file:
    MC_REBUILD_EPOCH_FILE, default $HOME/.mooncake-rebuild-epoch. Do not reset or
    roll it back while ownership survives. This requirement also applies with
    tracking disabled. Changing HA/standalone token domains requires cold-store/refill.
  • Client-rebuild Copy/Move APIs return UNAVAILABLE_IN_CURRENT_MODE.
  • Client replay does not import SSD/3FS/offloaded or cachelib allocations. Without
    separate applicable snapshot/OpLog recovery, refill/recompute unsupported-tier-only
    objects. Managed-weight manifests/control state require authoritative weight
    recovery; ineligible owner entries are not an alternate manifest source.
  • A failed staged replacement retains the old route. Unsupported overlapping
    resize reservation fails safely rather than erasing old metadata to force progress.
  • New opt-in writes can report ownership-confirmation failure after data commit;
    this is not transactional rollback. OBJECT_ALREADY_EXISTS Put/BatchPut remain
    idempotently successful and do not wait for ownership ACKs.
  • No durable deletion/data persistence across owner failure is promised. Existing
    lazy-delete resurrection restrictions remain documented.

Suggested Merge Priority

  • P0
  • P1
  • P2 — opt-in feature; merge remains gated on fresh validation.
  • P3

Type of change

  • New feature
  • Bug fix
  • Documentation update

Module

Mooncake Store: client/master metadata recovery, allocator reconstruction,
rebuild RPC gates and focused validation drivers. The merged build/dependency/
transport initialization/force/manual-device/custom-topology/TENT baseline is retained.

Testing

Current head: acc5a054b6ad79215fbdeaae06df7a666c6bee70 — unbuilt and untested. The submitter requested a fix/simplify-only
phase before resuming validation. Only PR-scoped pre-commit, static call-path review
and git diff --check have been performed. Do not approve this revision on the
basis of historical binary results.

Historical evidence for bc8bb57, not the current head:

Experiment Saved result and scope
GCC13 CPU focused suite 13 targets; client metadata suite 28/28
CANN9 Ascend Direct native suite 13 targets; client metadata suite 28/28
Native NPU restart / two-master failover 50/50 values, unchanged 3 registrations, fresh write/read; master 0 -> 1
Single-NPU Qwen3-0.6B + real xLLM Store backend Baseline, HA-disabled regression, HA failover and coordinated cold rollback stages have PASS artifacts; TCP Store with actual NPU inference
Joint failover Closed gate observed; expected/completed owner 1/1; nine cache keys and actual fetched KV buffers byte-identical; Qwen/client/engine/two registrations unchanged
Separate real 120 s supervisor timer Expected/completed 1/0; actual timer opens degraded service at ~125 s; no manual force call and no Qwen inference in this timer experiment

The Qwen writer owns no mounted global segment and is not a second expected owner.
The joint failover is not bidirectional or cross-host. Rollback is coordinated
cold deployment of matching ff39d88 binaries, not hot mixed-version rollback or
old-cache persistence. Paired functional outputs match the baseline, but baseline
answers on a few prompts were incorrect; model accuracy is not accepted.
No PD, million-key latency, all-tier persistence or leak-free shutdown claim is made.

Pending before merge: rebuild the new revision; add/run actual ff39d88-produced
snapshot/OpLog compatibility fixtures, non-P2P owner ACKs, pre-replay standalone
writes, replacement fault injection, weight control races, post-completion RPC/event
counts, unmount and feature-off dynamic-replica regressions; rerun the native/NPU
and all four required joint stages from the new source.

Checklist

  • Based on merged v0.3.14_xllm_patch, not the original upstream tag.
  • Contribution commits include Signed-off-by.
  • Unrelated CMake formatting removed; only feature registrations retained.
  • Limitations and validation provenance described.
  • Current revision rebuilt and all required tests passed.
  • Human submitter reviewed every changed line and can defend the change.
  • Ready to merge.

AI assistance disclosure

AI-assisted porting, review fixes, static simplification and validation harness
preparation. The human-review requirement remains outstanding; historical test
artifacts are not acceptance evidence for this untested revision.

Compaction follow-up

Current head: acc5a054b6ad79215fbdeaae06df7a666c6bee70. The complete PR diff against ff39d88 is
4,884 additions and 116 deletions (5,000 total), including production, tests,
scripts and documentation. Production adds 2,400 lines; tests/scripts add 2,378;
documentation adds 106. Unused scale benchmarking and redundant RPC/test wrappers
were removed. Shared fixtures and parameter cases retain self/remote/lazy-delete
byte recovery, registrations, Upsert variants, notify redelivery, concurrency,
version/address fences, tenant isolation and replica merging. Const-vector replay
avoids redundant metadata copies; the shared-token high-water mark is retained.
This head is unbuilt and untested, not merge-ready; old bc8bb57 binaries/results
are not acceptance evidence. These descriptions/replies have not been posted.

@ShuweiShen772 ShuweiShen772 changed the title [Store] Port safe client metadata rebuild to v0.3.14 [Store] Port safe client metadata rebuild to v0.3.14. Nov 6, 2026
Comment thread mooncake-store/src/client_service.cpp Resolved
Comment thread mooncake-store/src/client_service.cpp Resolved
Comment thread mooncake-store/src/client_service.cpp Resolved
Comment thread mooncake-store/src/client_service.cpp Resolved
Comment thread mooncake-store/src/master_service.cpp Resolved
Comment thread mooncake-store/src/master_service.cpp Resolved
Comment thread mooncake-store/src/CMakeLists.txt Resolved
@yt-zeng

yt-zeng commented Nov 6, 2026

Copy link
Copy Markdown

全面评审:43/43 文件 + 主路径调用链取证

评审版本 head 0b3dc812,base v0.3.14_xllm_patch @ ff39d883。静态评审(未构建、未跑测试、无 NPU/多机环境),
但下面「判干净」的每一条都实际查了调用点,包括我一开始怀疑、查完不成立的几条。8 条 inline 见下。

总判定

设计站得住,不建议按现状合入。硬风险集中在一处,正好打在本特性的核心目标上:一个永久离线的 owner
就能让恢复永远完不成,退化成"每次 failover 白等 120s 后降级服务"。

值得肯定的是:特性双侧默认关闭(master flag 默认 false / client 需 MC_ENABLE_HA_REBUILD=1),
非 HA 部署零影响;门控有硬上界(120s 强制降级),不会永久锁死集群;版本栅栏 (view_version, replica_id)
设计干净,ReorderedRetryCannotResurrectReusedAddress 把"地址复用不得复活旧 key"这个核心不变量测到了;
文档 ha_rebuild_usage.md 把已知限制(Copy/Move 禁用、lazy-delete 复活、混合版本不支持、不做持久化)写清楚了。

P2(3 条,建议合入前修)

  1. signal_complete 等整张 pending 表清空(client_service.cpp:5981)。队列表只有收到 ACK 才 erase,
    没有 TTL/重试上限,view 切换也不清理。一个永久离线的 owner ⇒ 该 client 永远不上报完成 ⇒ master 只能等满
    120s 降级开门,且后续每次 promote 都一样。
  2. pending 队列无上限、无退避、每 50ms 整表复制重发(client_service.cpp:2377)。N 条积压 = 20N 次/秒
    永久打在死 endpoint 上,N 只增不减。
  3. ResendLocalReplicaTable 持表锁跨 RPC(client_service.cpp:2488)。百万级 key 恢复期间挡住别的 owner
    推给它的 UPSERT,烧掉对方 put 的 30s ack 期限。
  4. OBJECT_ALREADY_EXISTS 由"无条件成功"变成"可能失败"(client_service.cpp:2135)。数据已提交却返回
    RPC_TIMEOUT;批量 CollectResults 也会把成功条目改写为失败。

P3(5 条)

  • master_service.cpp:1709:GrantReadLease(zero) 是 no-op(lease 是单调取 max),看起来想清 lease 但什么都没做。
  • master_service.cpp:1632:批内任一非法 entry 就让整批 INVALID_PARAMS,client 首批失败即整体放弃 → 与第 1 条同源的尾部风险。
  • rebuild_replica_table.h:16:非 memory 副本直接丢弃。文档建议直接写结论"failover 后 offloaded 对象不可见"。
  • mooncake-store/src/CMakeLists.txt:377(+ tests/CMakeLists.txt):~20 行与本特性无关的纯格式化改动,违反 AGENTS.md "Keep PRs lean"。
  • 描述未提及:Copy / Move 在 HA 下返回 UNAVAILABLE_IN_CURRENT_MODE(文档有、描述没有);before_upsert_end_for_test_ 测试 seam 出现在写主路径。

判干净(含排除的怀疑)

  1. 不会永久 503:rebuild_force_serve_timeout_ms 默认 120s,由 supervisor promote 后 detach 的线程调用
    ForceServingAfterTimeout()(master_service_supervisor.cpp:415-420),IsServing() 对 DEGRADED 返回 true。
  2. 跨 view 的陈旧定时器不会误开下一次的门(我一开始判有风险,查完不成立):wrapped_master_service 在每个
    leadership session 内重新 make_shared(:799),DeactivateServingState 会 SetServiceDelegate(nullptr)(:425),
    旧实例析构后 detach 线程里的 weak.lock() 返回空。
  3. 分配器记账一致:OffsetAllocator::reserve() 用 m_allocated_size += size,与正常 allocate()(:673/:728)
    同口径;extent 通过 allocationSize() 单独表达,不存在 reserve 记 extent / free 记 size 的不对称。
  4. master 侧 shared_lock 是既有模式:正常写路径 UpsertStart 也用 shared_lock(master_state_mutex_)
    • MetadataAccessorRW 做每 key 互斥(master_service.cpp:802),RebuildMetadata 没放宽锁。
  5. RebuildMetadata 无自锁死:SpinLocker pin_lock(&metadata.lock) 只包住
    TotalLength()/group_id/IsHardPinned()(三者都不再内部加锁),GrantReadLease()(内部 SpinLocker)与
    VisitReplicas 都在临界区之外。
  6. 重试有界:RunRebuildRecovery 3 次 + 100/500/1000ms 退避 + wait_before_retry 可被关停中断;
    rebuild_done_before_lock_ 有 10000 硬上限。
  7. 幂等合并成立:同 key 两 owner 同版本 → 第二个走 IsPublished 分支、ReplicaAlreadyPresent 去重后
    AddReplicas;版本不同按 (epoch,id) 取新。GetCommittedRebuildEntry 与 RebuildMetadata 的版本推导口径一致。
  8. 地址绑定不会绑错:FindAllocatorByEndpointAndAddr 要求 buffer_address ∈ [base, base+size),
    同机多 segment 共享 endpoint 时用地址区间消歧,绑不上就整体失败而不是绑错。

测试覆盖

19 个 TEST/TEST_F(client_metadata_rebuild_test.cpp 1235 行)+ 5 个新 target,在同类 PR 里算很好的:
乱序重试不复活已复用地址、多副本幂等合并、批量 ack 超时只失败受影响 key、lazy-delete 行为固化、
所有 Move 入口在门控关闭时拒绝。

缺口(正好对应上面的 P2):

应有用例 现状
owner 永久离线 → signal_complete 行为 / 队列是否阻塞完成 缺。BatchAckTimeoutOnlyFailsKeysOnStalledOwner 只断言"超时后条目保留"(PendingNotifyEntryCountForTest()==2),把成因固化了但没测收尾
大表 resend 期间 notify 是否被阻塞 缺
ALREADY_EXISTS + ack 超时 → Put 返回值 缺
批内有毒 entry → 部分成功 缺
view 切换后旧 pending 是否清理 缺
真实 120s 定时线程(现在用 ForceServingAfterTimeout() 手动驱动) 缺

未验证边界

没有构建、没有跑测试;+5398 行里我精读了 master 侧 rebuild、gate 生命周期、client 侧记录/通知/重发/完成信号、
分配器 reserve 五条主路径与全部新增头文件,rpc_service.cpp 里几十处 IsServing() 前置是抽查的。
PR 描述的 NPU 双向 failover(closed gate 观察、1/1 owner 完成、字节级恢复)属提交者自述,无法复现。

建议

  1. 合入前修 P2 四条(第 1 条是可用性核心,第 3 条改动最小收益最大)。
  2. 描述补三句:Copy/Move 在 HA 下不可用、offloaded 对象不参与恢复、descriptor/schema 变更不支持混部。
  3. 回退无关 CMake 格式化。
  4. P3 可择机:GrantReadLease(zero)、逐条容错、测试 seam 收进 #ifdef。

需要作者回答

  1. 一个 owner 永久离线时,你期望这个 client 的 signal_complete 成功还是失败?如果期望成功(体现"不能因为
    一个 client 阻塞集群"),那空参 WaitForPendingNotifies() 就是 bug 而非设计。
  2. pending 队列是否应该跨 view 存活?旧 view 的条目重发还有意义吗?
  3. Put 在 ownership 未确认时返错,是有意的 API 语义吗(还是只想让后台重试、前台仍成功)?
  4. 百万级 key 的恢复时长量过吗?ResendLocalReplicaTable 持锁跨 RPC 在这个量级下会不会把别人的写拖垮?

Semantically adapt 7932b32 onto the merged ff39d88 patch baseline. Preserve reviewed initialization contracts and use v0.3.14 route, liveness, allocator, and snapshot interfaces.

Validated fresh GCC 13 / CANN 9.0 Ascend Direct binaries: 13 focused test targets, real NPU master kill/restart, and two repeated two-master NPU failovers with byte-exact read/write recovery and unchanged memory registrations.

Original-commit: 7932b32
Signed-off-by: ShuweiShen772 <shenshuwei.3@jd.com>
@ShuweiShen772
ShuweiShen772 force-pushed the port/v0.3.14-ha-metadata-rebuild branch from 0b3dc81 to bc8bb57 Compare November 6, 2026 09:37

@THU-syh THU-syh left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

基于当前 bc8bb57 复核,以下两项问题仍成立并有 CPU 对照证据。成功卸载 segment 后残留 ownership 记录阻断恢复的问题,将补充到已有批量重建讨论。未进行设备验证。

Comment thread mooncake-store/include/allocator.h Resolved
Comment thread mooncake-store/src/client_service.cpp Resolved
Comment thread mooncake-store/src/master_service.cpp Resolved
Comment thread mooncake-store/src/master_service.cpp Resolved
Comment thread mooncake-store/src/master_service.cpp Resolved
Comment thread mooncake-store/src/master_service.cpp Resolved
Comment thread mooncake-store/src/client_service.cpp Resolved
Comment thread mooncake-store/src/rpc_service.cpp Resolved
Comment thread mooncake-store/src/master_service.cpp Resolved
Preserve legacy persisted descriptor schemas, use logical notify identities, fence standalone restarts with durable allocation incarnations, and retain checksum/session/residency during replay. Stage replacement resources before retiring old routes, respect managed weight import guards, retire unmounted ownership, close completed replay windows, and rekey dynamic replica tracking.

Validation: PR-scoped pre-commit/static diff checks only. No build or test execution for this revision, per the requested review-and-simplify phase; prior bc8bb57 results do not validate this commit.
Signed-off-by: ShuweiShen772 <shenshuwei.3@jd.com>
Centralize write-token derivation and resolve the segment identity together with allocator lookup, avoiding the second segment/owner traversal. Reject pre-connect heartbeat observations by connection generation while allowing fresh Ping recovery after a transient handshake failure. Fence completion against concurrent connection/epoch changes.

Validation: static call-path review and PR-scoped pre-commit only. Builds and tests remain deferred by request; no new CPU, NPU or joint Qwen acceptance is claimed.
Signed-off-by: ShuweiShen772 <shenshuwei.3@jd.com>
Retire the completed retry attempt in the same short critical section as the replay window so a late failure cannot be overwritten by a later success cleanup. Match existing group-before-state lock order, retain managed-weight ownership classification, and quarantine replay that lacks authoritative weight state or attempts to mutate a closed import.

Validation: static review and file-scoped pre-commit only. Current revision is unbuilt and untested by request and is not merge-ready.
Signed-off-by: ShuweiShen772 <shenshuwei.3@jd.com>
Remove unused scale benchmark and single-entry/completion RPC wrappers. Share typed batch gating, progress metrics, metadata publication, test fixtures, buffers and polling. Parameterize self/remote/lazy-delete recovery and single/batch Upsert; consolidate redelivery coverage with stalled-owner batch recovery while retaining byte checks, registrations, ordering and concurrency barriers.

Avoid redundant metadata-vector copies and preserve the shared write-token high-water mark. PR diff against ff39d88 is 4,884 additions and 116 deletions (5,000 total), including production, tests, scripts and documentation.

Validation: file-scoped pre-commit, changed-block CMake formatting, shell/Python syntax and static review only. No new build or test execution; current revision is not merge-ready.
Signed-off-by: ShuweiShen772 <shenshuwei.3@jd.com>
@ShuweiShen772

Copy link
Copy Markdown
Collaborator Author

本文覆盖 yt-zeng、THU-syh 和 DragonFive 的评审意见,逐条回复和最后的完整回复均可用于中文回复评审。

分支已更新到 acc5a054b6ad79215fbdeaae06df7a666c6bee70。由于 GitHub API 尚未登录,这些回复还没有发布到 GitHub,也没有将任何评审讨论标记为已解决。

当前提交尚未重新构建、尚未执行测试。 按当前要求,这个阶段只进行修改、简化和静态评审,测试暂缓。下文提到的历史测试结果属于 bc8bb573,不能用于验收当前提交。

一、回复 yt-zeng 的逐条评审意见

1. signal_complete 等待整张 outgoing pending 表清空(4490670685)

这个问题成立,已修改。恢复完成不再等待历史 outgoing ACK 队列清空,而是等待当前恢复窗口内已接收、归本 client 所有的元数据重放任务。因此,永久离线的远端 owner 不会再阻止这个发送方完成自己符合恢复条件的元数据重放。

这里没有承诺恢复离线 owner 已丢失的内存,也没有绕过 master 的预期 owner 名单及降级超时机制。完成栅栏之后收到的迟到通知,如果只被旧 master 确认过,仍需先向当前 master 重放成功,再发送 ACK。

2. pending 队列无上限、无退避、固定频率全表重发(4490670835)

这个问题成立,已修改。完全相同的重试会合并,队列设置了 1,024 条消息、65,536 条 entry 和 64 MiB 序列化载荷的上限。每轮最多取出 64 条到期任务,不再复制全部排队载荷后全量发送。

每条任务采用指数退避,间隔从 100 ms 增长到最高 10 s,并受 12 次尝试或五分钟存活时间限制;容量不足时优先淘汰最老任务。丢弃时记录 WARNING 并增加 rebuild_failed_batches。ACKED 和 DROPPED 明确区分,过期或淘汰不会把未确认的写入变成成功。

3. owner 表锁跨整个批量重放 RPC 持有(4490671004)

这个问题成立,已修改。快照采集和本地记账只使用短临界区,批量重放及迟到通知的 RPC 都在表锁之外执行;使用本地 owner 重放任务的 ticket 实现完成栅栏。

已有 8,193 条记录的回归用例用于检查锁作用范围,但它不是百万 key 恢复时延的测量,不能据此宣称百万级恢复性能已经验证。

4. OBJECT_ALREADY_EXISTS 从幂等成功变成可能失败(4490671145)

这个问题成立,已修改。单条 Put 和 BatchPut 遇到 OBJECT_ALREADY_EXISTS 时保持幂等成功,并保留原来已提交的字节内容。这个分支的 ownership 维护采用尽力而为的方式,不等待远端 ACK,也不再用通知失败覆盖原本成功的结果。

新 Put/Upsert 仍保留显式开启该特性后的 ownership 确认约定:数据可能已经提交,但 ACK 确认超时仍可能返回错误;这个错误不表示数据事务已经回滚。未开启该特性的 client 不进入 ownership 跟踪路径。

5. GrantReadLease(0) 没有效果,容易误导(4490671329)

这个意见成立,已删除重建路径中的这行调用,没有改成强制撤销已有 lease。已有读者仍保留其 lease,被替换的 allocation 按既有读租约和写入宽限期延迟释放。

6. 一条非法 entry 导致整批有效记录无法恢复(4490671509)

这个问题成立,已修改,同时保留原 RPC 返回类型,避免为此增加不必要的线协议/API 改动。

批量重放遇到永久性的 INVALID_PARAMS 后,client 会逐条重试以定位被拒绝的记录,记录 key 和失败计数,并只在本地记录仍对应被拒绝的快照版本时将其隔离,继续恢复有效记录。传输、资源和 view 过期错误仍然保留重试能力,不按非法记录丢弃。

完成信号不包含已拒绝或不符合恢复条件的记录,不能将其解释为这些对象也已恢复成功。

7. offload/cachelib 不参与恢复,需要写清运维结论(4490671722)

同意补充运维结论,但需要限定范围。client ownership 重放支持 OFFSET allocator 管理的内存副本,不导入 SSD/3FS/offloaded 副本或 cachelib allocation。

如果没有另外配置且适用的 snapshot/OpLog 恢复路径,只存在于这些不支持存储层的对象不会被本功能恢复,需要业务回源、重填或重新计算。但如果部署本身配置了独立的快照恢复,就不能笼统声称所有 offloaded 对象在每次 failover 后都一定不可见。

8. 无关 CMake 格式化改动(4490671868)

这个意见成立,已回退。相对 ff39d883 的 CMake 差异只保留 rebuild_retry.cpp、五个专项测试 target、相关环境设置,以及实际故障/规模测试辅助程序的注册。

与功能无关的 nof、TENT 和 source-check 等格式化改动已经移除。

补充:PR 描述中的限制及写主路径测试 hook

PR 描述草稿已补充:client rebuild 模式下 Copy/Move 不可用、不支持的存储层不参与导入,以及 client/master 必须使用匹配的 schema。

before_upsert_end_for_test_ 是私有且默认未设置的测试 hook,保留用于提交与 ACK 之间竞争窗口的回归用例,做法与已有 Client 测试 hook 一致。本轮没有仅为了隐藏它而引入按构建宏变化的 Client 类布局。这是对该 P3 建议的明确取舍,不是在生产中启用测试回调。

二、回复 yt-zeng 提出的四个设计问题

  1. 远端 owner 永久离线时,这个 client 是否应成功完成恢复?

    应允许这个 client 完成自己符合恢复条件的本地元数据重放,但不能宣称远端已丢失的内存被恢复。master 的预期 owner 名单与显式降级超时仍是独立的约束。

  2. pending 队列是否跨 view 保留?旧记录重发是否仍有意义?

    有意保留,因为记录描述的是 allocation/write 的 ownership 事实,不是发往旧 master view 的 RPC。队列已经有容量、退避和存活时间限制,也不再阻塞发送方的恢复完成。接收 owner 会检查版本、地址和 segment 栅栏,必要时向当前 master 重放后再 ACK。

  3. ownership 未确认时 Put 返回错误,是有意的 API 语义吗?

    仅在显式开启特性的新写入路径上有意保留。它表示 ownership 确认失败,不表示已提交的数据回滚。已存在对象的幂等 Put/BatchPut 已恢复为不等待这次确认、直接保持成功。

  4. 百万 key 的恢复时间是否实际测过?

    尚未测量。锁作用范围回归和小规模实际/NPU 实验不能替代百万级测量,目前不宣称百万 key 的吞吐或恢复时间已经通过验证。

三、回复 THU-syh 的评审意见

1. 嵌套 descriptor 新增普通字段,破坏旧 snapshot/OpLog 的持久化兼容性

这个问题成立,确实是持久化格式兼容问题,而不只是滚动升级时混用新旧二进制的问题。

已将 epoch、allocation extent 和 segment UUID 放在 AllocatedBuffer::Descriptor 内作为 compatible 字段。旧记录缺少 epoch 时按 0 处理;缺少 extent 时由 offset allocator 在 reserve 时推导,不能直接强行按逻辑 size 保留空间;缺少 segment UUID 时使用旧格式的查找路径。同时保留 rebuild token 和新增的对象属性。

当前暂停测试阶段,尚未生成或运行由真实 ff39d883 版本产生的 snapshot/OpLog fixture 测试。 后续不会仅用当前版本自身的 roundtrip 作为向后兼容的证明。此前未合入的实验性 HA 版本使用普通字段写出的格式,也不宣称与正式基线的持久化格式兼容。

2. HTTP/etcd 等模式下 ACK 回发地址和匹配身份不一致

这个问题成立,已修改。UPSERT 的回发地址和 ACK 中的 owner 身份统一采用 segment 注册使用的 GetSegmentEndpoint() 约定:P2PHANDSHAKE 使用实际 RPC 地址,依赖 metadata 查询的模式使用逻辑 local hostname。pending 中保存的 owner 身份因此与 ACK 匹配身份保持一致。

仍需补充真实非 P2P 模式的回归验收,目前不宣称当前提交已完成非 P2P 或设备验证。

3. 成功卸载 segment 后残留 ownership 记录,阻断其它存活 segment 的恢复

这个补充成立,bc8bb573 中确实存在所述生命周期问题。也同意不能用“跳过所有未知失败条目”替代修复:找不到已卸载 allocator 返回的是 INTERNAL_ERROR,只隔离 INVALID_PARAMS 并不能修复这个场景;反过来,把所有 INTERNAL_ERROR 都隔离又会误丢暂时未 remount、reserve 冲突等仍可恢复的记录。

已在 c704ddc6 按生命周期修复,当前 7d2da4db 已包含该修改:

  1. 普通 UnmountSegmentImpl 成功返回前,先调用 ForgetRegion(base, size) 清理对应区域的 ownership,再从 mounted_segments_ 移除 segment(client_service.cpp:4547–4552)。所以 S1/S2 场景在 S1 成功卸载后,后续快照只保留仍存活的 S2 记录,而不是向 master 继续发送 S1 的 descriptor。
  2. 优雅卸载请求成功后,同样清理该区域的 ownership,并把 segment 从 active 集合转入 gracefully_unmounting_segments_(client_service.cpp:4668–4677)。传输注册仍按原有流程等 master 确认卸载后再释放,没有提前释放仍可能被读取的缓冲区。
  3. ApplyOwnedRebuildEntries 不再只比较 endpoint,而是检查当前 active segment 的地址范围、完整 allocation extent,以及 descriptor 携带的 segment UUID(client_service.cpp:2517–2548)。校验与卸载使用同一个 mounted-segment → table 锁序;已经卸载的区域无法由迟到 UPSERT 重新录入。master 侧绑定也校验 segment UUID,防止同 endpoint/地址重新挂载另一个 segment 后误绑定旧 allocation。
  4. ForgetRegion 只删除指定范围内的有效记录,保留地址/key 版本栅栏,不清空其它 segment 的记录(rebuild_replica_table.h:159–166)。

因此,本轮不需要为同一个问题重复增加代码,也不会扩大 ReplayRebuildEntries 的错误吞掉范围。目前这是对当前源码的静态核对结论,不是该故障用例已通过测试。 后续恢复测试阶段需要明确补齐并执行:同一 owner 挂载 S1/S2、分别写入对象、卸载 S1、重启空 master,验证 S2 的 key 实际可读且能完成重建;还要覆盖优雅卸载、迟到 UPSERT,以及同 endpoint/地址重新挂载不同 segment 的情况。

四、回复 DragonFive 的逐条评审意见

1. P0:非 HA 重启复用 (0,id),旧高 token 可能覆盖重放前的新写

这个问题成立,已修改。Standalone master 在接受任何写入之前分配一个持久化、严格递增的进程代次(incarnation),即使 gate 关闭也一样。计数器使用文件锁,并在每次启动时通过文件 fsync、原子 rename 和目录 fsync 持久化;HA 模式仍使用 leadership view。旧格式 allocation 的 epoch 0 早于第一个 standalone incarnation。

Ping 会传递确认记录的 master incarnation,它与对象自身的 allocation/write token 分开处理。运维必须跨重启保留 MC_REBUILD_EPOCH_FILE,默认路径为 $HOME/.mooncake-rebuild-epoch;文件损坏、不可写或计数耗尽时拒绝启动,不能静默重置。这新增了 standalone 启动时的文件系统要求,关闭 ownership 跟踪时也存在这个要求。

不能在已有 ownership/持久化 token 仍存活时,把 HA/standalone 模式切换当作自动 token 转换;需要协调冷启动 store 并回源重填。重放前新写的对抗性竞争窗口仍需针对当前提交重新执行回归测试。

2. P1:client 重放清空 checksum、session 和 residency

这个问题成立,已修改。共享 entry 已携带这些属性,GetCommittedRebuildEntry 从已提交元数据填充它们;重放时恢复 checksum/session,并将 residency 传入 Publish。同版本合并会校验 entry 中携带的不可变属性是否一致,之后使用同一份恢复后的 metadata 进入既有 OpLog 序列化流程。

逐字段恢复和后续 standby/OpLog 恢复仍需在当前提交上重新执行验证。

3. P1:先 Erase 再 reserve,新副本准备失败会破坏旧 route

这个问题成立,已修改。在同一 key guard 内,所有可能失败的 reserve/bind 都先完成准备,期间保留旧 publication。完全相同的 in-place allocation 直接复用;只有准备成功后才替换旧 route。失败时通过 RAII 释放新准备的资源,被淘汰的旧 allocation 使用既有宽限期和记账清理方式处理。

如果 overlapping resize 无法与旧 allocation 同时 reserve,会安全失败并保留重试能力,不会仅为了推进恢复而先删除旧 route。当前提交尚未执行未 remount、allocator 不支持、reserve 冲突和 bind 失败的完整故障注入矩阵。

4. P1:managed-weight 重放绕过 group/import 控制约束

这个问题成立,已修改。重放采用 group → master-state → key 的锁序,并使用既有 import activity guard 覆盖元数据重放及 OpLog 可见化过程。

entry 还保留该对象是否属于 managed weight,避免空 master 在缺少权威 weight 状态时,将旧成员重新解释为普通 group。缺少权威状态,或者向已关闭/非 IMPORTING 的组新增或替换成员,都不符合 owner 重放条件,返回 INVALID_PARAMS,由 client 定位并隔离;已经由权威状态恢复的完全相同副本则不做修改。仍在 closing 过程中的 import 使用既有可重试 activity guard。

这个功能不是独立的 weight manifest 恢复机制。commit、abort、delete 和 residency 操作的竞争回归尚未在当前提交上执行。

5. P1:complete 后 replay view 未清除,后续写持续重复重建 RPC

这个问题成立,已修改。成功发送完成信号后,在短表临界区内清理活动 replay 窗口,同时检查 view、连接代次和 master incarnation。已经由当前 master 确认的普通写入不再额外调用 RebuildMetadata。

只被旧 master 确认过的迟到记录仍需先重放再 ACK;不能简单清空全部状态,否则会丢失这部分恢复任务。失败或过期的完成过程仍保留重试能力。

retry 状态也与完成窗口在同一个临界区内退出,删除了后面另一个成功清理块,避免覆盖刚发生的失败所请求的新重试。恢复完成后连续 100 次写入的 RPC/事件计数回归仍需执行。

6. P1:UnmountLocalDiskSegment 漏掉 REBUILDING 门控

这个问题成立,已修改。已补上与相邻 mount/unmount API 一致的 IsServing() 入口检查。REBUILDING 状态在进入注销和清扫实现之前返回 UNAVAILABLE_IN_CURRENT_STATUS,SERVING 状态保留原有行为。

当前提交尚未执行该对称性检查及 offload 故障回归。

7. P2:in-place Upsert 重发 ReplicaID,却没有同步 dynamic 侧表

这个问题成立,已修改。对已完成副本重新发号时,将已有 dynamic_replicas 节点取出并以新 ID 重新插入,保留记录内容。默认关闭 rebuild 的路径也受到这项修复保护。

关闭特性后循环执行 dynamic-copy、Upsert 和 removal 的回归仍需在当前提交上重新执行,目前不宣称已经完成长期循环测试。

五、完整回复

感谢几位评审对主路径逐项取证,并提供可复核的对照证据。可用性、持久化格式、ACK 身份、重启 token 和元数据生命周期方面的问题成立,相关修改已经推送到现有 PR,基线保持为 ff39d883。

这轮修改保留并补齐了以下约束:通知队列有界重试、只等待本地 owner 恢复任务的完成栅栏、短表锁、已存在对象的 Put 幂等成功语义、嵌套 descriptor 的旧持久化格式兼容、统一的逻辑 ACK 身份、standalone 重启代次、完整的已提交对象属性、先准备资源再替换 route、weight 控制面约束、完成后 replay 窗口退出、卸载门控,以及 dynamic-replica 侧表一致性。

简化方面,已删除重复的本地元数据结构,统一 token 推导,使用栈上的 metadata accessor,并让 allocator 查找同时返回 segment 身份,避免再次遍历 segment/owner 表。后续静态复核还修复了连接/Ping 短暂失败后的恢复问题,以及迟到重试与完成状态清理的竞争。本轮没有通过删除安全栅栏、回归覆盖,或者隐藏不支持的存储层行为来缩小 diff。

验证结果与边界

bc8bb573 已保留 CPU、CANN9 native,以及单 NPU 下 Qwen/xLLM + Mooncake 基本功能、HA 关闭回归、failover 和协调冷回退的历史结果。联合实验使用 TCP Store 和真实 NPU 推理;native Ascend Direct 字节往返测试是另一组独立证据,不能混为同一种后端的联合验收。

联合 failover 的方向是 master 0 → 1,挂载 segment 的 provider 是预期 owner,完成数为 1/1。Qwen writer 的 global_segment_size=0,不是第二个参与 roster 的 owner。切换后的九次实际 KV 读取进行了逐字节一致性检查,Qwen 进程、client 身份、TransferEngine 和内存注册保持不变。

另外保存了一次真实 supervisor 定时线程的实验:预期 owner 为 1、完成数为 0,约 125 秒后进入降级服务,没有手动调用 force-serving;这是独立的控制面定时实验,不是 Qwen 定时故障实验。

以上均为历史结果,不是当前提交的验收结果。 联合功能回归的成对输出与基线一致,但基线在部分提示词上的回答本身存在错误,因此不能宣称模型精度验收通过;也不宣称 PD、跨主机故障、百万 key 恢复时延或全存储层持久化已经验证。

按最新要求,当前提交只完成了静态调用链评审、PR 范围的 pre-commit 和 diff 检查,尚未重新构建、尚未执行测试,目前不能合入。

恢复验收前仍需要补齐并执行:真实基线版本产生的 snapshot/OpLog fixture、非 P2P 通知、重放前写入窗口、替换资源故障注入、weight 控制竞争、完成后的热路径 RPC 计数、磁盘卸载和 dynamic 侧表回归;同时必须从当前提交重新构建 CPU/NPU 二进制,并重新完成要求的全部联合实验,不能沿用旧二进制结果请求合入。

六、压缩迭代补充

压缩版已推送到 acc5a054b6ad79215fbdeaae06df7a666c6bee70,相对 ff39d883 为新增 4,884 行、删除 116 行,合计 5,000 行(包含生产代码、测试、脚本和文档)。生产代码新增 2,400 行,测试/脚本新增 2,378 行,文档新增 106 行。

已删除未用于功能验收的独立 scale benchmark、未使用的单条 committed-metadata RPC 和重复 completion RPC 包装;批量元数据读取同时服务单条与批量写,完成 RPC 直接使用同一实现。移除了生产 Client 中新增的公开测试访问接口,测试改用 friend peer;保留私有的提交/ACK 竞争 hook。

测试不是简单删场景:自身 owner、远端 owner、lazy-delete 和不同长度/字节内容的恢复合并为参数化读恢复用例;单条/批量 Upsert 共享创建、in-place、resize 用例;通知重试后 owner 记录及真实空 master 重启恢复合并到 Put/Upsert 的 stalled-owner batch 用例。并发栅栏、永久失联、超时/淘汰不等于 ACK、乱序地址复用、租户隔离和多副本合并继续保留。新增了 local-disk unmount 门控断言。

生产重放改用 const vector 引用,去掉不必要的整批复制;统一 batch 拒绝结果、进度 gauge 更新和 metadata 发布逻辑。同时恢复 shared write-token 的高水位推进,保留其专项回归,未通过删测试掩盖这项遗漏。

当前版本仅完成文件级 pre-commit、新增 CMake 块格式检查、shell/Python 语法和静态审查,尚未构建或执行测试;所有历史测试仍只对应 bc8bb573,不能用于当前版本合入验收。

Fresh GCC13 CPU validation reproduced ClientRebuildRestoresQuotaIdempotently: a same-version additional replica raised the inferred current token, so a newer 128-byte replacement was skipped and 512 bytes remained charged. Trust explicit metadata write tokens; derive from descriptors only when absent. Keep committed-entry reporting consistent and assert the shared token survives replica merging.

The reproducing CPU run was 11/12 focused targets passing. This fix has PR-scoped pre-commit checks; fresh rebuild/regression and NPU/joint validation follow. No merge-ready or new NPU pass claim.

Signed-off-by: ShuweiShen772 <shenshuwei.3@jd.com>
Signed-off-by: ShuweiShen772 <shenshuwei.3@jd.com>
Signed-off-by: ShuweiShen772 <shenshuwei.3@jd.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants