ParticleDB测试发现96个缺陷,文档与实现存在矛盾

Jepsen测试发现ParticleDB存在96个缺陷。该数据库宣称经正式验证且支持全ACID,但实际缺乏TLA+规范,文档中关于持久性保证及隔离级别的描述也存在矛盾。

背景:多系统协调难题与 ParticleDB 的野心

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

测试发现96个单节点缺陷

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 协议状态机存在错误。

评论 0

0/500

评论需审核后展示,请文明发言

💬
还没有评论,来说两句

相关阅读