在实现了 CURP 共识协议之后,一个关键问题是:如何验证实现的正确性? 分布式系统的测试比单机系统复杂得多,需要考虑网络延迟、节点崩溃、消息丢失等各种故障场景。本节将详细介绍 CURP 的测试框架设计。
在讨论测试框架之前,让我们先理解分布式系统测试的挑战。
单机程序的执行通常是确定性的:给定相同的输入,输出也是相同的。但分布式系统不同:
1 | 场景:两个客户端同时写入相同 key |
挑战: 测试必须接受所有正确的执行顺序,不能只验证一种预期结果。
分布式系统必须正确处理各种故障:
| 故障类型 | 表现 | 测试难点 |
|---|---|---|
| 网络延迟 | 消息延迟到达 | 可能打乱消息顺序 |
| 网络分区 | 节点间无法通信 | 可能导致脑裂 |
| 消息丢失 | 消息永远不会到达 | 需要重传机制 |
| 节点崩溃 | 进程突然终止 | 需要恢复机制 |
| 崩溃恢复 | 节点重启后恢复 | 状态可能不一致 |
挑战: 这些故障可能以任意组合、任意顺序发生,穷举测试是不可能的。
分布式系统的 bug 通常与特定的执行顺序相关:
1 | // 一个典型的竞态条件 |
挑战: 这类 bug 在正常测试中可能永远不会触发,只有在特定的并发场景下才会出现。
针对上述挑战,我们采用多层测试策略:
1 | 测试金字塔(从下到上): |
目标: 验证单个组件的正确性。
测试内容:
特点:
目标: 验证多个组件的交互正确性。
测试内容:
特点:
目标: 验证真实环境下的正确性。
测试内容:
特点:
为了支持上述测试策略,我们需要一个完整的测试框架。
1 | ┌─────────────────────────────────────────────────────────────┐ |
职责: 模拟网络行为,包括延迟、丢包、分区。
1 | class NetworkSimulator { |
使用示例:
1 | TEST(CurpTest, NetworkLatency) { |
职责: 控制节点的生命周期,模拟崩溃和恢复。
1 | class NodeController { |
关键区别:
stopNode:优雅关闭,数据完整保存crashNode:模拟崩溃,内存数据丢失,只有持久化的数据能恢复1 | TEST(CurpTest, CrashRecovery) { |
职责: 验证操作的线性一致性。
1 | class ConsistencyChecker { |
线性一致性(Linearizability)是最强的一致性模型,要求:
所有操作看起来都在某个时间点原子执行,且操作的顺序与实时序一致。
我们使用 Wing & Gong 算法 的简化版本:
1 | bool ConsistencyChecker::checkLinearizability() { |
1 | // test/dtest_witness.cpp |
1 | // test/dtest_curp_master.cpp |
1 | // test/dtest_curp_integration.cpp |
1 | TEST(CurpFaultTest, NetworkPartition) { |
1 | TEST(CurpConcurrencyTest, ConcurrentWrites) { |
1 | TEST(CurpPerformanceTest, FastPathSuccessRate) { |
1 | TEST(CurpPerformanceTest, WriteLatency) { |
| 指标 | 说明 | 目标值 |
|---|---|---|
| 快速路径成功率 | 1 RTT 完成的比例 | > 90% |
| 写延迟 P50 | 中位数写延迟 | < 5ms(同数据中心) |
| 写延迟 P99 | 99% 写延迟 | < 20ms |
| 恢复时间 | 崩溃后恢复时间 | < 1s |
| 数据丢失率 | 故障后数据丢失比例 | 0% |
| 线性一致性违反 | 检测到的不一致次数 | 0 |
建议使用 GitHub Actions 进行持续集成测试:
1 | # .github/workflows/test.yml |
分布式系统测试是一个复杂但必要的任务。通过本文介绍的测试框架:
我们可以系统性地验证 CURP 实现的正确性和性能。
测试不是一次性的工作,而是持续的过程。 每次修改代码都应该运行完整的测试套件,确保没有引入新的 bug。
技术 — 2026年7月21日