背景:多系统协调难题与 ParticleDB 的野心
现代应用程序往往需要在数据库、缓存、队列、数据仓库和搜索索引等多种系统中扩展状态。这种架构带来了巨大的协调挑战:缓存可能过时,分析平台可能滞后于主数据库数天,搜索索引可能丢失数据,而跨系统的交易更新至今仍是开放的研究问题。

ParticleDB 1 试图通过单一引擎统一这些工作负载,特别针对人工智能代理和应用场景进行了优化。它提供事务性 SQL、键值存储、向量搜索、实时分析以及被称为“粒子”的用户定义函数。为了兼容现有生态系统,客户端可通过 Postgres 和 Redis 协议,以及 gRPC 和 HTTP2 API 进行连接。基于 Rust 构建的 ParticleDB 声称在保持同等安全特性的前提下,性能优于 Postgres。
开发历程与测试范围
ParticleDB 由 Orkes 开发,旨在支持其工作流编排平台 Conductor。Conductor 最初依赖 Redis 和 Postgres 存储数据,但为提升性能,Orkes 决定自研数据库。ParticleDB 1.0.0 版本提供了专为 Conductor 查询设计的有限功能集,并作为部分集群的后端部署,少数客户已采用该方案。
为准备更广泛的发布,Orkes 对 ParticleDB 进行了重大重构,增加了自定义网络协议、更多数据类型及额外的 OLAP 查询功能。在继续运行 1.0.0 版本的同时,Orkes 在 particledb.ai 上软发布了重构后的版本,并聘请 Jepsen 从 1.1.4 版本开始对其进行严格测试。
尽管 ParticleDB 支持使用 Raft 共识协议进行集群复制,但该功能仅在企业云部署中可用。由于 Orkes 提供的测试构建不支持复制,本次报告仅涵盖单节点行为。
安全性声明与文档矛盾
ParticleDB 首页宣称其产品经过“正式验证”,具备 TLA+ 验证能力。然而,在与 Jepsen 的合作期间,并未发现相关的 TLA+ 规范。目前尚不清楚具体证明了哪些属性,或者这一描述是否准确反映了产品的实际状态。
在写入日志(WAL)同步策略方面,ParticleDB 提供三种模式:sync(每次提交时 fsync)、groupsync(将多个事务分组为单个 fsync)和 nosync(完全跳过 fsync)。关于 groupsync 的持久性保证,不同来源存在冲突:交易引擎文档称其提供“比群同步更强大的耐用性”;而 particledb start --help 及内部操作指南则表示组合模式保证“COMMIT 等待冲洗”,即提供与同步模式相当的持久性。
此外,ParticleDB 自豪地宣称实现“全 ACID”特性,但在下方文本中又提及“快照隔离”,这并非完全隔离级别。大多数 SQL 数据库允许通过 SET TRANSACTION ISOLATION LEVEL 语句控制并发语义,但 ParticleDB 忽视或拒绝此类语句,有时甚至默默忽略用户指定的隔离级别请求。
事务模式与一致性模型解析
相反,事务隔离级别由启动时的 --txn-mode 设置控制。官方文档描述了四种主要模式,但其定义晦涩难懂:
| Mode | 行为描述 | 适用场景 |
|---|---|---|
fast | 无锁;书面形式提交 | 大量装载,单线程基准测试 |
occ | 覆盖 + 提交时间 OCC 验证 (默认) | 一般 OLTP,号称在并发条件下安全 |
zero-copy-occ | 分区钉住的 OCC;单个分区 txns 跳过跨分区协调 | 分区相关工作负载上的高通量 OLTP |
table-2pl | 首次触摸时独占表锁 | 最严格的序列化,简单语义 |
最佳实践文档警告用户应“故意选择 --txn-mode”,因为它比 SQL 文本更能改变正确性和吞吐量特征,但未详细解释各模式的具体保证。fast 模式显然用于较弱隔离,但具体是读原子还是其他模型并不清楚。occ 模式的描述暗示其可能是序列化的,但缺乏明确界定。zero-copy-occ 未被标记为“在并发条件下安全”,引发了对其潜在风险的疑问。若 table-2pl 提供“最严格的序列化”,则是否存在更弱的隔离级别也未说明。
CLI 参考页面进一步混淆了概念,将 occ 和 table-2pl 标记为“Jepsen-safe”,而 fast 不是,但未解释安全性质。另一张表格显示 row-2pl 是 occ 的别名,且提到“表锁”,这与乐观并发控制(OCC)避免锁的目的相悖。“对大多数 OLTP 来说安全”的说法也破坏了此前“在竞争条件下安全”的承诺。
Orkes 澄清称文档存在错误:occ 实际上是乐观的,row-2pl 别名是为了兼容早期的双相锁定机制。一份未发表的内部操作指南明确了以下一致性模型:
| Mode | 一致性模型 |
|---|---|
fast | 未知 |
occ, occ-v2, row-2pl | 快照隔离 |
zero-copy-occ | 快照隔离 |
serializable-ssi | 可序列化 |
strict-2pl (a.k.a. serializable) | 强序列化 |
消息来源对默认事务模式和 WAL 同步模式存在分歧,但通常认为 occ-v3 和 groupsync 是默认配置。
测试环境与工作负载设计
Jepsen 团队使用其测试库为 ParticleDB 编写了一套测试套件,在独立的 Debian 13 节点上运行版本 1.1.4 至 1.1.18.11.rc-22,环境包括容器和 EC2 虚拟机。测试框架模拟了进程崩溃、未 fsync 写入丢失(使用 LazyFS)以及磁盘文件截断或损坏。测试分别以每种事务模式和全部三种 WAL 同步模式运行。
初期尝试使用官方 Java SDK,但因 Maven 依赖不可用而受阻,转而使用 Postgres JDBC 客户端(版本 42.7.11),后续增加了对 ParticleDB Java SDK 1.0.0 至 1.0.2 的支持。
测试采用了多种工作负载:
- rw 工作负载:执行随机生成的读写事务,通过唯一整数键识别整数值。假设每个事务中写入跟随读取,利用 Elle 检查器在依赖图中搜索周期和非周期性现象(如 G1a 中断阅读)。
- 添加工作负载:在行上执行随机事务,值为整数列表。通过 SQL concat 添加唯一整数到列表中,利用间接表连接进行选择,并使用 Elle 推断版本顺序。
- 内部一致性工作负载:当单事务出现违规时,执行一系列 INSERT, UPDATE, DELETE 和 SELECT 语句,验证读取是否观察到先前写入。
- 比较工作负载:使用 Clojure test.check 生成随机表和 SQL 语句,对比 ParticleDB 与 PostgreSQL 的行为差异,以复现分歧。
- 混合内部 SIM 工作负载:生成随机表并并发执行事务,独立检查每笔交易的内部一致性。
发现的问题分类
在 ParticleDB 中发现了 96 个问题,除三个外均发生在单节点实例中。问题类别包括:
- 可用性和性能
- 缺陷的特征
- Predicates
- Constraints
- Joins
- Zombies
- State Corruption
- Transactions and Concurrency
- With Faults
附录 A 还展示了类型安全和语法、算术、字符串等方面的额外结果。
可用性与性能严重受损
在正常操作下,ParticleDB 表现缓慢,频繁死锁,并在没有警告的情况下崩溃。
table-2pl 模式下的糟糕性能 (#62)
在 1.1.4 至 1.1.18.4 版本中,--txn-mode table-2pl 极其缓慢,几乎不可用。虽然部分事务可能在几毫秒内完成,但大多数耗时 10 到 30 秒后失败。例如,在 1.1.18.4 版本中,十个并发客户端大多阻塞数十秒,抛出 could not serialize access due to concurrent update 或 IO 错误。高延迟问题在 1.1.18.6 版本中有所改善,至少能在几秒内执行亚 100 毫秒延迟的事务。
神秘崩溃 (#81)
1.1.18.6 版本虽改善了延迟,但频繁崩溃。崩溃在不同事务模式、WAL 模式和客户端接口下自发发生,无明显原因。节点运行数百至数千秒后退出,未打印错误信息。此问题在 1.1.18.11.rc-18 中不再出现。
两个事务后的死锁 (#27)
在 1.1.18.8 版本中,向健康的单节点部署提交仅两个并发事务即可导致死锁。Postgres 和 ParticleDB 接口均受影响。节点记录类似 [DEADLOCK] 1 cycles detected... 的错误及空堆栈跟踪。部分情况下新客户端可连接,其他情况下则无限挂起。使用 append 工作负载时,1.1.18.9 版本通常无法运行超过 45 秒。1.1.18.11.rc-1 版本能可靠运行两分钟,该问题被宣告解决。
table-2pl 模式下的死锁 (#71)
尽管 1.1.18.11.rc-1 修复了常见死锁,但在运行 table-2pl 时,直至 1.1.18.11.rc-6 仍发现死锁。节点执行约 30 秒事务后永久卡住,记录类似 #27 的死锁错误,ParticleDB 客户端请求在 100 秒超时后抛出 PDB JDBC: null。此问题在 1.1.18.11.rc-7 中似乎已解决。
功能缺失与协议兼容性破裂
Orkes 公开文档声称广泛支持 Postgres 特性,称引擎原生支持 PostgreSQL v3 线协议,任何 pgjdbc/libpq/psycopg2/pq 客户端均可无缝连接,BEGIN/COMMIT/ROLLBACK、预编译语句等均按预期工作。然而,实际情况较为乐观偏差,ParticleDB 缺失大量 Postgres 特性,且部分功能严重损坏。
CASE 表达式中不支持聚合 (#20)
在 1.1.4 至 1.1.18.4 版本中,CASE 表达式内不支持 COUNT(*) 等聚合函数,抛出 Error: cannot evaluate wildcard expression。1.1.18.6 版本增加了对该特性的支持。
不支持幂运算 (#28)
1.1.4 至 1.1.18.8 版本不支持 Postgres 幂运算符 ^,执行 SELECT 2 ^ 3; 报错 unsupported binary operator: PGExp。1.1.18.9 版本添加了对该运算符的支持。
预编译语句响应格式错误 (#2)
在 1.1.4 至 1.1.18.9 版本中,使用 Postgres JDBC 驱动执行预编译语句会导致异常,如 NoSuchElementException at java.util.ArrayDeque/removeFirst。由于 JDBC 驱动在几次调用后自动切换至预编译语句,这使得数据库基本不可用。Jepsen 团队不得不禁用客户端的预编译语句功能作为变通方案。Orkes 认为问题在于 ParticleDB 未能返回扩展查询协议(Parse, Bind, Execute 阶段)中的某个响应或返回了多余消息,导致客户端队列下溢。此问题在 1.1.18.11.rc-1 中似乎已修复。
大多数 Postgres 查询失败 (#83)
在 1.1.18.11.rc-7 至 1.1.18.11.rc-19 版本中,许多使用 Postgres 接口的查询抛出 Bad value for type long: some_column 错误,使数据库大部分不可用。例如创建表并查询时出错。Orkes 认为此问题在 1.1.19-rc.12 中已修复。
元组缺少字段结构 (#111)
在 1.1.18.11.rc-18 和 rc-19 版本中,Postgres 接口查询偶尔抛出 Received resultset tuples, but no field structure for them 错误。这表明 ParticleDB 的 Postgres 协议状态机存在错误。





