第6章:迁移协议 —— freeze-drain-handoff-publish
AURA 4 阶段迁移协议的完整时序、epoch 单调性、不变式 I1–I4 的证明草图、关键 corner case(owner 故障 / 跨 cohort 事务 / 链式迁移)
第 4 章给了 AURA 的骨架,第 5 章解释了”决策怎么做”,本章解决最后一个核心问题:决策做完之后怎么把锁所有权安全搬过去。AURA 用一个 4 阶段协议——freeze-drain-handoff-publish——加 epoch 单调与 4 个不变式,把”迁移期间一致性不丢”钉死。读完你应该能徒手画完整时序图,并能描述至少 3 个 corner case 下协议的行为——这是 paper §3b/§5 的核心。
📑 目录
- 1. 4 阶段时序图
- 2. Phase 1: Freeze
- 3. Phase 2: Drain
- 4. Phase 3: Handoff
- 5. Phase 4: Publish
- 6. 不变式 I1–I4 的证明草图
- 7. corner cases
- 8. fallback 路径详细
- 自我检验清单
- 参考资料
1. 4 阶段时序图
1.1 完整时序
时刻 Old Owner (CN_A) Planner New Owner (CN_B) Clients
───── ──────────────── ──────── ──────────────── ───────
t0 OWNED decide (idle) use OwnerMap_e
migrate
A → B
│
t0+10µs ◄── Phase 1 Freeze ── │
refuse new acquire
cohort.state =
TRANSFERRING
t0+20µs notify Planner ──────►
│
t0+50µs │
drain in-flight │
──┐ │
│ wait commit/abort │
◄─┘ │
t0+200µs drain done ──────────►
│
t0+250µs ── Phase 3 Handoff ──────────►
ship lock state (apply)
────────────────────────►
t0+300µs ◄── ack ──────────── ──────────
│
t0+310µs Phase 4 ────────────────► broadcast
Publish OwnerMap_(e+1)
epoch++
t0+360µs cohort.state = (ready) OwnerMap_(e+1)
RELEASED new owner serve
route to B
1.2 总耗时分解
| Phase | 实测时长 | 关键操作 |
|---|---|---|
| 1 Freeze | ~10–20µs | atomic flag 翻转 + cohort.state = TRANSFERRING |
| 2 Drain | ~50–200µs | 等 in-flight tx commit/abort |
| 3 Handoff | ~50–80µs | RPC 传 lock state(数 KB 数据) |
| 4 Publish | ~50–80µs | OwnerMap delta 广播 |
| 合计 | ~150–400µs |
🌟 关键事实:单次迁移 ~150–400µs,与 5ms 决策窗口比是 1/15。这给了 AURA 在一个 5ms 内完成 1–2 次迁移的物理预算。
1.3 协议设计原则
| 原则 | 解释 |
|---|---|
| 先关后开 | 必须先 freeze 旧 owner,再 publish 新 owner。否则有”双 owner 窗口” |
| 单调推进 | 每次迁移 epoch 严格 +1,不可回退 |
| 可中断 | 任意 phase 失败可以回退到上一稳定状态 |
| 可观测 | 每 phase 记日志 + 计数,便于 debug |
🍎 直觉比喻:4 阶段协议像”换柜员”——先告诉旧柜员”别再接新客”(Freeze),等他把手头客户处理完(Drain),把账本递给新柜员(Handoff),最后挂”新柜员上岗”的牌子(Publish)。
2. Phase 1: Freeze
2.1 旧 owner 收到 Freeze cmd 后做什么
// TransferController 调用旧 owner
void OldOwner::on_freeze(cohort_id_t c) {
auto& cohort = cohorts_[c];
cohort.state.store(CohortState::TRANSFERRING, std::memory_order_release);
cohort.freeze_ts = now_us();
// 通知 OwnerLockTable:拒绝新 acquire
lock_table_.set_frozen(c, true);
// 通知 OwnerRpc:拒绝新 cross-cohort 请求
owner_rpc_.set_frozen(c, true);
// 立即回 ack 给 Planner
rpc_reply(FREEZE_ACK);
}
2.2 新 acquire 请求收到 frozen 怎么办
AcquireResult OwnerLockTable::acquire(key_group_id_t kg, ...) {
auto cohort_id = kg2cohort_[kg];
if (cohorts_[cohort_id].state == CohortState::TRANSFERRING) {
return TRANSFERRING_RETRY; // ← 客户端会 short retry
}
// 正常 CAS 路径
...
}
🌟 客户端处理:收到 TRANSFERRING_RETRY 后短 retry(~100µs),等 Phase 4 publish 完。这是 AURA 用户事务的”短暂 stall 时间”。
2.3 在途 acquire 怎么处理
| 情况 | 处理 |
|---|---|
| acquire 已经被 OldOwner.granted | 让它继续完成(不会被 freeze 影响) |
| acquire 还在 OwnerLockTable.queue | 立刻返回 TRANSFERRING_RETRY |
| acquire 还没到 OldOwner | 路由到 OwnerLockTable 时返回 TRANSFERRING_RETRY |
⭐ 关键事实:Freeze 不影响已经持锁的事务——它们继续 commit + release。Drain 阶段在等的就是这些事务。
2.4 Freeze 失败怎么办
| 失败场景 | 处理 |
|---|---|
| OldOwner 心跳超时 | TransferController 把 cohort 直接进入 FALLBACK |
| OldOwner crash | 同上 |
| 网络分区 | 同上 |
⭕ 互补:Freeze 失败 = OldOwner 不可用 ≈ 它已经”自然 freeze 了”。后续直接走 fallback 路径。
2.5 Freeze 防止”双 owner 窗口”
考虑反例:如果不 Freeze 直接 Publish 新 OwnerMap:
Old OwnerMap: cohort_C → CN_A
New OwnerMap: cohort_C → CN_B
t=0 Publish New OwnerMap
t+1µs Client X 用旧 OwnerMap,发到 CN_A → 取得锁
t+2µs Client Y 用新 OwnerMap,发到 CN_B → 也取得锁
t+3µs X 和 Y 同时 commit → 数据 race
🌟 结论:Phase 1 Freeze 是 I1(Authority Uniqueness)的关键保证。少了它,I1 立刻被破坏。
3. Phase 2: Drain
3.1 drain 的语义
drain = 等待该 cohort 上所有已经在执行的事务完成(commit 或 abort)。
void OldOwner::drain(cohort_id_t c, uint64_t timeout_us) {
auto deadline = now_us() + timeout_us;
while (now_us() < deadline) {
auto in_flight = count_in_flight_txns(c);
if (in_flight == 0) {
return DRAIN_OK;
}
std::this_thread::sleep_for(std::chrono::microseconds(10));
}
return DRAIN_TIMEOUT;
}
3.2 在途事务的”in-flight”定义
| 事务状态 | 是否需要 drain |
|---|---|
| Read phase(取 read set) | 否(不持锁) |
| Validate(已开始但还没 commit) | 是 |
| Commit acquire(取锁中) | 是 |
| Commit data(写数据中) | 是 |
| Commit release(释放锁中) | 是 |
| Already committed | 否 |
| Already aborted | 否 |
3.3 drain 超时怎么办
auto res = drain(cohort_id, timeout_us=200);
if (res == DRAIN_TIMEOUT) {
// 强制 abort 所有 in-flight 事务
abort_all_in_flight(cohort_id);
// 进入 FALLBACK
cohort.state = CohortState::FALLBACK;
return MIGRATION_ABORTED;
}
⚠️ 关键风险:强制 abort 会让客户端看到大量 abort,影响 SLO。所以 drain timeout 不能太短——AURA 默认 200µs(覆盖 99% 的 in-flight tx)。
3.4 drain 期间的并发情况
时刻 OldOwner in-flight tx (5 个)
───── ──────── ──────────────────
t0 FREEZE done A: validate
B: commit acquire (4/5 取了)
C: commit data (写到一半)
D: commit release (1/3 释放了)
E: read phase (没影响)
t0+50µs wait A: validate fail → abort(不影响 drain)
B: 取完 lock 5/5 → commit data
C: data 写完 → release
D: release 完 → done
E: validate ... commit
t0+200µs done? 全部 done!
return DRAIN_OK
🌟 关键事实:drain 不阻塞已持锁的事务的 release——它们能正常完成。drain 真正等的是”还在 commit phase 里”的事务。
3.5 drain 时长的实测分布
c6525, TPC-C 1M tps:
| 百分位 | drain 耗时 |
|---|---|
| P50 | ~30µs |
| P95 | ~120µs |
| P99 | ~180µs |
| P99.9 | ~250µs(超时风险) |
⭕ 互补:超时阈值设 200µs 时 P99.9 仍可能 timeout——这部分会被 fallback 兜住。生产中可调到 500µs,trade-off 是迁移更慢。
4. Phase 3: Handoff
4.1 转移什么
OldOwner 必须把 cohort 内所有锁状态转给 NewOwner:
struct CohortHandoffPayload {
cohort_id_t id;
epoch_t from_epoch;
// ❶ 该 cohort 的所有锁状态
std::unordered_map<key_group_id_t, LockEntry> lock_entries;
// ❷ 当前持锁的事务(应该是空,因为 drain 完了)
std::vector<TxnId> still_held;
// ❸ cohort metadata
Cohort cohort_meta;
// ❹ 跨 cohort 协调状态(如果有)
std::vector<CrossOwnerRpcState> pending_rpcs;
};
4.2 用 RPC 而不是 RDMA WRITE
理论上可以用 RDMA WRITE 直接把状态写到 NewOwner 的内存。但 AURA 用 RPC:
| 维度 | RDMA WRITE | RPC |
|---|---|---|
| 字节带宽 | 高 | 中 |
| 解析开销 | 接收方必须解析 + 复制 | 发送方序列化、接收方反序列化 |
| 错误处理 | 复杂(需要应用层 ack) | 标准 RPC 框架 |
| 兼容性 | 要求 NewOwner 已注册 MR | 不需要 |
选 RPC 的关键理由:handoff 数据是结构化的(hashmap + metadata),RPC 框架(如 grpc / brpc)能自动处理。RDMA WRITE 适合 bulk binary 传输。
4.3 RPC 的幂等性
NewOwner 必须对同一个 from_epoch 的 handoff 幂等——重发不会重新应用:
void NewOwner::on_handoff(const CohortHandoffPayload& p) {
auto& cohort = cohorts_[p.id];
if (cohort.last_handoff_epoch >= p.from_epoch) {
return rpc_reply(HANDOFF_ALREADY_APPLIED); // 幂等
}
// 应用状态
for (const auto& [kg, entry] : p.lock_entries) {
lock_table_.install(kg, entry);
}
cohort.last_handoff_epoch = p.from_epoch;
cohort.state = CohortState::OWNED; // 但还没 publish
cohort.epoch = p.from_epoch + 1;
rpc_reply(HANDOFF_OK);
}
⭐ 关键设计:NewOwner 在 Phase 3 完成后已经持有锁状态,但不接受新 acquire,因为 OwnerMap 还没 publish。这是有意的——Phase 4 是真正的”上线”开关。
4.4 Handoff 失败怎么办
| 失败 | 处理 |
|---|---|
| RPC timeout(NewOwner 无响应) | 标记 NewOwner 不可用 → 选其他 CN 重试 |
| NewOwner 拒绝(容量不足) | 选其他 CN |
| 网络分区 | 进 FALLBACK |
如果所有候选 NewOwner 都失败 → cohort 进入 FALLBACK。
4.5 Handoff 与 OwnerLockTable 的协调
t=0 drain done in OldOwner
t=10µs OldOwner serialize lock state → ~30KB
t=20µs RPC send → NewOwner
t=30µs NewOwner receive → deserialize
t=50µs NewOwner.lock_table.install(entries)
t=70µs NewOwner reply HANDOFF_OK
t=80µs OldOwner ack received → enter Phase 4
🍎 直觉比喻:handoff 像”快递包裹”——OldOwner 打包、寄出,NewOwner 收到后拆包安装。Phase 4 才是”开门营业”。
5. Phase 4: Publish
5.1 Publish 的语义
Publisher 通过原子操作推进 epoch,并广播 OwnerMap delta:
class OwnerMapPublisher {
std::atomic<epoch_t> current_epoch_{0};
public:
epoch_t publish_delta(const std::vector<OwnerMapEntry>& deltas) {
epoch_t old = current_epoch_.load();
epoch_t next = old + 1;
if (!current_epoch_.compare_exchange_strong(old, next)) {
return 0; // 别人推进了,本次放弃
}
// 广播
for (auto cn : all_cns_) {
broadcast_owner_map_delta(cn, next, deltas);
}
return next;
}
};
5.2 广播 vs 拉取
| 维度 | 广播(push) | 拉取(pull) |
|---|---|---|
| 一致性 | 强(Publisher 确保送到) | 弱(CN 自己来拿) |
| 失败处理 | Publisher 重发 | CN 检测过期重拉 |
| 带宽 | Publisher 单点 | CN 分散 |
| 实现 | 简单 | 需要 epoch heartbeat |
AURA 选广播 + lazy refresh:广播是主要路径,CN 收到 STALE_EPOCH 时 lazy 重拉作兜底。
5.3 客户端 lazy epoch 同步
// 客户端事务路径
void TxnExecutor::run(TxnRequest& tx) {
auto reply = OwnerRpc::send(target_cn, tx, my_epoch);
if (reply.status == STALE_EPOCH) {
owner_map_ = OwnerMapPublisher::pull_latest();
my_epoch = owner_map_.epoch;
goto retry;
}
}
🌟 关键性质:客户端 epoch 落后不会引发不一致——只会被 reject + retry。这是 AURA 不要求 epoch 全局严格同步的关键。
5.4 何时旧 OwnerMap 可以 GC
| 条件 | 解释 |
|---|---|
| 所有 CN 都已经升级到 new_epoch | 通过 epoch heartbeat 确认 |
| 没有 in-flight 事务还在使用 old_epoch 的 OwnerMap | 等 in-flight grace period 过去 |
| 一般保守 100ms(5 ms × 20) | safety margin |
5.5 Publish 失败怎么办
| 失败 | 处理 |
|---|---|
| atomic CAS 失败(并发推进) | 当前 publish 放弃,等下一轮 tick |
| 广播部分 CN 失败 | 重发(不影响 epoch 推进) |
| 所有 CN 失败 | 极端罕见,cohort 自动 fallback |
5.6 Publish 完成后老 owner 的责任
老 owner 在 Publish 之后还要做一件事:帮助 release 已经持锁但还没 release 的事务:
void OldOwner::on_publish_complete(cohort_id_t c) {
// I4 保证:这些事务的 release 仍然来到 OldOwner
for (auto& tx : my_pending_releases_for_cohort_[c]) {
// 正常处理 release(forward 到 NewOwner 也可,但简单起见就地处理)
process_release(tx);
}
// 全部 release 完之后,cohort 状态变 RELEASED
cohort.state = CohortState::RELEASED;
}
🧠 关键设计:OldOwner 在 publish 之后还”留尾”一段时间——等 in-flight 事务完成 release。这是 I4 的具体实现。
6. 不变式 I1–I4 的证明草图
6.1 I1:Authority Uniqueness
证明:
设迁移 时刻区间 ,其中:
- :Phase 1 Freeze 开始
- :Phase 2 Drain 完成
- :Phase 3 Handoff 完成
- :Phase 4 Publish 完成
A 是唯一 owner 的时段:(A 完全 OWNED)
TRANSFERRING 时段():
- A 已 freeze(拒绝新 acquire)
- B 还没接收 publish(拒绝新 acquire)
- → 没有任何节点接受新 acquire
B 是唯一 owner 的时段:(B 完全 OWNED)
🌟 结论:任意时刻最多一个节点接受新 acquire——I1 成立。
6.2 I2:Epoch Monotonicity
证明:
epoch 推进通过 compare_exchange_strong(old, old+1) 实现,这是单调递增的原子操作。任何并发推进只有一个成功,其他全部 retry → epoch 永不回退。
OwnerMap 携带 epoch,客户端只接受 new_epoch > local_epoch → 老广播被拒绝。
→ epoch 在所有节点视图下单调递增。
6.3 I3:Migration Drain
证明:
Phase 2 Drain 显式等待 in_flight_count(C) == 0 或 timeout:
- DRAIN_OK:所有 in-flight 都已 commit/abort → C 上无未完事务进入 Phase 3
- DRAIN_TIMEOUT:强制 abort 所有 in-flight → 同样无未完事务进入 Phase 3
→ 进入 Phase 3 时 cohort C 上没有未完成的事务。
6.4 I4:Data Commit Invariance
证明:
每个事务在 acquire 阶段记录”持锁的 owner CN”在事务对象里:
struct Txn {
std::unordered_map<key_group_id_t, cn_id_t> lock_owners_; // 取锁时的 owner
};
Release 时根据 lock_owners_ 路由,不再查 OwnerMap:
void release_locks(Txn& tx) {
for (auto& [kg, owner] : tx.lock_owners_) {
send_release(owner, kg, tx.id);
}
}
→ commit 路径只依赖事务局部状态,与 OwnerMap 无关 → I4 成立。
6.5 不变式之间的依赖
I2 (epoch monotonicity)
│ 帮助
▼
I1 (authority uniqueness): epoch 比较防止老 owner 误以为仍是 owner
│ 帮助
▼
I3 (migration drain): drain 配合 freeze 把双 owner 窗口压到 0
I4 (commit invariance) 独立兜底:即使 I1 短暂违反,commit 也不丢一致
🧠 设计师视角:I4 是兜底——即使 I1 在边界 case 短暂被破坏(比如时钟偏移导致 freeze 晚到),I4 保证数据本身不会损坏。这是工程上”多重防御”。
7. corner cases
7.1 owner CN 在 Drain 期间挂
t=0 Phase 1 Freeze done
t=50µs OldOwner crash
t=?? Drain wait...
处理:
- TransferController 检测心跳超时(500ms)
- 标记 cohort 为 FALLBACK
- 所有 in-flight 事务的 release 自动 fallback 到 MN(依赖 I4——它们记录了 owner 是 OldOwner,但现在路由到 fallback 的 RDMA CAS)
⭕ 互补:OldOwner crash 不会卡住整个 cohort——FALLBACK 是兜底。
7.2 Planner 决定 A→B 同时 A→C
并发迁移同一 cohort 必须串行化:
class TransferController {
std::unordered_map<cohort_id_t, std::mutex> cohort_locks_;
public:
void apply(MigrationDelta d) {
std::lock_guard lk(cohort_locks_[d.cohort_id]); // ← 串行化
do_4_phase_migration(d);
}
};
🌟 结论:同一 cohort 的迁移严格串行,不同 cohort 可并发。
7.3 客户端 epoch 落后多个版本
Client epoch = 100
Latest epoch = 105
Client routes to A (according to e=100)
A 已经不是 cohort 的 owner(在 e=103 迁走)→ A 返回 STALE_EPOCH。
Client 处理:
case STALE_EPOCH:
pull_latest_owner_map();
target_cn = router.lookup(tx, latest_map);
retry_with_new_target();
最坏情况:连续 retry 几次(每次 epoch 又往前推),直到稳定。
7.4 Drain 超时 → 强制 abort 所有 in-flight
实测中 P99.9 drain 超时是 250µs,AURA 默认 timeout 200µs。~0.1% 概率会 timeout:
| 后果 | 处理 |
|---|---|
| 强制 abort ~10 个事务 | 它们看到 ABORT_DRAIN,client 重试 |
| 客户端尾延迟 +100µs | 罕见,对 SLO 影响小 |
| cohort 进入 FALLBACK | 短暂降级,下一窗口可能重新升级 |
7.5 Handoff 包丢失
Handoff 用幂等 RPC(带 from_epoch):
t=0 OldOwner send handoff (epoch=100)
t=?? Network drop
t=50µs OldOwner timeout, resend
t=70µs NewOwner receive (epoch=100, 第一次)
install
reply HANDOFF_OK
t=120µs OldOwner receive ack
如果第二次到达:
t=200µs NewOwner receive (epoch=100, 第二次)
check last_handoff_epoch == 100 → already applied
reply HANDOFF_ALREADY_APPLIED
→ 重复无副作用。
7.6 跨 cohort 事务在迁移中
最复杂的 case:事务 T 同时访问 cohort_C1 和 cohort_C2,C2 正在迁移。
T 路由到 C1 owner = CN_A
T 在 CN_A 取 cohort_C1 锁 ✓
T 通过 OwnerRpc 让 CN_A 跟 cohort_C2 owner 取 C2 锁
但 C2 处于 TRANSFERRING → 返回 TRANSFERRING_RETRY
CN_A 收到 → 选择:
(a) abort T,client 重试
(b) 短 retry(等 C2 publish 完)
AURA 默认(a)—— 简单且符合 OCC 语义
🌟 结论:跨 cohort 事务在迁移中会被 abort + retry——这是 OCC 风格(不阻塞)的合理选择。
7.7 链式迁移:A→B→C
t=0 Planner 决定 cohort_X: A → B
t=300µs migration done, X owner = B
t=400µs Planner 决定 cohort_X: B → C
这是合法的——只要每次迁移完整(4 phase 都完成)+ 冷却窗口没违反,链式迁移没问题。
但如果在 t=200µs(B 还没完整接管)就决定 B→C:
// TransferController
if (cohort.state == CohortState::TRANSFERRING) {
// 拒绝新迁移决策
return MIGRATION_REJECTED;
}
→ 不允许”迁移中再迁移”。
8. fallback 路径详细
8.1 进入 fallback 的 4 种触发
| 触发 | 例子 |
|---|---|
| 主动降级 | 冷数据 cohort,OwnershipPlanner 决定不再有 owner |
| owner 故障 | 心跳超时、crash、network partition |
| 迁移 timeout | drain timeout / handoff fail |
| 控制面异常 | epoch 推进失败超过 N 次 |
8.2 fallback 期间的事务路径(与原 CREST 一致)
TxnExecutor::run() {
if (route_result == FALLBACK) {
// 走原 CREST 路径
for (auto& key : write_set) {
if (!fallback_lock.cas_mn(key, expected=0, swap=tx_id)) {
abort();
return;
}
}
rdma_write_data();
rdma_release_locks();
return COMMITTED;
}
}
🌟 关键性质:fallback 路径就是 CREST——AURA 与 CREST 二进制兼容,已 deployed 的 CREST 集群升级 AURA 无感。
8.3 fallback 期间的成本
| 维度 | OWNED | FALLBACK |
|---|---|---|
| 取锁延迟 | ~100ns(本地) | ~5µs(RDMA CAS) |
| atomic 消耗 | 0 | 每事务 ~3 次 |
| owner CN CPU | 中 | 0(不参与) |
| MN atomic 上限 | 不影响 | 影响(与 baseline 同) |
8.4 fallback 退出条件
# OwnershipPlanner 每个窗口检查
def check_fallback_recovery(cohort):
if cohort.access_count > THRESHOLD_HEAT and \
cohort.recent_window_growth > 1.5:
# 选新 owner
new_owner = pick_owner(cohort)
# 进入 TRANSFERRING (FALLBACK → TRANSFERRING)
transfer_controller.elect(cohort, new_owner)
| 条件 | 阈值 |
|---|---|
| 单窗口访问次数 > THRESHOLD_HEAT | 1000 ops |
| 连续 N 个窗口热度上升 | N=3 |
| 距离上次 fallback 超过 cooldown | 1s |
8.5 fallback 与渐进部署
集群组成
──────────────────
3 × AURA-aware CN (升级版)
2 × legacy CN (未升级)
AURA-aware CN 处理 OwnerMap,能跑 OWNED 路径
legacy CN 不认 OwnerMap,所有事务自动走 fallback
AURA 设计上把 legacy CN 的所有 cohort 都 mark 为 FALLBACK
↓
两类 CN 混合运行,无冲突,逐步升级
⭕ 互补:FALLBACK 是 AURA 渐进发布的关键支撑——如果 fallback 不存在,就必须全集群同时升级。
✅ 自我检验清单
- 4 阶段时序:能徒手画完整时序图(含 4 个角色 / 时间标尺)
- 总耗时:能列出 4 个 phase 的 µs 估算并解释为什么总和小于 5ms 决策窗口
- Freeze:能描述 Freeze 阶段如何处理新到达的事务
- 双 owner 反例:能解释为什么不 freeze 直接 publish 会破坏 I1
- Drain in-flight 定义:能列出哪些事务状态需要 drain
- Drain 超时:能描述 timeout 后的处理路径
- Handoff RPC 选择:能解释为什么用 RPC 而不是 RDMA WRITE
- Handoff 幂等性:能写一段代码示意如何保证幂等
- Publish epoch 推进:能写 atomic CAS 推进 epoch 的代码
- 客户端 STALE_EPOCH:能描述 lazy refresh 的工作流
- 不变式证明:能用 1 段话说清 I3 是怎么被 Phase 2 保证的
- I4 兜底:能解释为什么 I4 让 I1 短暂违反也不致命
- corner cases:能描述至少 4 个 corner case 及对应处理
- 链式迁移限制:能解释为什么不允许 transferring 状态再触发迁移
- 跨 cohort 事务 abort:能解释为什么 OCC 选 abort 而不是等待
- fallback 与 CREST 兼容:能解释为什么 fallback 路径就是 CREST 原路径
📚 参考资料
概念入门
- AURA 论文 §3b 组件 workflows:本仓库
paper_lock_ownership_cn/sections/3b_component_workflows.tex - AURA 论文 §5 不变式与协议:本仓库
paper_lock_ownership_cn/sections/
关键论文
- 2PC / 3PC / Paxos —— 分布式协议的经典对照(AURA 不是 2PC,但 epoch + drain 受其启发)
- Spanner (Corbett et al., OSDI’12) —— TrueTime 与 epoch 模型的工业先例
- Calvin (Thomson et al., SIGMOD’12) —— deterministic transaction
- FaRM (Dragojević et al., NSDI’14) —— 早期 RDMA OCC commit 协议
行业讨论
- Chandy-Lamport snapshot algorithm (1985) —— 分布式快照经典
- Distributed Snapshots: Determining Global States —— epoch-based snapshot 教材
框架文档
- gRPC / brpc 文档 —— 用于 OwnerRpc 实现参考
- CREST 仓库 —— commit 路径在 fallback 路径下完全保留