ARC 2选择正确的一致性语义第 5–9 章
第 5 章 —— Register:当前值与陈旧读取
本章目标: 本章完成后,开发者能够用 Register 表达一个 key 的当前逻辑值,并避免把本地查询误当成集群强一致读取。
学习目标
- 掌握
put/get/all/remove/size的本地契约。 - 区分更新同一 key 与创建新 key。
- 解释 remove 也需要传播和冲突排序。
- 为 route hint 写出陈旧读取策略。
前置条件
- 已观察第 4 章的双节点 delta。
- 理解 locator 与 entry key。
案例进度
履约服务从 10.0.0.1 滚动到
10.0.0.2。网关更新同一个
fulfillment-primary,旧节点退役后删除提示。
一个 key 的可见值
put(v2) 提交一个带 lineage metadata
的新候选值,没有数据库行锁保护的覆盖写语义。单节点立即看到
v2;其他节点通过在线 delta 或后续 repair 追上。
register.put(route("fulfillment-primary", "10.0.0.1:8080"));
register.put(route("fulfillment-primary", "10.0.0.2:8080"));
Optional<RouteHint> current = register.get("fulfillment-primary");
Optional<RouteHint> removed = register.remove("fulfillment-primary");all()、size() 和 get()
都观察当前节点已知状态。它们适合本地路由决策和诊断,不是跨节点快照,也没有读
quorum。
业务侧的陈旧读取策略
网关不能只写「从 Register 读取」。它还要回答:条目缺失时是否回源?连接失败时是否尝试次选地址?多长时间后把 hint 视为过期?如果路由错误会产生不可撤销效果,DSM 就不应独自承担决定。
本案例采用:route hint 失败时重新查询权威服务目录并重试一次;订单提交本身仍由履约服务的事务接口保证幂等。
反例与故障注入
把服务每次健康探测结果写成随机 key,然后调用
all().get(0)
取一个地址。条目会无限增长,顺序也没有业务含义。正确模型是稳定服务 key
对应当前 hint;需要保留历史时,把历史写入可审计系统。
另一个错误是 remove 后立刻断言所有节点都为空。remove 与 put 一样需要传播,并可能遇到并发候选值。
实验
运行双节点 put/remove/反向 put 测试,并记录三个观察点:本地提交、远端 put、远端 remove。
cd submodule/dsm
mvn -q -pl dsm-integration-test -am \
-Dtest=TwoNodeIntegrationTest#runtimeFirstRegisterDeltasReplicateAcrossTwoNodes \
-Dsurefire.failIfNoSpecifiedTests=false test实验验收卡
| 字段 | 内容 |
|---|---|
| 运行命令 | 上述聚焦 Maven 命令 |
| 输入或故障 | 同一 key 的 put、remove;反向节点写入 |
| 可观察结果 | 对端当前可见值按操作变化;远端 remove 有来源事件 |
| 证据等级 | E3:双节点受控集成测试 |
| 本实验未证明 | 全局强一致读取、历史查询、事务条件更新 |
回顾
- Register 表达一个 key 的当前逻辑值,不表达完整历史。
- 所有查询都是当前节点视角。
- remove 也是需要排序、传播和修复的状态变化。
- 业务需要定义缺失、陈旧和调用失败时的回源或降级策略。
下一步
第 6 章让 A、B 在分区窗口同时更新同一个 key,解释它们为何需要选出同一个 winner。