第 3 章 —— 共享状态的稳定身份
本章目标: 本章完成后,开发者能够定义跨节点可识别、可编码且可演进的
RouteHint。
学习目标
- 区分 collection locator 与 entry key。
- 解释
DsmEntity、EntityMetadata和 codec 的协作。 - 用
schemaId管理线格式兼容边界。 - 识别身份漂移与 schema mismatch。
前置条件
- 能启动第 2 章的单节点 Runtime。
- 理解 Java record 和序列化的基本概念。
案例进度
第 2 章只写入了一个地址。本章为它补上稳定身份:集合是
demo/gateway/route-hints,条目 key
是服务名,实体值才是地址和健康提示。
五层身份
public record RouteHint(
String entryKey,
EntityMetadata metadata,
String address)
implements DsmEntity<RouteHint> {
@Override
public RouteHint withMetadata(EntityMetadata next) {
return new RouteHint(entryKey, next, address);
}
}entryKey
是集合内部身份,需要在实体内容变化时保持不变。metadata 保存
lineage、HLC、epoch、fencing token 和可选 expiry
等运行时信息。业务不应自行伪造这些字段来赢得冲突。
集合 locator 由 tenantId/applicationId/collectionId
组成。同名 key 放在不同 locator
下,是两个相互隔离的条目。clusterId/serviceId
属于运行与通信域,不能替代集合 locator;第 17 章会验证这两层隔离。
codec 与 schema 不是装饰
两端需要把相同字节解释成相同实体。recordCodec(RouteHint.class)
提供记录编码,而 schemaId("route-hints/v1")
把线格式意图带入集合契约。若一端把 address
当字符串,另一端悄悄改成完全不同的结构,正确行为是拒绝不兼容数据,而不是猜测转换。
安全演进通常遵循:先发布能读取旧/新格式的版本,再切换写格式,最后清理旧读取路径。仅修改 Java 类名不等于完成分布式 schema 迁移。
反例与故障注入
准备两个同 locator、同 schemaId,但 codec
含义不同的节点。即使注册成功,也可能在远端 decode 时失败。反过来,schema
指纹不一致被拒绝,是保护而不是可用性缺陷:它阻止错误数据进入可见状态。
另一个错误是用随机 UUID 作为每次发布的
entryKey。这样每次健康更新都会新增条目,Register
永远没有机会替换同一个逻辑服务。
实验
检查本章身份卡,并运行固定的 schema mismatch 集成测试:
node --test tests/chapter-assets.test.mjs
cd submodule/dsm
mvn -q -pl dsm-integration-test -am \
-Dtest=TwoNodeIntegrationTest#schemaFingerPrintMismatchIsRejectedGracefullyBetweenTwoNodes \
-Dsurefire.failIfNoSpecifiedTests=false test实验先证明书中 locator/key/schema 卡片完整,再证明两个节点对不兼容 schema 采取显式拒绝。
实验验收卡
| 字段 | 内容 |
|---|---|
| 运行命令 | node --test tests/chapter-assets.test.mjs;聚焦 Maven
命令见上文 |
| 输入或故障 | 同一路由集合上的 schema fingerprint 不匹配 |
| 可观察结果 | 章节资产有效;不兼容复制被拒绝且测试通过 |
| 证据等级 | E3:双节点受控集成测试 |
| 本实验未证明 | 自动 schema 迁移、跨版本滚动升级和生产数据修复 |
回顾
- locator 识别集合,entry key 识别集合中的逻辑条目。
- 实体业务字段与 Runtime metadata 各负其责。
- codec 决定字节含义,schemaId/fingerprint 保护兼容边界。
- 身份漂移会制造新状态,schema 漂移应 fail closed。
下一步
第 4 章连接第二个节点,观察同一个 RouteHint
从本地提交变成远端可见。