系统设计 / CRDT 可变树 · 多副本层级如何无冲突收敛 / undo-do-redo 重放 待审核 4 / 5
undo-do-redo · 乱序也收敛

undo-do-redo 重放

祖先检查保证了单次应用不成环,但分布式下操作会乱序到达。每个副本给操作打一个全序时间戳,采用 Lamport 时钟的「计数器加副本 id」形式,计数器相同再比副本 id 打破平局,并维护一份按时间戳升序的 op log。关键技巧是:当一个时间戳较小的 op 迟到,不能直接追加,而要先把 log 末尾所有时间戳更大的 entry 依次 undo 回去、do 这条迟到的 op、再按时间戳升序 redo 回来。

1 · 三种到达顺序下的重放

这样无论 op 以什么顺序到达,每个副本的 log 始终维持同一个时间戳升序序列,等价于一次性按时间戳顺序从头重放,于是结果只由这组 op 决定而与到达顺序无关。并发地 move 同一节点时,就由时间戳最大者胜出,即 LWW。

图 1-1 · 同一组 op 在三种到达顺序下的重放。可单步观察 undo、do 与 redo 三个阶段,并核对三种顺序的最终树是否一致。

警示 · 等价于「按时间戳顺序重放」这一点要归纳来看:每次 applyOp 结束时 log 都恰好是「已知全部 op 按时间戳升序」的序列,且树正好是把这个序列从头 doOp 一遍的结果。新 op 无论何时到达,undo-do-redo 都把它插回它该在的时间戳位置,并让它之后的 op 在新状态下重新 doOp。所以同一条 op 在不同重放里可能这次生效、那次因成环被拒,但最终序列唯一,最终树也就唯一,这正是强最终一致性(strong eventual consistency)。

把祖先检查与 undo-do-redo 装进多个副本、再配一条可乱序的网络,就是多副本模拟器