土法炼钢兴趣小组的算法知识备份

【FoundationDB 内核】写事务流水线:Client → Commit Proxy → Sequencer → Resolver → TLog

文章导航

分类入口
databasestorage
标签入口
#foundationdb#write-pipeline#commit-proxy#sequencer#resolver#tlog#unbundled#sigmod2021

目录

第 6 篇 Sequencer 已经说明 read version 与 commit version 如何由逻辑单点分配;本篇把它接进一次真实提交。问题不是「Proxy 做什么」,而是:客户端何时可以相信事务已提交,以及 Storage Server 何时一定能读到这笔提交——这两件事在 FoundationDB 里刻意不是同一时刻。

SIGMOD 2021 论文把写路径拆成 Transaction System(TS)与 Log System(LS),Storage System(SS)在后台消费 redo log。官方 7.x Architecture 文档进一步把 Proxy 分成 GRV Proxy(发 read version)与 Commit Proxy(编排提交)。本篇只走写事务提交那一段:Client → Commit Proxy → Sequencer(Master)→ Resolver → Transaction Log(TLog)。

本文是「FoundationDB 内核」系列第 7 篇(共 18 篇)。上一篇Sequencer下一篇Resolver 与 OCC→ 系列目录

版本锚定:FoundationDB 7.x;架构与提交步骤以官方 Architecture / Transaction Processing 文档与 Zhou et al., SIGMOD 2021 为准。不引用未经本机复现的吞吐或延迟数字。


一、问题:提交成功 ≠ 立刻在 Storage 上可见

Unbundled 架构的核心取舍是:把「事务排序与冲突检测」放在无状态 TS,「耐久性」放在 LS,「服务读」放在 SS。SIGMOD 2021 §2.3.2 写明:TS 做内存侧事务处理,LS 存 WAL,SS 存数据并服务读。

由此导出本篇要钉死的边界:

时刻 系统状态 客户端语义
Commit Proxy 收到客户端 commit 写仍在 Proxy 本地批里 未提交
Resolver 判定无冲突 仍可因后续步骤失败而中止 未提交
指定 TLog 副本全部 durable 并回复 Proxy 可向客户端返回成功 已提交
Storage Server 拉到并应用该版本 该副本可按该 version 读出 读路径可见(可能略滞后)

官方 Architecture 文档对 Commit Proxy 的三步描述与上表一致:向 Master 取 commit version → Resolver 判冲突 → 在 transaction logs 上 durable。Storage 拉取明确写在 Background Work,不在 commit 临界路径上。

sequenceDiagram
  participant C as Client
  participant CP as CommitProxy
  participant S as Sequencer
  participant R as Resolver
  participant L as TLog
  participant SS as StorageServer

  C->>CP: commit(readSet, writeSet)
  CP->>S: request commit version
  S-->>CP: commitVersion (+ prev LSN)
  CP->>R: conflict ranges
  R-->>CP: ok or conflict
  alt conflict
    CP-->>C: not_committed
  else no conflict
    CP->>L: persist mutations
    L-->>CP: durable ack
    CP-->>C: commit success
    L-->>SS: async pull / push path
  end

读路径不经过这条流水线:客户端拿 read version 后直接访问 Storage Server(第 11 篇)。本篇只回答写侧「何时 durable、何时可读」。


二、Client 侧:本地缓冲写,提交时打包冲突范围

事务开始时,客户端向 GRV Proxy 取 read version,随后对 Storage Server 做版本化读;set / clear 只缓存在客户端,不立刻进集群。SIGMOD 2021 §2.4.1 与 Developer Guide 都强调:写缓冲到 commit(),同事务内读见自己的写(read-your-writes)由客户端合并本地缓冲与远端读结果完成。

提交时客户端发给 Commit Proxy 的不只是 mutation 列表,还包括:

冲突范围如何被 Resolver 使用,见 第 8 篇。本篇只需记住:流水线携带的是范围集合,不是锁表。

@fdb.transactional(Python 绑定)在 not_committed 等可重试错误上自动重跑整段函数。官方 Developer Guide 明确:客户端内存里的普通赋值不会随事务回滚;应放在重试循环之外,除非接受冲突重试时再次执行。


三、Commit Proxy:批处理与三步编排

Commit Proxy 是写路径的编排者,本身无状态(角色状态随事务系统世代恢复重建)。官方 Architecture 列出它的职责:提交事务、向 Master 汇报已提交版本、维护 key range → Storage Server 映射供客户端缓存。

3.1 取 commit version

Proxy 向 Master / Sequencer 申请 commit version。SIGMOD 2021 §2.4.1:该版本必须大于任何已有的 read version 与 commit version;Sequencer 以约每秒一百万个版本的速率推进版本号(论文原文)。同时返回 previous LSN,让 Resolver 与 TLog 能按 LSN 无间隙串行处理。

批处理(§2.6):多个客户端事务可共用一次版本分配,再整批送 Resolver。负载低时缩小批以压延迟,负载高时增大批以摊销 Sequencer 调用。这是写吞吐扩展的关键手段之一——扩展靠加 Commit Proxy / Resolver / TLog,不是靠把 Sequencer 做成多活写者。

3.2 Resolver 冲突检测

Proxy 按 key 空间切分,把各片冲突范围发给对应 Resolver。官方文档:任一 Resolver 报冲突,整事务 not_committed。已知工程副作用:某一 Resolver 已把写集记入近期历史、另一 Resolver 判冲突导致中止时,可能对后续事务产生假阳性冲突;官方认为生产冲突率通常很低,且 5 秒 MVCC 窗口会过期冲刷历史(Architecture, Transaction Commit)。细节与 OCC 算法见第 8 篇。

3.3 写入 TLog

无冲突后,Proxy 查内存 shard map,给 mutation 打上目标 Storage Server 的 tag,再写入满足复制策略的一组 TLog。SIGMOD 2021 Figure 2:mutation 只发到 preferred LogServer 及额外副本以满足复制度,其余 LogServer 可收空包以保持版本序。日志头携带 LSN、previous LSN、以及该 Proxy 的 known committed version(KCV)

当指定副本全部回复 durable 后:

  1. Proxy 更新自身 KCV;
  2. 向 Sequencer / Master 汇报已提交版本,使后续 read version 不低于此提交;
  3. 向客户端返回成功。

这是客户端可见的提交点

flowchart TD
  A["Client commit RPC"] --> B["Commit Proxy batch"]
  B --> C["Sequencer: allocate commit version"]
  C --> D["Resolvers: OCC check"]
  D -->|conflict| E["Return not_committed"]
  D -->|ok| F["TLog: durable replicas"]
  F --> G["Report committed version"]
  G --> H["Reply success to client"]
  F -.-> I["Storage Server async apply"]

四、提交点与可读边界

4.1 耐久性在 TLog,不在 Storage Engine 的本地 WAL 语义

对客户端而言,「已提交」意味着 mutation 已按复制策略持久在 Log System 上。SIGMOD 2021 §2.4.3:Shipping redo log 到 SS 不是 commit path 的一部分。Storage Server 可以缓冲更新、延迟刷盘,因为耐久性已由 TLog 承担。

这与「每个存储节点先写本地 WAL 再 commit」的传统单机模型不同:FDB 的 WAL 是集群级、按 Storage 分片的分布式队列(论文称 LogServers 为 replicated, sharded, distributed persistent queues)。

4.2 读可见性:通常很快,但不与 commit 原子同拍

论文 §2.4.3 说明 Storage 会积极从 LogServer 拉 redo(甚至在 LS 完全 durable 之前也可拉,以降低多版本读延迟);若某副本尚不可读,客户端可等待、换副本,或收到可重试错误后重启事务。官方 Architecture 也写:在日志落盘前即可转发给负责该 mutation 的 Storage Server;Storage durable 后从 log 上 pop。

因此:

GRV Proxy 发放的 read version 来自 Master 上记录的最大已提交版本,并校验 transaction log 集合仍满足复制策略且未被停掉(防止旧世代 Proxy 发出版本)。严格可串行化如何由此成立,见 第 9 篇

4.3 空提交与版本推进

官方 Background Work:在无客户端事务时,Commit Proxy 仍会向 TLog 发空提交,保持 commit version 前进。这避免版本停滞影响租约与恢复逻辑,也说明「版本推进」不完全等同于「用户有写」。


五、批处理、空提交与限流如何咬合流水线

5.1 动态批

论文 §2.6:Proxy 把多个客户端事务收成一批,只向 Sequencer 要一次 commit version,再整批做冲突检测与日志写入。批大小随负载伸缩——轻载缩小以改善延迟,重载增大以保护 Sequencer。机制含义是:客户端感知的「一次 commit RPC」在 Proxy 内部可能与邻居事务共享版本分配往返,但每个事务仍有自己的冲突范围与成败结果。

5.2 空提交

无用户事务时,Commit Proxy 仍向 TLog 写入空提交以推进版本(Architecture, Background Work)。这对理解监控有帮助:TLog 上版本在动,不等于业务 QPS 非零;反过来,版本停滞更值得当作事务系统健康信号(第 17 篇会接到排障树)。

5.3 Ratekeeper 不挡在 TLog 之后

Ratekeeper 主要通过降低 GRV Proxy 发放 read version 的速率来限流(Architecture)。新事务更难「开始」,从而间接降低进入 Commit Proxy 的压力。它不是在 TLog durable 之后再拒绝已验证事务的主路径——过载保护发生在流水线更早的入口。


六、与 TiKV 写路径的机制对照(不比性能)

维度 FoundationDB(本篇) TiKV Percolator(站内对照)
提交编排 Commit Proxy 中心化三步 客户端驱动 Prewrite / Commit,按 Region 并发
冲突检测 Resolver 上 OCC,提交时一次性 Prewrite 时查 lock / write CF
耐久性 TLog 复制队列 Region Raft 日志
读路径 直达 Storage Server 通常走 Region Leader(或 follower 读配置)
隔离默认 严格可串行化 快照隔离(可叠加悲观锁)

对照目的是分清协调点放在哪:FDB 把排序与冲突收到 TS;TiKV 把锁与写状态编码进每 Region 的 MVCC。不在此比较谁更快——本系列无实测则不下性能结论。TiKV 细节见 tikv-htap 第 10 篇

一次计数器自增在两边的形态差异,也能说明流水线位置不同。FDB 侧(Developer Guide 风格)是单事务读改写,冲突由 Resolver 在提交时判定;TiKV 侧是 Prewrite 锁 + Commit 两阶段。FDB 若改用原子 add,则连「读」都不进 read conflict range,流水线仍走完 TLog,但 OCC 压力更小——这是 API 选择对流水线负载的影响,不是另一条提交协议。


七、学术谱系、工程间隙与开放问题

谱系:Lomet & Mokbel 等提出的 unbundled / Deuteronomy 思路(把事务管理与数据管理拆开)是论文 Related Work 中的参照系;FoundationDB SIGMOD 2021 的分叉是:MVCC 数据放在 SS 而非 TS,OCC + 全局 Sequencer 版本实现严格可串行化,并用确定性模拟兜住故障组合。Kung & Robinson 的 OCC(ACM TODS 1981)与 Bernstein 等人的 MVCC 教材模型,是 Resolver / 版本读的经典来源。

工程间隙:论文微基准称单线程 Resolver 可处理大量 TPS 量级的随机范围读写冲突检测,但那是冲突检测微基准,不是端到端集群吞吐,且依赖低冲突假设。生产上假阳性冲突、Resolver 分片边界、批大小与 Ratekeeper 限流如何耦合,论文未给出可直接搬用的运维公式——第 16、17 篇 只讨论可观察症状,不编造容量公式。

开放问题:durable 与 readable 解耦之后,Storage 落后尾延迟在何种负载下会把「严格可串行化读」逼成大量重试?论文 Figure 3 给出某生产集群上 Storage 相对 Log 的滞后分布,但那是特定部署快照,本系列不把它写成普适 SLA。可读入口仍是 SIGMOD 2021 §2.4.3 与官方 Architecture 的 Background Work 描述。


八、失败模式在流水线上的落点

现象 通常落在哪一跳 客户端常见错误
读集过期 Resolver not_committed
事务超过约 5 秒 版本窗口 / Storage 拒旧版本 transaction_too_old
TLog 或 Proxy 世代切换 整代 TS 恢复 提交失败或 commit_result_unknown
问错 Storage 后缓存过期 读路径(非本篇主线) 定位重试,不必然 abort 写

commit_result_unknown 值得单独记:Architecture 说明恢复可能发生在 Proxy 已向 TLog 发送 mutation 之后、客户端尚未收到成功之前。此时重试必须假设「第一次可能已提交」。流水线提供的是耐久性与版本协议,不自动提供业务幂等——幂等是 Layer / 应用契约(第 15、17 篇会回到这一点)。


九、常见误解

误解 事实
「提交成功表示所有 Storage 已写完」 成功只要求 TLog 侧按策略 durable;Storage 异步应用
「写也要经过 Storage 才能返回」 写路径是 Proxy → Resolver → TLog;读才直达 Storage
「Sequencer 参与每一字节的复制」 Sequencer 分配版本;复制与落盘在 TLog
「多个 Commit Proxy 各自独立排序」 版本全序来自 Sequencer;Proxy 只是并行编排入口
「空提交没有意义」 用于推进版本并维持事务系统健康假设
「GRV Proxy 与 Commit Proxy 可互相替代」 7.x 文档中两者职责分离:发读版本 vs 编排提交

十、小结

三句话小结

  1. 写事务提交由 Commit Proxy 编排:Sequencer 发 commit version → Resolver OCC → TLog 指定副本 durable 后向客户端返回成功。
  2. 「已提交」锚定在 Log System 的耐久性上;Storage Server 异步拉日志,读可见性可能略滞后于提交返回。
  3. 读写路径分离使读扩展与写扩展解耦;严格可串行化依赖版本分配与冲突检测的组合,下一篇拆 Resolver。

下一篇进入 Resolver 与 OCC:read/write conflict ranges、冲突窗口、自动重试,以及与 TiKV Percolator SI 的对照。


上一篇Sequencer:全局版本与恢复

下一篇Resolver 与 OCC

返回 系列目录


参考资料

论文(A 级)

  1. Zhou, Jingyu et al. FoundationDB: A Distributed Unbundled Transactional Key Value Store. SIGMOD 2021(§2.3–2.4 写路径、Figure 1–2、批处理与 atomic ops)。
  2. Kung, H. T., Robinson, John T. On Optimistic Methods for Concurrency Control. ACM TODS 1981(OCC 经典定义)。

官方文档(A 级)

  1. FoundationDB 7.x, Architecture — Commit Proxies、Transaction Commit、Background Work、commit_result_unknown
  2. FoundationDB 7.x, Developer Guide — 事务、冲突、@transactional 重试语义。

站内对读

  1. 第 6 篇 Sequencer第 8 篇 Resolver第 10 篇 TLog
  2. tikv-htap 第 10 篇 Percolator
  3. distributed/39 FoundationDB 段落(选型概览,不复述其性能表)。

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .