☰
WuKongIM InternalV2 动态节点 Stage 12C:E2EV2 诊断证据链与就绪门实战指南
2026/10/17 11:11:21 网站建设 项目流程
  • 即时通讯
  • 后端

【免费下载链接】WuKongIM

More than just IM 不只是即时通讯(IM)

项目地址:https://gitcode.com/gh_mirrors/wu/WuKongIM
点击查看免费下载

在 WuKongIM(WuKongIM,"More than just IM" 即时通讯系统)的 dynamic-node(动态节点)能力演进中,Stage 12 的设计目标是把"Scale-in 为什么卡住"这类问题从人工拼装证据变成可复现的诊断产物。本文围绕实现计划文档 Stage 12C E2EV2 与 Runbook 实施计划 展开:它定义了一个 e2e(e2ev2)诊断 bundle helper 的完整契约、如何在动态节点操作与 gofail 延迟故障场景中接入诊断采集、如何扩展 readiness gate 的opsprofile 保留诊断证据,以及 runbook 的排障决策树如何更新。读完后,你能够掌握"测试失败即产出诊断工件"的证据链构建方法,并能在 WuKongIM 仓库中按 RED/GREEN 节奏完成落地与验证。

背景与目标:从"能看到卡住"到"能看到为什么卡住"

Stage 12 的三份前置文档定义了整体脉络:

  • 设计文档 Stage 12 诊断设计:解决"当 Slot replica 迁移或控制面 revision 收敛缓慢时,运维只能看到safe_to_remove=false,却要手动拼接节点清单、scale-in 状态、Controller 活动任务、任务审计、Slot 运行时与 metrics"这一诊断缺口;
  • Stage 12A 计划 诊断面实现:提供GET /manager/nodes/:node_id/diagnostics只读路由与wkcli node diagnose命令;
  • Stage 12B 计划 metrics 扩展:补齐slot_replica_move的低基数 metrics 与相位观测。

Stage 12C(即本文主体)的目标非常聚焦,来自计划文档的 Goal 一节:

Make dynamic-node e2ev2 failures and delayed Slot replica move scenarios produce actionable diagnostic artifacts, then update the readiness gate and runbook to use those artifacts.

(让 dynamic-node 的 e2ev2 失败与延迟 Slot replica 迁移场景产出可操作的诊断工件,并更新就绪门与 runbook 来使用这些工件。)

架构约束同样在文档中明确:新增的 e2ev2 helper只允许调用三类公开接口——manager 公开 HTTP、公开wkcli node diagnose、/metrics;helper 接入 dynamic-node 操作场景与选定的 gofail 延迟场景;scripts/e2ev2/dynamic-node-readiness-gate.sh --profile ops被扩展为"把诊断 bundle 链接进证据目录,但不修改正确性测试本身"。技术栈包括:Go e2ev2 harness(test/e2ev2/suite)、cmd/wkcli、manager 公开 HTTP、Prometheus 文本快照、Bash readiness gate 与 dynamic-node runbook。

计划文档还列出了动笔前必须通读的 Prerequisites 清单(设计文档、12A/12B 计划、test/e2ev2/suite/manager_client.go、test/e2ev2/suite/metrics.go、test/e2ev2/cluster/dynamic_node_operations/ops_cli_test.go、两个 gofail 故障测试、readiness gate 脚本及其测试、以及 runbook docs/superpowers/runbooks/internalv2-dynamic-node-operations.md)。

需要说明一个路径事实:当前仓库 checkout 中,e2e harness 与 gate 脚本位于 test/e2e 与 scripts/e2e/dynamic-node-readiness-gate.sh;计划文档与 runbook 中使用的e2ev2前缀(test/e2ev2、scripts/e2ev2、cmd/wukongimv2、WK_E2EV2_*环境变量)指向同一套机制在 internalv2 二进制下的对应物。下文引用路径时,优先使用当前仓库可查证的实际路径,并保留计划文档中的e2ev2命名以便对照。

诊断工件契约(Artifact Contract)

计划文档对每个 dynamic-node 诊断 bundle 的目录结构给出了硬性契约——bundle 存放在测试自管的目录中:

dynamic-node-diagnostics/ node-4-manager-diagnostics.json node-4-wkcli-diagnose.json node-1-metrics.prom node-2-metrics.prom node-3-metrics.prom node-4-metrics.prom cluster-diagnostics.txt

四类工件各对应一条证据来源:manager 诊断路由的 JSON 快照、wkcli node diagnose的 JSON 输出、每个节点的 Prometheus 文本快照,以及集群级诊断转储。helper 必须遵循 best-effort 原则,文档列出了四条纪律:

  • 某一个工件采集失败时,为该工件写一个*.error.txt文件;
  • 不得掩盖原始测试断言失败(诊断采集是"锦上添花",不能吞掉真正的断言错误);
  • 永不调用内部 Go 包或私有 runtime API——只走公开面;
  • metrics 被禁用时不强制要求 metrics,但要记录 scrape 错误。

这个契约与 12A/12B 的公开面严格对应。以wkcli node diagnose为例,当前仓库 cmd/wkcli/internal/nodeops/command.go 中该命令的默认请求边界为task-limit=20、audit-limit=10、slot-limit=256,并通过--task-limit/--audit-limit/--slot-limit三个 flag 可调;其客户端 cmd/wkcli/internal/nodeops/client.go 通过nodePath(nodeID, "diagnostics")拼接出GET /manager/nodes/<node_id>/diagnostics请求(带查询参数编码 limit)。这正印证了计划文档的边界约束:诊断读取是有界快照,而不是无界扫描。设计文档对返回体字段也有定义:generated_at、state_revision、node_id、node、scale_in、onboarding、active_tasks、task_audits、slots、summary、warnings、sources,其中summary提供safe_to_remove、blocked_reasons、active_task_count、slot_replica_move_state、recommended_next_action等机器可读字段,slot_replica_move_state的取值域为waiting_learner_catchup、waiting_leader_transfer、phase_observation_missing、task_failed、no_active_move、unknown。

Task 1:新增 Suite 诊断 Bundle Helper

涉及文件:新建test/e2ev2/suite/dynamic_node_diagnostics.go与test/e2ev2/suite/dynamic_node_diagnostics_test.go,修改test/e2ev2/suite/manager_client.go。

计划的实现节奏是标准 TDD:

  1. 先写失败的 helper 测试,三个测试函数:

    • TestDynamicNodeDiagnosticBundleWritesManagerWKCLIMetricsAndClusterFiles
    • TestDynamicNodeDiagnosticBundleWritesErrorFiles
    • TestManagerClientDynamicNodeDiagnosticsCallsPublicRoute

    路由测试必须断言请求路径为GET /manager/nodes/4/diagnostics;bundle 测试必须断言文件名集合(node-4-manager-diagnostics.json、node-4-wkcli-diagnose.json、node-1-metrics.prom、cluster-diagnostics.txt等)。

  2. RED 阶段运行:

    GOWORK=off go test ./test/e2ev2/suite -run DynamicNodeDiagnostic -count=1

    预期失败,原因是 helper 类型与路由客户端尚不存在。

  3. 为 manager 客户端补充公开路由方法,在manager_client.go中新增:

    func (m *ManagerClient) DynamicNodeDiagnosticsRaw(ctx context.Context, nodeID uint64) ([]byte, error)

    该方法必须调用/manager/nodes/<node_id>/diagnostics。

  4. 创建 bundle helper,核心签名为:

    type DynamicNodeDiagnosticBundleOptions struct { NodeID uint64 Manager *ManagerClient ContextDir string WKCLIArgs []string OutDir string } func DumpDynamicNodeDiagnostics(t testing.TB, cluster *StartedCluster, opts DynamicNodeDiagnosticBundleOptions) string

    行为契约:

    • opts.OutDir已设置时直接使用,否则回落到t.TempDir()/dynamic-node-diagnostics;
    • 写出 manager 诊断 JSON;
    • 使用提供的 context dir 与 manager context 参数执行go run ./cmd/wkcli node diagnose <node_id> --json;
    • 对所有启用了 manager/API metrics 的在跑节点 scrape/metrics;
    • 把cluster.DumpDiagnostics()写入cluster-diagnostics.txt;
    • 返回输出目录路径。
  5. GREEN 阶段复跑同一条命令,预期 PASS。

这里有一个重要的现状事实:在当前仓库 checkout 中,该 helper尚未存在——test/e2e/suite目录(对应计划中的test/e2ev2/suite)里没有任何DumpDynamicNodeDiagnostics/dynamic_node_diagnostics.go的实现,而ManagerClient(见 test/e2e/suite/manager_client.go)已具备MustSlots、EventuallyNodeJoinState等公开路由客户端模式,且每个等待失败路径都会附带cluster.DumpDiagnostics()输出。从源码结构看,helper 正是沿着ManagerClient既有的"公开 HTTP + 轮询 + 诊断附注"模式扩展:metrics 采集可复用 test/e2e/suite/metrics.go 中的FetchMetricSamples等公开/metrics读取工具,集群级转储则直接调用 test/e2e/suite/runtime.go 的StartedCluster.DumpDiagnostics()(它聚合每个节点的 readyz 观测与节点工件快照)。

Task 2:把诊断接入操作类 E2EV2 场景

涉及文件:修改test/e2ev2/cluster/dynamic_node_operations/ops_cli_test.go与同目录AGENTS.md。

对应仓库中的场景目录是 test/e2e/cluster/dynamic_node_operations,其主测试为TestWKCLINodeOperationsLifecycleWithTraffic。接入点选在"第一次 scale-in advance 之后、等待最终安全之前"——即节点正处于排空窗口、诊断信息最有解释力的时刻。步骤为:

  1. 写失败断言:调用 bundle helper 采集 node 4 的诊断,断言返回目录包含
    node-4-manager-diagnostics.json node-4-wkcli-diagnose.json cluster-diagnostics.txt

    并断言 manager 诊断 JSON 中包含关键字段:

    "node_id": 4 "active_tasks" "summary" "recommended_next_action"

    这些字段与 12A 路由契约中的返回体定义一一对应。

  2. RED 阶段运行聚焦 e2e:
    GOWORK=off go test -tags=e2e ./test/e2ev2/cluster/dynamic_node_operations -run TestWKCLINodeOperationsLifecycleWithTraffic -count=1 -timeout 12m -p=1

    预期在 Stage 12A 路由与 helper 就绪前失败。

  3. 加入诊断 dump 调用:复用为wkcli context add ops创建的同一contextDir,调用示例:
    suite.DynamicNodeDiagnosticBundleOptions{ NodeID: 4, Manager: manager, ContextDir: contextDir, WKCLIArgs: []string{"--context", "ops"}, OutDir: filepath.Join(t.TempDir(), "dynamic-node-diagnostics"), }

    要求在断言失败的错误信息中附带 bundle 路径——这正是"失败即可取证"的关键:断言失败消息本身就指向证据目录。

  4. GREEN 阶段复跑同命令,预期 PASS。

当前仓库的 ops_cli_test.go 已有eventuallyWKCLIContains/formatDiagnostics等辅助,用于把 wkcli 输出断言失败时附带诊断上下文,与计划中"失败消息包含 bundle 路径"的要求同源。

Task 3:在 gofail 延迟窗口中断言诊断内容

涉及文件:修改test/e2ev2/cluster/dynamic_node_faults/scale_in_slot_drain_fault_test.go、test/e2ev2/cluster/dynamic_node_faults/slot_replica_move_fault_test.go与test/e2ev2/cluster/dynamic_node_faults/AGENTS.md。

对应仓库目录为 test/e2e/cluster/dynamic_node_faults。这一任务的价值在于:不只是"有工件",而是"延迟窗口内的工件内容本身就是可解释的证据"。

  1. 延迟 scale-in 场景(TestScaleInSlotDrainSurvivesDelayedLeaderTransfer):在waitGofailCountWhileScaleInBlocked之后采集诊断,断言
    summary.safe_to_remove=false summary.recommended_next_action in ["inspect_controller_task","inspect_slot_runtime"] active_tasks includes kind=slot_replica_move active_tasks includes phase_index active_tasks includes observed_voters

    这证明了在任务仍活跃时,延迟窗口是可解释的——safe_to_remove=false不是黑盒,而是指向具体的slot_replica_move任务与其相位/投票者证据。phase_index、observed_voters等正是 12A 设计中"active task DTO 暴露 Slot replica move proof 字段"的落点。

  2. 延迟 onboarding 场景:在 failpoint 生效期间对 node 4 采集诊断,断言
    onboarding.summary.total_active > 0 active_tasks includes target_node=4 task_audits contains slot_replica_move or task_audit source warning is explicit

    注意第三条是"二选一"式断言:要么审计历史中存在slot_replica_move,要么task_audit来源的不可用 warning 被显式声明——这与 12A 设计中"审计不可用不得导致整条路由失败,必须置sources.task_audit.available=false并加 warning"的契约一致。

  3. 运行 opt-in 聚焦 gofail 测试:
    scripts/build-gofail-binary.sh --cmd ./cmd/wukongimv2 --package internalv2/usecase/management --package pkg/controllerv2 --package pkg/clusterv2/tasks --package pkg/clusterv2/net --out /tmp/wukongimv2-gofail WK_E2EV2_BINARY=/tmp/wukongimv2-gofail WK_E2EV2_GOFAIL_DYNAMIC_NODE=1 GOWORK=off go test -tags=e2e ./test/e2ev2/cluster/dynamic_node_faults -run 'TestSlotReplicaMoveSurvivesDelayedLeaderTransfer|TestScaleInSlotDrainSurvivesDelayedLeaderTransfer' -count=1 -timeout 15m -p=1

    预期 PASS。

关于 gofail 构建的对应关系:当前仓库中的 scripts/build-gofail-binary.sh 支持--cmd、--package(可重复)、--out参数;例如 gate 脚本 scripts/e2e/dynamic-node-readiness-gate.sh 中以--cmd ./cmd/wukongim --package internal/usecase/management --package pkg/controller --package pkg/cluster/tasks --package pkg/cluster/net构建故障注入二进制,并用WK_E2E_BINARY/WK_E2E_GOFAIL_DYNAMIC_NODE环境变量驱动故障场景。计划文档中的wukongimv2/internalv2/*v2包名与WK_E2EV2_*变量即该机制在 internalv2 版本下的等价物,运行前应以目标分支的实际包名与环境变量为准。

Task 4:扩展 Readiness Gate 的证据面

涉及文件:修改scripts/e2ev2/dynamic-node-readiness-gate.sh(对应当前仓库 scripts/e2e/dynamic-node-readiness-gate.sh)与scripts/dynamic_node_readiness_gate_script_test.go(对应 scripts/dynamic_node_readiness_gate_script_test.go)。

先看现有 gate 的行为基线:脚本支持--profile quick|full|ops、--dry-run、--reuse-binary、--out-dir、--binary参数;证据默认写入data/dynamic-node-readiness-gate/<timestamp>/,包含summary.md、commands.log、environment.md与各 step 日志;opsprofile 在quick/full基础之上额外执行wkcli包测试与stage11-ops(即 dynamic_node_operations 目录的 e2e)。--dry-run时脚本只打印解析后的计划(各 step 的命令文本)而不执行,这为脚本测试提供了稳定的可断言面。

计划的扩展步骤:

  1. 先写失败的脚本测试:为 ops profile 的 dry-run 增加期望输出

    diagnostics_artifact_glob=data/dynamic-node-readiness-gate/<timestamp>/**/dynamic-node-diagnostics

    并构造一个假的"成功的 Stage11 ops 命令",使其创建

    dynamic-node-diagnostics/node-4-manager-diagnostics.json

    然后断言summary.md中链接了该诊断工件路径。

  2. RED 阶段:

    GOWORK=off go test ./scripts -run DynamicNodeReadinessGate -count=1

    预期失败,直到脚本支持加入。

  3. 加入工件收集逻辑:让--profile ops

    • 保持现有 Stage11 ops 日志保留行为不变;
    • 当测试打印或导出路径时,在 e2e temp/artifact 根目录下查找dynamic-node-diagnostics目录;
    • 将其复制或链接到$OUT_DIR/diagnostics/;
    • 在summary.md中追加## Diagnostics章节。

    文档强调这条逻辑必须保持 best-effort:诊断工件缺失只应产生 warning,不得让一次绿色的正确性运行因此失败——这与 Task 1 的 bundle 纪律一脉相承。

  4. GREEN 阶段:

    GOWORK=off go test ./scripts -run DynamicNodeReadinessGate -count=1 scripts/e2ev2/dynamic-node-readiness-gate.sh --profile ops --dry-run

    预期 PASS。dry-run 输出中的diagnostics_artifact_glob=...行即为可被脚本测试断言的契约点。

Task 5:更新 Runbook 的排障决策树

涉及文件:修改 docs/superpowers/runbooks/internalv2-dynamic-node-operations.md。

计划要求向 runbook 补充两部分:

  1. 诊断命令(人类可读与 JSON 双视图):
    go run ./cmd/wkcli node diagnose 4 --context prod-a go run ./cmd/wkcli node diagnose 4 --context prod-a --json
  2. blocker 决策树,把每一类 blocker 映射到首个诊断动作:
    blocked_by_control_revision -> compare required/observed revision, inspect node health and readyz blocked_by_tasks -> inspect active task step, phase_index, observed_voters/learners, audit last_reason blocked_by_slots -> run plan/advance only when no active conflicting task exists blocked_by_slot_runtime -> compare desired peers with live voters/learners and leader unknown_runtime -> check process, readyz, metrics scrape, manager node health unknown_channel_inventory -> do not remove; retry inventory read, collect channel status

从当前仓库的 runbook 内容看,这些更新已经部分落地:手册第 5 步在safe_to_remove=false时要求先执行上述两条wkcli node diagnose命令(文本视图用于快速定位建议,JSON 视图用于故障记录中保留scale_in、active_tasks、task_audits、slots、sources、warnings等精确字段),"Root-Cause Triage" 小节则覆盖了target_health_stale、blocked_by_slots、blocked_by_slot_leadership、blocked_by_tasks、blocked_by_channels/unknown_channel_inventory、blocked_by_runtime_drain、unknown_runtime/unknown_control_revision等 blocker 的处置要点——其中unknown_channel_inventory明确"不得移除节点,信道清单未证明目标为空",与计划决策树的 fail-closed 语义一致。手册的 "Local Release Gate" 小节仍引用scripts/e2ev2/dynamic-node-readiness-gate.sh --profile ops,实际仓库中的可执行脚本为 scripts/e2e/dynamic-node-readiness-gate.sh,证据输出目录为data/dynamic-node-readiness-gate/<timestamp>/。

最后一步是文档检查:

git diff --check

预期 PASS(无空白错误)。

Stage 12C 验证矩阵与提交规范

计划文档的 "Stage 12C Verification" 一节给出完整验证清单。必需命令:

GOWORK=off go test ./test/e2ev2/suite -run DynamicNodeDiagnostic -count=1 GOWORK=off go test ./scripts -run DynamicNodeReadinessGate -count=1 GOWORK=off go test -tags=e2e ./test/e2ev2/cluster/dynamic_node_operations -run TestWKCLINodeOperationsLifecycleWithTraffic -count=1 -timeout 12m -p=1 scripts/e2ev2/dynamic-node-readiness-gate.sh --profile ops git diff --check

可选的 opt-in gofail 证据(覆盖延迟 Slot replica 迁移与 scale-in 排空期间重启两条故障路径):

scripts/build-gofail-binary.sh --cmd ./cmd/wukongimv2 --package internalv2/usecase/management --package pkg/controllerv2 --package pkg/clusterv2/tasks --package pkg/clusterv2/net --out /tmp/wukongimv2-gofail WK_E2EV2_BINARY=/tmp/wukongimv2-gofail WK_E2EV2_GOFAIL_DYNAMIC_NODE=1 GOWORK=off go test -tags=e2e ./test/e2ev2/cluster/dynamic_node_faults -run 'TestSlotReplicaMoveSurvivesDelayedLeaderTransfer|TestScaleInSlotDrainSurvivesDelayedLeaderTransfer|TestLeavingNodeRestartDuringScaleInSlotDrainRecovers' -count=1 -timeout 15m -p=1

预期全部必需命令通过;若 Stage 12B 触碰过 Slot replica 迁移执行器的 instrumentation,则合并前应让 gofail 证据也通过。注意各命令的适用前提:GOWORK=off关闭 Go workspace 以隔离模块构建,-tags=e2e表明 e2e 场景文件带 build tag(当前仓库 test/e2e/suite/manager_client.go 等文件顶部即为//go:build e2e),-p=1串行执行避免端口与二进制冲突,gofail 场景依赖WK_E2EV2_BINARY(或当前脚本体系下的WK_E2E_BINARY)指向故障注入二进制。

计划的收尾是一个标准化提交(供流程参考;仓库只读,实际操作请在你自己的工作副本中执行):

git add test/e2ev2 scripts docs/superpowers/runbooks/internalv2-dynamic-node-operations.md git commit -m "test: collect dynamic node diagnostics evidence"

小结:这条证据链为什么重要

Stage 12C 的三个组件构成了完整闭环:suite helper 在测试进程内以 best-effort 方式产出七类标准工件;操作与 gofail 延迟场景把"断言内容"写进测试,使延迟窗口内的safe_to_remove=false必须能被active_tasks/summary/task_audits解释;readiness gate 的opsprofile 再把工件沉淀进data/dynamic-node-readiness-gate/<timestamp>/并从summary.md链接出来。结合 12A 的GET /manager/nodes/:node_id/diagnostics路由、12B 的slot_replica_move指标,以及 runbook 的 blocker 决策树,运维与自动化都拿到了同一套"命令驱动"的排障入口。后续若要继续深入,可从 cmd/wkcli/README.md、test/e2e/cluster/dynamic_node_operations/AGENTS.md 与 docs/superpowers/runbooks/internalv2-dynamic-node-operations.md 沿源码与文档继续展开。

  • 即时通讯
  • 后端

【免费下载链接】WuKongIM

More than just IM 不只是即时通讯(IM)

项目地址:https://gitcode.com/gh_mirrors/wu/WuKongIM
点击查看免费下载

相关推荐

上一篇:D2RML终极指南:一键解锁暗黑2重制版多开新境界
下一篇:Framework Laptop 13主板作为单板计算机使用:终极开发指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询