ARC 2选择正确的一致性语义第 5–9 章

第 8 章 —— CRDT:多节点本地更新与合并

本章目标: 本章完成后,开发者能够用 PN Counter 表达多节点本地增减,并说明合并函数需要满足的代数性质。

学习目标

  1. 解释 state、update、version vector 和 delta。
  2. 区分 CRDT 合并与 Register 选 winner。
  3. 说明交换律、结合律和幂等为何必要。
  4. 判断哪些业务量不能只靠计数器表达。

前置条件

案例进度

east 节点本地记录 3 次请求,west 节点记录 2 次。网络恢复后两端都应得到 5,而不需要为每次请求争夺中心锁。

合并状态,不挑一个 winner

各节点本地计数与合并

DsmCrdtCollection 的核心操作是 apply(entity)state()versionVector() 描述已观察的节点进度,deltaSince(vector) 生成给落后 peer 的状态增量。

counter.apply(new PnCounterUpdate("east-1", metadata, "east", 3, 0));
counter.apply(new PnCounterUpdate("west-1", metadata, "west", 2, 0));
long total = counter.state().value();

具体构造参数应以固定源码为准;这里的重点是每个节点贡献独立分量,merge 对每个分量取单调信息,再计算总值。重复收到同一状态不会再次加 3。

三条不可协商的性质

这三条描述 merge,不等于所有业务操作都可交换。余额扣减、库存约束和「不超过一次」的外部效果不能因为用了 CRDT 就自动成立。

反例与故障注入

实现 merge(left, right) = left.total + right.total 并把总数当作新状态。相同快照被重复发送时,总数不断翻倍。正确 PN Counter 保存每个节点的正/负分量或等价单调结构,而不是反复相加已经聚合的总数。

实验

cd submodule/dsm
mvn -q -pl dsm-integration-test -am \
  -Dtest=RuntimeIntegrationTest#oneRuntimeMergesCrdtCounterUpdatesThroughTheRuntimeFacade \
  -Dsurefire.failIfNoSpecifiedTests=false test

再用 crdt-merge-cases.json 手算顺序交换、分组交换和重复输入三组结果。

实验验收卡

字段 内容
运行命令 上述 Maven 命令;node --test tests/chapter-assets.test.mjs
输入或故障 两个节点分量;顺序变化和重复输入
可观察结果 Runtime facade 得到合并计数;三条性质的期望结果完整
证据等级 E2:Runtime 内合并测试 + 书籍代数样例
本实验未证明 双节点网络传播、业务去重、精确一次计费或生产容量

回顾

下一步

第 9 章把 Register、Lease、CRDT 放进同一张业务不变量决策树,完成第一次设计评审。

证据链接