跳到主要内容
自适应运行时物理设计 · MorphoSys → AURA

第6章:安全运行时重配 —— epoch handoff 与 I1 / I2 / I3 不变式

AURA v27 怎么保证在线搬锁不破坏快照隔离:freeze-drain-handoff-publish 四阶段时序、W14.5 dual-authority 守卫、I1(at-most-one authority)+ I2(read set 与 commit ts 一致)+ I3(epoch handoff 排空 in-flight txn)三个不变式的证明草图与反例破坏

Epoch Handoff Invariants Snapshot Isolation Safe Reconfiguration I1 I2 I3 Dual Authority AURA v27

第 4-5 章告诉你 v27 用 A1/A2/A3 真的会在线搬 cohort owner、改 cohort 形状。但 OLTP 系统不能用”反正后台慢慢搬”的方式应付一致性——搬运过程中只要有一刻出现”两个 CN 同时认为自己是 owner”,整个 SI/OCC 一致性就完蛋。第 6 章就是回答这个问题的:AURA v27 用 epoch-based handoff + W14.5 dual-authority guard + I1/I2/I3 三不变式 把”safe reconfiguration”做成可证明的协议。本章把 freeze-drain-handoff-publish 四阶段时序图、W14.5 invariant 守卫、I1/I2/I3 证明草图和最重要的反例破坏(如果哪个阶段省掉会怎么坏)讲清楚——这是 paper §5 Correctness 一节的可教学版。

📑 目录


1. 为什么”在线搬”不能简单地”复制 + 切换”

朴素方案如下:

CN A (旧 owner)              Router                  CN B (新 owner)
   ┌────────────────────┐                            ┌─────────────┐
   │ cohort C 的 lock   │                            │             │
   │ table              │                            │             │
   └──────────┬─────────┘                            └──────┬──────┘
              │                                             │
              │  ① 复制 lock table state ────────────────►  │
              │                                             │
              │  ② 通知 router 切换 ownership               │
              ├──────────► router.OwnerMap[C] = B           │
              │                                             │
              │  ③ 后续 txn 到 CN B                         │

这个方案在 OLTP 上至少三处会坏

问题 1:CN A 的 in-flight txn 还没 commit,复制时机不对

CN A 正在处理 txn T1(已经拿到 cohort C 的锁,正在做 OCC validation)。复制时刻 T1 还没 commit——复制过去的 state 是 “T1 持有锁但未释放” 这个中间态。CN B 接管后 T1 commit/abort 走 CN A 的路径,CN B 的 state 跟实际 lock holder 不一致。

问题 2:router 切换那一刻,旧请求还可能到 CN A、新请求已经到 CN B —— 双权威

router 把 OwnerMap[C] 改成 B 这一动作是 TCP push 给所有 client。push 完成前,部分 client 还在用 A,部分 client 已经在用 B。如果一笔 txn 在 A acquire 同时另一笔 txn 在 B acquire,两笔 txn 都成功 —— OCC validation 时各自看到的 lock holder 不一样。

问题 3:copy 不是原子的,复制中途 CN A 可能新写入 cohort 状态

复制 1 MB 的 lock table 需要时间。复制开始到结束之间 CN A 可能:

  • 释放某把锁(state 在 A 是”已释放”,但复制过去的 state 是”持有”)
  • 接受新 acquire(state 在 A 是”新锁”,复制过去的 state 是”无锁”)

结论:必须设计显式协议,不能依赖”快速复制 + 切换”的乐观假设。

🍎 直觉比喻:搬家不能”两边都开门、客户随便走”——必须先关 A 门、清空 A 屋、把行李运到 B、再开 B 门。

🧠 关键洞察:disaggregated OLTP 的 in-line migration 在所有 prior work 里都有一些版本(DrTM+H / Calvin / Spanner),但 AURA 是最频繁的(100 ms tick)。频率高使得”协议正确性 + drain 速度”成为关键 trade-off——这就是 §3-§6 要讲的。


2. Epoch handoff 四阶段:freeze-drain-handoff-publish

v27 的迁移协议有 4 个显式阶段。任意一阶段的次序错乱都会导致 §1 中列出的三个问题之一。

2.1 完整时序图

   t            CN A (旧 owner)            CN B (新 owner)          Router
   │                                                                  │
   ├── ①freeze──> stop accepting new req on C                         │
   │              (新到达的 txn 被 reject,client retry)               │
   │                                                                  │
   ├── ②drain──> wait until all in-flight on C commit/abort           │
   │             (CN A 上 cohort C 的 in-flight count → 0)            │
   │                                                                  │
   ├── ③handoff ─copy frozen state─────────────────────► install      │
   │                                                                  │
   │                                                  state on CN B   │
   │                                                                  │
   ├── ④publish ────────────────────────────────────────────────────►│
   │                                                  router 切 OwnerMap[C] = B
   │                                                                  │
   ▼  ⑤新请求路由到 CN B

2.2 4 阶段的必要性

阶段防什么跳过会怎样
①freeze防止新 txn 在迁移期间加塞drain 永远不结束
②drain防止 in-flight txn commit 时找不到 authoritypartial commit / SI 破坏
③handoff防止 CN B 接管时 state 缺失CN B 持有的锁是空的
④publish防止 client 继续路由到已 freeze 的 Acohort 不可用

2.3 关键时序约束

四阶段必须严格顺序

freeze  ──before──>  drain ──before──>  handoff ──before──>  publish

任意两个阶段的次序颠倒都会触发问题。§7 给出每种颠倒的反例。

2.4 实现位置

代码骨架:

// src/transaction/aura/MigrationCoordinator.cc
void Coordinator::Migrate(CohortId c, CnId from, CnId to) {
    Freeze(c, from);                       // ① freeze
    DrainInflight(c, from);                // ② drain
    auto state = CopyFrozenState(c, from); // ③ handoff: copy
    InstallState(c, to, state);            // ③ handoff: install
    PublishOwnerMap(c, to);                // ④ publish
}

教学要点:四阶段每一步都是 blocking——下一步必须等上一步完成。AURA 每 100 ms 触发 N 个 cohort migration,所以 drain 速度是性能关键(见 §6 trade-off)。


3. W14.5 dual-authority 守卫

四阶段协议看起来 sound,但存在一个 race window

第 ④ publish 阶段,router 把 OwnerMap[C] 改成 B 这一动作不是 atomic broadcast。 部分 client 已经看到新 map(路由到 B),部分 client 还看到旧 map(路由到 A)。 如果旧 client 在这一窗口里发请求到 CN A —— CN A 已经 freeze 了,怎么办?

答案:W14.5 dual-authority guard

3.1 守卫位置

CN 在接收任何 lock 请求时,先检查 epoch

// src/transaction/aura/OwnerLockTable.cc - W14.5 guard
LockResult OwnerLockTable::Acquire(LockReq req) {
    if (req.epoch < current_epoch_) {
        return REJECT_STALE_EPOCH;   // ← W14.5 guard
    }
    if (cohort_state[req.cohort] == FROZEN) {
        return REJECT_FROZEN;
    }
    // ...
}
  • 每次 OwnerMap 更新时 epoch 单调递增
  • client 路由请求时把当前 epoch 带上
  • CN 收到请求时拒绝 stale epoch

3.2 client 端处理

收到 REJECT_STALE_EPOCH → client 拉新 manifest(从 memcached)→ retry。

retry 路径非常快(manifest 拉取 ~ms 级别),用户感知不到。

3.3 W14.5 vs W14

W14 是 v25 时期的原始 invariant:

W14:cohort C 的 active authority 在任意 wall-clock 时刻 ≤ 1。

W14.5 是 v27 对 W14 的细化:

W14.5:W14 + 在 OwnerMap 切换的 race window 内,旧 CN 通过 epoch check 拒绝 stale 请求,新 CN 通过 mode check 拒绝未安装完 state 的请求。

W14 是目标,W14.5 是实现方式

🍎 直觉比喻:W14 是法律:“任何时候只能一个 owner”。W14.5 是执法:“出示 epoch 凭证,旧 epoch 一律拒绝”。

3.4 实现细节

epoch 在 router 进程维护:

// src/transaction/aura/EpochManager.cc
class EpochManager {
    std::atomic<Epoch> current_epoch_;
    
    Epoch BumpEpoch() {
        return current_epoch_.fetch_add(1, std::memory_order_acq_rel);
    }
};

每次 PublishOwnerMap 之前先 BumpEpoch。

🧠 关键洞察:W14.5 guard 加在 CN 端而不是 router 端 —— 因为 router 端无法保证 atomic broadcast。把守卫推到 CN 端就避免了”router broadcast 时间窗口”这个不可消除的物理事实。


4. I1 不变式:at-most-one active authority

4.1 形式化陈述

对任意 cohort c、任意 wall-clock 时刻 t:

| { cn : cn ∈ CNs ∧ Active(cn, c, t) } | ≤ 1

其中 Active(cn, c, t) = cn 在 t 时刻持有 c 的 active authority。

4.2 证明草图

按四阶段时序逐阶段证明:

阶段 ①freeze(after):CN A 仍然持有 active authority,但不接受 new req。Active = {A},size = 1。

阶段 ②drain(after):CN A 仍持有 authority,但 in-flight count = 0。Active = {A},size = 1。

阶段 ③handoff(after):CN A 仍持有名义 authority(OwnerMap 没改),CN B 安装了 state 但还没”声明 active”。Active = {A},size = 1。

阶段 ④publish(瞬间切换):router BumpEpoch + 切 OwnerMap。在这一瞬间:

  • 任何到 CN A 的请求带的是新 epoch(因为 client 已看到新 map)→ W14.5 guard 跟 CN A 的 current_epoch_ 比较 → 注意:CN A 没收到 BumpEpoch 通知前,自己的 epoch 仍是旧值
  • 关键细化:router publish 前先 broadcast epoch bump 给所有 CN(包括 A),等所有 CN 确认收到后再切 OwnerMap

这一细化保证:publish 瞬间 CN A 已经知道 current_epoch_ 是新值,新请求带新 epoch 到 A 会被拒绝(cohort state 是 FROZEN)。

阶段 ④publish(after):CN B 收到第一个新 epoch 请求 → Acquire → state 是 INSTALLED → Active = {B},size = 1。

→ 全程任意 t 都有 |Active| ≤ 1

4.3 关键引理

引理证明依赖两个事实:

  1. Epoch 单调递增:BumpEpoch 是 atomic fetch_add,全局有序
  2. Epoch broadcast 在 OwnerMap 切换之前:router 协议保证

少任一条 I1 都不成立。

教学重点:I1 是”双权威”的负面定义——证明”不出现双权威”等价于证明”任意时刻 active authority 集合 ≤ 1”。


5. I2 不变式:read set 与 commit timestamp 一致

5.1 形式化陈述

对每个 commit 的 txn T:

∀ k ∈ T.read_set : version(k, T.commit_ts) = T.read_value(k)

即 T 读到的每个 key 的值,在 T 的 commit timestamp 处仍然有效(OCC validation pass)。

5.2 证明草图

OCC validation 的标准证明依赖:

  1. validation 阶段需要 lock authority(防止其他 txn 同时改 read set)
  2. lock authority 必须 “active”
  3. active authority 必须唯一(即 I1)

I2 依赖 I1

只要 I1 成立,OCC validation 的原子性就有保证——validation 在唯一 active authority 内进行,没有并发干扰。

5.3 cohort 边界处理

如果 T 的 read set 跨多个 cohort,每个 cohort 都有自己的 active authority。需要:

  • 每个 cohort 的 lock authority 提供”local validation”
  • 跨 cohort 的 read set consistency 由 timestamp 序保证(OCC 全局 commit_ts 单调)

这是标准 OCC 协议保证的,AURA 不改这部分。

🌟 结论:I2 不是 AURA 的新发明——是 OCC 协议的基本保证。AURA 的贡献是让 I2 在 active authority 迁移中仍成立(因为 I1)。


6. I3 不变式:epoch handoff 排空 in-flight txn

6.1 形式化陈述

对每个 epoch e → e+1 切换:

∀ txn T : T 在 epoch e 启动 ⇒ T 在 epoch e 内 commit 或 abort

即 epoch 内启动的 txn 必须在 epoch 内结束。

6.2 实现:drain 阶段

阶段 ②drain 的定义就是”等所有 in-flight 完成”:

// src/transaction/aura/MigrationCoordinator.cc - drain
void DrainInflight(CohortId c, CnId from) {
    while (true) {
        auto count = inflight_count_.load(std::memory_order_acquire);
        if (count == 0) break;
        std::this_thread::yield();
    }
}

CN 端维护 cohort-level in-flight counter,drain 等到 counter == 0。

6.3 风险:长事务导致 drain 卡住

OLTP 大部分 txn 是短事务(< 1 ms),drain 通常 < 10 ms 完成。但偶尔有长事务(如 TPC-C delivery),drain 等几十 ms 才结束。

→ migration 卡住,planner 后面排队的 migration 全部 stall。

6.4 v27 mitigation

drain 超时(默认 100 ms)→ 强制 abort 残余 txn → migration 继续。

trade-off:

  • 不超时:safety 优先(所有 txn 正常 commit),但 migration 可能 stall
  • 短超时:liveness 优先(migration 不 stall),但部分长 txn 被无辜 abort

v27 选 100 ms 是个经验值——TPC-C 99% txn < 5 ms,超时影响 < 1%。

🧠 关键洞察:I3 是”liveness vs safety”的 trade——drain 超时是 safety 优先的体现。两者不可兼得,必须显式选择。 AURA 选 safety 主导,liveness 用超时兜底。

6.5 跟 Calvin / Spanner 的对照

Calvin 的 epoch 是 batch boundary(deterministic execution),drain 是天然的(每 batch 跑完)。 Spanner 的 epoch 是 commit timestamp 边界,drain 通过 TrueTime wait 实现。 AURA 的 epoch 是 OwnerMap 切换边界,drain 通过 cohort-level in-flight counter 实现。

三者都是 epoch 范式,但 actuation 不同。AURA 是最细粒度的(per-cohort)。


7. 反例破坏:每一步省掉会发生什么

教学反例——把每种”省掉”的具体后果讲清楚。

7.1 省掉 ①freeze

症状:CN A 继续接 new req on cohort C。

后果

t=0:    drain 开始,inflight_count = 5
t=1ms:  3 个 in-flight commit,count = 2
t=1.1ms: 新 req 到 CN A(freeze 没生效)→ count = 3
t=2ms:  2 个 commit + 新 1 个 acquire → count = 2
...

inflight_count 永远到不了 0 —— drain 永远不结束,migration 永远卡。

7.2 省掉 ②drain

症状:CN A 上还有 in-flight txn T1(持有 cohort C 的锁),handoff 直接开始。

后果

  • T1 在 CN A 上做 OCC validation,需要查 cohort C 的 lock holders
  • handoff 同时把 lock table state 复制到 CN B
  • T1 commit 时 CN A 的 state 已被复制,T1 释放锁的动作没同步到 CN B
  • → CN B 上 cohort C 的 lock table 显示 “T1 仍持有锁”,但实际 T1 已 commit
  • → 后续 txn 在 CN B acquire 锁时被 phantom T1 阻塞

SI 破坏:partial commit + phantom lock。

7.3 省掉 ③handoff(含 state copy)

症状:CN A freeze + drain 完成,router 直接切 OwnerMap[C] = B。

后果

  • CN B 上 cohort C 的 lock table 是空的
  • 第一个新 txn 在 CN B acquire cohort C 的 lock:lock table 显示无 holder → grant
  • 但 CN A 在 freeze 前持有的写者状态(如 record k 被 T0 写过、写时间戳 ts0)丢了
  • → CN B 给新 txn 的 view 不包含 T0 的写
  • → SI snapshot read 错误

SI 破坏:丢失旧写。

7.4 省掉 ④publish

症状:所有阶段都执行完,但 router OwnerMap 没翻新。

后果

  • client 继续路由到 CN A
  • CN A 已 freeze,所有请求被 REJECT_FROZEN
  • client retry → 仍 reject → 用户超时
  • → 整个 cohort C 不可用,直到 router 终于 publish

Liveness 破坏:cohort 永久不可用。

7.5 省掉 W14.5 guard

症状:四阶段都正常,但 CN B 不检查 epoch。

后果

  • publish 阶段 router broadcast OwnerMap[C] = B,部分 client 已看到,部分还没
  • 旧 client 发请求到 CN A(带旧 epoch)→ CN A 已 freeze → REJECT
  • 新 client 发请求到 CN B(带新 epoch)→ CN B accept
  • 但是:万一某个新 client 因为 partial broadcast 看到了”OwnerMap 的新值但 epoch 是旧的”(这是 broadcast 不 atomic 的副作用)
  • → 该 client 路由到 CN B 但带旧 epoch
  • → 没有 W14.5 guard,CN B 接受这笔请求
  • → 这笔请求的 read set 是基于”旧 OwnerMap 假设的 cohort 分布”,跟 CN B 的实际 state 不一致
  • → 读到过期数据

SI 破坏:dirty read。

7.6 反例破坏的 5 条都被处理

v27 协议设计已经 cover 这 5 个反例:

反例v27 防御
省 freezefreeze 是 ① 阶段,必须在 drain 前
省 draindrain 等 inflight_count = 0
省 handoffhandoff 是 ③ 阶段,必须在 publish 前
省 publishpublish 是 ④ 阶段,由 MigrationCoordinator 保证
省 W14.5W14.5 是 OwnerLockTable.Acquire 第一行 check

教学重点:把”为什么这一步不能省”讲透,比把”协议是什么”讲透更重要——评委或读者真正想知道的是”如果偷懒会怎么坏”。

🌟 结论:第 6 章不是抽象证明,而是 5 个具体反例的”破坏剧本”。读完你能讲出”如果第 N 步不做,会在第 M 时刻具体怎么坏”,而不是只复述协议名字。


✅ 自我检验清单

  • 三大朴素问题:能列出 “复制 + 切换”朴素方案的 3 个失败模式(in-flight 未完 / 双权威 / copy 不原子)
  • 四阶段顺序:能默写 freeze → drain → handoff → publish 并解释每阶段的必要性
  • W14.5 守卫:能解释 W14.5 守卫在什么位置、防什么 race(broadcast 不 atomic 的 race window)
  • I1 证明:能给出 I1 的形式化陈述 + 四阶段证明草图 + 两个关键引理(epoch 单调 + epoch broadcast 在 publish 前)
  • I2 依赖 I1:能解释 I2 怎么依赖 I1 + OCC 原子性 + cohort 边界处理
  • I3 trade:能解释 I3 drain 超时是 safety vs liveness 的取舍,并举出 100 ms 经验值的理由
  • 反例破坏:能针对四阶段中任意一阶段 + W14.5 共 5 条,举出”如果省掉会发生什么”的具体例子
  • v25 → v27 差异:能解释 W14.5 比 W14 多防了哪个 race window(broadcast 不 atomic)

📚 参考资料

概念入门

  • v27 paper outline §3.8 + §5 —— epoch handoff + I1/I2/I3 source of truth
  • Snapshot Isolation (Berenson et al., SIGMOD’95) —— SI 定义
  • OCC (Kung & Robinson, TODS’81) —— OCC 三阶段定义

关键论文

  • Calvin (SIGMOD’12) —— deterministic 执行的 epoch 范式启发;AURA 用 epoch 边界做 OwnerMap 切换
  • Spanner (OSDI’12) —— global epoch + TrueTime;AURA 不需要 TrueTime(用 cohort-level inflight counter)
  • DrTM+H (USENIX ATC’18) —— RDMA 时代的事务 epoch handoff 范式

行业讨论

框架文档(代码 anchor)

  • src/transaction/aura/EpochManager.cc —— epoch 状态机
  • src/transaction/aura/MigrationCoordinator.cc —— 四阶段编排
  • src/transaction/aura/OwnerLockTable.cc::Acquire —— W14.5 dual-authority guard
  • src/transaction/aura/OwnerLockTable.cc::ReleaseAfterCommit —— I2 lock release timing

📎 v25 对照视角:模块二十三-AURA 论文精讲 第7章-迁移协议与I1-I4一致性证明 —— v25 用 4 个不变式(I1-I4),v27 合并 I3+I4 为单条 I3;增加 W14.5 处理 broadcast race window