<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>jepsen.io</title>
  <subtitle>Browse aymeric&#39;s, an independent, searchable archive of readable snapshots from followed feeds, preserved in Git with aggr.</subtitle>
  <id>https://aggr.aymericbeaumet.com/sources/jepsen.io/</id>
  <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/sources/jepsen.io/"/>
  <link rel="self" type="application/atom+xml" href="https://aggr.aymericbeaumet.com/sources/jepsen.io/atom.xml"/>
  <updated>2026-10-31T13:39:52+00:00</updated>
  <author>
    <name>aymeric&#39;s</name>
  </author>
  <generator version="2.0.0" uri="https://github.com/aymericbeaumet/aggr">aggr</generator>
  <entry>
    <id>urn:aggr:item:d268757d1664fd4a5cbf85a74de7f0226865469d</id>
    <title type="text">Jepsen: ParticleDB 1.1.4</title>
    <link rel="alternate" type="text/html" href="https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-31-jepsen-particledb-1-1-4/"/>
    <link rel="via" type="text/html" href="https://jepsen.io/analyses/particledb-1.1.4"/>
    <published>2026-10-31T13:39:52+00:00</published>
    <updated>2026-10-31T13:39:52+00:00</updated>
    <author>
      <name>jepsen.io by aphyr</name>
    </author>
    <category term="news"/>
    <category term="databases"/>
    <category term="testing"/>
    <source>
      <title>Lobsters: Top Stories of the Past Week</title>
    </source>
    <summary type="text">Comments</summary>
    <content type="html">&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://particledb.ai&quot;&gt;ParticleDB&lt;/a&gt; is a multi-model database designed for AI workloads. We tested versions 1.1.4 through 1.1.18.11.rc-22 and found that ParticleDB’s claims of “Full ACID” were somewhat premature. We present 96 issues in ParticleDB, ranging from the pedestrian (crashes, deadlocks, data loss, etc.) to the piquant (partially-invisible rows which could not be deleted, &lt;code&gt;WHERE 4 = 5&lt;/code&gt;, dropped tables re-emerging in distorted forms, confused columns, strings turning into integers, and so on). Most issues required just a single server, single client, and no faults. By version 1.1.18.11.rc-22, all but ten of those issues appeared resolved. However, ParticleDB continued to lose committed writes during normal operation with the default &lt;code&gt;occ&lt;/code&gt; transaction mode, and allowed internal anomalies and Lost Update with &lt;code&gt;serializable&lt;/code&gt;, the strongest setting. Orkes, the makers of ParticleDB, have since released version 1.1.19-rc.13; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://orkes.io/blog/particledb-database-built-for-ai-agents&quot;&gt;they claim&lt;/a&gt; that it resolves all remaining issues discussed in this report. This research was funded by Orkes, Inc., and conducted in accordance with the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/ethics&quot;&gt;Jepsen ethics policy&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;Modern applications often spread state across a variety of systems: databases, caches, queues, data warehouses, search indices, and so on. Coordinating these systems is challenging: caches are often stale, the analytics platform might lag days behind the primary database, the search index might lose data, and transactional updates across disparate systems &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.semanticscholar.org/paper/Typhon%3A-Consistency-Semantics-for-Data-Processing-Arora-Nawab/018ce7bfc890712fa62ab988fe0703bccb806907&quot;&gt;remains an open research problem&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;ParticleDB&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fnref-1&quot; data-footnote-ref&gt;1&lt;/a&gt;&lt;/sup&gt; aims to unify these workloads in a single engine with a special focus on AI agents and applications. It offers transactional SQL, a key-value store, vector search, live analytics, and user-defined functions called &lt;em&gt;Particles&lt;/em&gt;.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fnref-2&quot; data-footnote-ref&gt;2&lt;/a&gt;&lt;/sup&gt; To integrate with existing systems, clients can connect through &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://particledb.ai/connectivity/overview/&quot;&gt;a variety of interfaces&lt;/a&gt;, including the Postgres and Redis protocols, as well as its own gRPC and HTTP2 APIs. Written from the ground up in Rust, ParticleDB claims to be orders of magnitude faster than &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.postgresql.org/&quot;&gt;Postgres&lt;/a&gt; while preserving the same safety characteristics.&lt;/p&gt;
&lt;p&gt;ParticleDB was built by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://orkes.io/&quot;&gt;Orkes&lt;/a&gt; to support their workflow orchestration platform, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/orkes-conductor-5.3.2&quot;&gt;Conductor&lt;/a&gt;. Conductor was initially built to store data in Redis and Postgres, but to improve performance, Orkes opted to write their own database. ParticleDB 1.0.0 offered a limited feature set specifically for Conductor’s queries, and was deployed as the backing database for some of their Conductor clusters. A few Orkes customers followed suit. In preparation for a broader release, Orkes undertook a major refactoring of ParticleDB, adding a custom wire protocol, more datatypes, additional OLAP queries, and more. While Orkes and their customers continued to run 1.0.0, they soft launched the refactored versions on &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://particledb.ai&quot;&gt;particledb.ai&lt;/a&gt;. Orkes engaged Jepsen to test these versions of ParticleDB, beginning with 1.1.4.&lt;/p&gt;
&lt;p&gt;While ParticleDB could run as a single node, it also offered a replicated cluster using the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://raft.github.io/&quot;&gt;Raft&lt;/a&gt; consensus protocol. This replication feature was available only in enterprise cloud deployments. The builds Orkes provided did not support replication, so this report covers only single-node behavior.&lt;/p&gt;
&lt;h2&gt;Safety&lt;/h2&gt;
&lt;p&gt;ParticleDB’s home page &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518203012/https://particledb.ai/&quot;&gt;advertised&lt;/a&gt; that ParticleDB was “formally proven” with a TLA+ proof. However, during our collaboration no TLA+ specification was available. It is unclear what properties might be proven, or whether they described ParticleDB as built.&lt;/p&gt;
&lt;p&gt;ParticleDB offered &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518193013/https://particledb.ai/architecture/transaction-engine/#wal-sync-modes&quot;&gt;three tactics&lt;/a&gt; for syncing its Write-Ahead Log (WAL). The &lt;code&gt;sync&lt;/code&gt; mode called &lt;code&gt;fsync&lt;/code&gt; on each commit, &lt;code&gt;groupsync&lt;/code&gt; batched multiple transactions into a single &lt;code&gt;fsync&lt;/code&gt;, and &lt;code&gt;nosync&lt;/code&gt; skipped &lt;code&gt;fsync&lt;/code&gt; altogether. The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518193013/https://particledb.ai/architecture/transaction-engine/#wal-sync-modes&quot;&gt;Transaction Engine documentation&lt;/a&gt; described &lt;code&gt;sync&lt;/code&gt; as offering “stronger durability” than &lt;code&gt;groupsync&lt;/code&gt;. On the other hand, &lt;code&gt;particledb start --help&lt;/code&gt; said the &lt;code&gt;groupsync&lt;/code&gt; mode ensured that “COMMIT waits for flush”, and Orkes’ internal operator guide concurred: &lt;code&gt;groupsync&lt;/code&gt; offered the “same durability as &lt;code&gt;sync&lt;/code&gt;”.&lt;/p&gt;
&lt;p&gt;ParticleDB’s home page proudly claimed “Full ACID. 159K TPS.” However, the text immediately below this said “snapshot isolation”, which is &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/models/snapshot-isolation&quot;&gt;not fully isolated&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most SQL databases use the &lt;code&gt;SET TRANSACTION ISOLATION LEVEL&lt;/code&gt; statement to control the concurrent semantics of a transaction. However, ParticleDB &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518193013/https://particledb.ai/architecture/transaction-engine/#transaction-modes&quot;&gt;ignored or rejected these statements&lt;/a&gt;. In some cases it instructed users to choose &lt;code&gt;READ COMMITTED&lt;/code&gt;, a relatively weak isolation level. In other cases it accepted requests for different isolation levels, and silently ignored them (despite documentation to the contrary).&lt;/p&gt;
&lt;p&gt;Instead, transaction isolation was governed by the &lt;code&gt;--txn-mode&lt;/code&gt; setting passed at startup. The transaction engine documentation &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518193013/https://particledb.ai/architecture/transaction-engine/#transaction-modes&quot;&gt;described four modes&lt;/a&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fast&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No locks; commit-on-write&lt;/td&gt;
&lt;td&gt;Bulk loads, single-writer benchmarks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;occ&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overlay + commit-time OCC validation (default)&lt;/td&gt;
&lt;td&gt;General OLTP — safe under concurrency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zero-copy-occ&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partition-pinned OCC; single-partition txns skip cross-partition coordination&lt;/td&gt;
&lt;td&gt;High-throughput OLTP on partition-affine workloads (TPC-C, key-value)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;table-2pl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exclusive table locks at first touch&lt;/td&gt;
&lt;td&gt;Strictest serialization, simple semantics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This table was difficult to interpret. While the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518193127/https://particledb.ai/sql/transactions/#best-practices&quot;&gt;best practices documentation&lt;/a&gt; warned that users should “pick &lt;code&gt;--txn-mode&lt;/code&gt; deliberately”, since “it changes correctness and throughput characteristics more than SQL text does”, the documentation did not explain what these modes actually guaranteed. The &lt;code&gt;fast&lt;/code&gt; mode was clearly intended for weaker isolation, but was that Read Atomic, Read Uncommitted, or something else? The &lt;code&gt;occ&lt;/code&gt; mode was similarly unclear. “Commit-time OCC validation” could describe a broad spectrum of consistency models from Read Uncommitted to Strong Serializable, but “safe under concurrency” suggests it might be Serializable. The &lt;code&gt;zero-copy-occ&lt;/code&gt; mode described a performance optimization which could be as safe as &lt;code&gt;occ&lt;/code&gt;, but it was &lt;em&gt;not&lt;/em&gt; described as “safe under concurrency”—raising the question: in what ways was it unsafe? Finally, if &lt;code&gt;table-2pl&lt;/code&gt; offered the “strictest serialization”, was &lt;code&gt;occ&lt;/code&gt; some weaker isolation level?&lt;/p&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518193140/https://particledb.ai/operations/cli-reference/&quot;&gt;CLI reference page&lt;/a&gt; contained another table which described &lt;code&gt;occ&lt;/code&gt; and &lt;code&gt;table-2pl&lt;/code&gt; as “Jepsen-safe”, whereas &lt;code&gt;fast&lt;/code&gt; was not “Jepsen-safe”, but did not explain their safety properties. Since Jepsen evaluates safety with respect to documentation and marketing claims, it is not clear how to interpret these terms.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fnref-3&quot; data-footnote-ref&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Running &lt;code&gt;particledb start --help&lt;/code&gt; provided the following table:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fast&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No locks, maximum OLTP throughput (unsafe under concurrency)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;occ&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Table locks + commit-time validation (safe for most OLTP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;row-2pl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alias for &lt;code&gt;occ&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zero-copy-occ&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partition-pinned OCC (highest throughput safe mode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;table-2pl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exclusive table locks, full serialization (strictest)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;serializable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Alias for &lt;code&gt;table-2pl&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;serializable-ssi&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adaptive SSI (Cahill 2008), PL-3 with optimistic semantics (v1 scaffolding — see SSI_ADAPTIVE_DESIGN.md)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The point of Optimistic Concurrency Control (OCC) is generally to &lt;em&gt;avoid&lt;/em&gt; taking locks, so the mention of table locks and a &lt;code&gt;row-2pl&lt;/code&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-4&quot; id=&quot;fnref-4&quot; data-footnote-ref&gt;4&lt;/a&gt;&lt;/sup&gt; alias was surprising. Moreover, “safe for most OLTP” undermines the claim that &lt;code&gt;occ&lt;/code&gt; was “safe under concurrency”; it suggests there are some OLTP workloads which are unsafe!&lt;/p&gt;
&lt;p&gt;Orkes explained that this documentation was wrong: &lt;code&gt;occ&lt;/code&gt; was actually optimistic, and the &lt;code&gt;row-2pl&lt;/code&gt; alias was to preserve compatibility with an earlier two-phase locking mechanism. They also provided an unpublished operator guide with detailed descriptions of the various transaction modes. This document was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.tiktok.com/@charlixcx/video/7603137654682570006&quot;&gt;difficult to interpret&lt;/a&gt;, but did specify the following consistency models:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Consistency Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fast&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unknown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;occ-v3&lt;/code&gt; (a.k.a. &lt;code&gt;occ&lt;/code&gt;, &lt;code&gt;occ-v2&lt;/code&gt;, &lt;code&gt;row-2pl&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Snapshot Isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;zero-copy-occ&lt;/code&gt; (a.k.a. &lt;code&gt;zocc&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Snapshot Isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;serializable-ssi&lt;/code&gt; (a.k.a. &lt;code&gt;ssi&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Serializable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;table-2pl&lt;/code&gt; (a.k.a. &lt;code&gt;serializable&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Strong Serializable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Sources disagreed about the default transaction and WAL sync modes, but in general, &lt;code&gt;occ-v3&lt;/code&gt; and &lt;code&gt;groupsync&lt;/code&gt; appeared to be the default.&lt;/p&gt;
&lt;h2&gt;Test Design&lt;/h2&gt;
&lt;p&gt;We wrote a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/particledb&quot;&gt;test suite&lt;/a&gt; for ParticleDB using the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/jepsen&quot;&gt;Jepsen testing library&lt;/a&gt;. We ran ParticleDB versions 1.1.4 through 1.1.18.11.rc-22 on individual Debian 13 nodes, both in containers and EC2 VMs. Our test harness could simulate process crashes by killing ParticleDB, simulate the loss of un-&lt;code&gt;fsync&lt;/code&gt;ed writes using &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/dsrhaslab/lazyfs&quot;&gt;LazyFS&lt;/a&gt;, and truncate or corrupt disk files. We ran ParticleDB using each transaction mode and all three WAL sync modes.&lt;/p&gt;
&lt;p&gt;We first tried ParticleDB’s official Java SDK. While the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518203023/https://particledb.ai/sdks/java/&quot;&gt;documentation indicated&lt;/a&gt; that the library was available as a Maven dependency, neither the JAR nor source code were actually available. Instead, we began with the official Postgres JDBC client, version 42.7.11, and later added support for the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://particledb.ai/sdks/java/&quot;&gt;ParticleDB Java SDK&lt;/a&gt;, versions 1.0.0 through 1.0.2.&lt;/p&gt;
&lt;h2&gt;Workloads&lt;/h2&gt;
&lt;p&gt;We used several Jepsen workloads from other SQL databases, and designed new ones specifically for ParticleDB.&lt;/p&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/sql/blob/81019139aa4bdc4b597cf9b63fa1f0c6b697a753/src/jepsen/sql/workload/rw.clj&quot;&gt;&lt;code&gt;rw&lt;/code&gt; workload&lt;/a&gt; performed randomly generated transactions involving reads and writes of integer values identified by unique integer keys. We stored each value in a single row and accessed them both via primary and unindexed secondary keys. Keys and values were encoded as a variety of datatypes, including integers, decimals, and strings. We assumed that writes followed reads within each transaction, and that individual keys were sequentially consistent, and used these assumptions to infer constraints on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/dependencies&quot;&gt;version orders and transaction dependency graphs&lt;/a&gt;. Jepsen’s transactional checker, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/elle&quot;&gt;Elle&lt;/a&gt;, then searched for cycles in that dependency graph, as well as a variety of non-cyclic phenomena like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/phenomena/g1a&quot;&gt;G1a (Aborted Read)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/sql/blob/81019139aa4bdc4b597cf9b63fa1f0c6b697a753/src/jepsen/sql/workload/append.clj&quot;&gt;&lt;code&gt;append&lt;/code&gt;&lt;/a&gt; workload also performed randomly generated transactions over rows identified by unique integer keys, but the values were lists of integers. Transactions could read one of these lists by key, or append a unique integer to a list by key. We encoded each list as a comma-separated &lt;code&gt;text&lt;/code&gt; field, and used SQL &lt;code&gt;concat&lt;/code&gt; to append to them. Rows could be selected via primary key or unindexed secondary key, or via a join to an indirection table. From there, we used Elle to infer version orders and dependency graphs, and searched for a variety of cyclic and non-cyclic phenomena.&lt;/p&gt;
&lt;p&gt;When these workloads revealed consistency violations within a single transaction, we designed an &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/sql/blob/81019139aa4bdc4b597cf9b63fa1f0c6b697a753/src/jepsen/sql/workload/internal.clj&quot;&gt;&lt;code&gt;internal&lt;/code&gt;&lt;/a&gt; workload to stress internal consistency. This workload performed a series of randomly generated transactions involving &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, and &lt;code&gt;SELECT&lt;/code&gt; statements over a simple key-value table, and verified that within each individual transaction, reads observed prior writes.&lt;/p&gt;
&lt;p&gt;To investigate tantalizing anomalies in the &lt;code&gt;internal&lt;/code&gt; workload, we designed the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/particledb/blob/e804ab8b0528b089b908b6249d2e1e4ac966802f/src/jepsen/particledb/workload/compare.clj&quot;&gt;&lt;code&gt;compare&lt;/code&gt;&lt;/a&gt; workload. This workload used Clojure’s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/clojure/test.check&quot;&gt;test.check&lt;/a&gt; generative testing library to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/particledb/blob/e804ab8b0528b089b908b6249d2e1e4ac966802f/src/jepsen/particledb/gen/tc.clj&quot;&gt;produce&lt;/a&gt; random tables and simple SQL statements over them, then executed those statements against both ParticleDB and PostgreSQL. If ParticleDB’s behavior was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/particledb/blob/e804ab8b0528b089b908b6249d2e1e4ac966802f/src/jepsen/particledb/workload/compare.clj#L71-L238&quot;&gt;not equivalent&lt;/a&gt; to PostgreSQL’s, the compare workload would &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/particledb/blob/d4721aeef9435af0131eacfb2163059272b13d57/src/jepsen/particledb/gen/tc.clj#L1195-L1234&quot;&gt;search&lt;/a&gt; for a simpler sequence of statements that could reproduce the same divergence. Although our SQL generator was relatively simple—just a few basic datatypes, literals, arithmetic, comparisons, boolean logic, transactions, and some simple &lt;code&gt;LIMIT&lt;/code&gt;, &lt;code&gt;OFFSET&lt;/code&gt;, and &lt;code&gt;ORDER BY&lt;/code&gt; clauses—it proved remarkably fruitful.&lt;/p&gt;
&lt;p&gt;Drawing on these successes, we devised a hybrid &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/sql/blob/81019139aa4bdc4b597cf9b63fa1f0c6b697a753/src/jepsen/sql/workload/internal_sim.clj&quot;&gt;&lt;code&gt;internal-sim&lt;/code&gt;&lt;/a&gt; workload to search for violations of internal consistency in more complex transactions. This workload &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/sql/blob/81019139aa4bdc4b597cf9b63fa1f0c6b697a753/src/jepsen/sql/gen.clj&quot;&gt;generated&lt;/a&gt; a random set of tables, then concurrently executed &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/sql/blob/81019139aa4bdc4b597cf9b63fa1f0c6b697a753/src/jepsen/sql/workload/internal_sim.clj#L370-L406&quot;&gt;random transactions&lt;/a&gt; over them. It then &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/sql/blob/81019139aa4bdc4b597cf9b63fa1f0c6b697a753/src/jepsen/sql/workload/internal_sim.clj#L115-L368&quot;&gt;checked each transaction&lt;/a&gt; independently to ensure that each of its statements observed results consistent with those preceding. For example, if a transaction executed &lt;code&gt;DELETE ... WHERE 2 &amp;lt; c&lt;/code&gt;, then observed a row with &lt;code&gt;c = 3&lt;/code&gt;, we could prove that transaction was not internally consistent.&lt;/p&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;We found 96 issues with ParticleDB, which we present in several categories below. All but three of these issues occurred in healthy, single-node instances of ParticleDB. Busy readers may wish to jump to the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#discussion&quot;&gt;Discussion&lt;/a&gt; section, which summarizes our findings.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#availability-and-performance&quot;&gt;Availability and Performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#broken-features&quot;&gt;Broken Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#predicates&quot;&gt;Predicates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#constraints&quot;&gt;Constraints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#joins&quot;&gt;Joins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#zombies&quot;&gt;Zombies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#state-corruption&quot;&gt;State Corruption&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#transactions-and-concurrency&quot;&gt;Transactions and Concurrency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#with-faults&quot;&gt;With Faults&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additional results are presented in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#appendix-a&quot;&gt;Appendix A&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#type-safety-and-syntax&quot;&gt;Type Safety and Syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#arithmetic&quot;&gt;Arithmetic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/particledb-1.1.4#strings&quot;&gt;Strings&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Availability and Performance&lt;/h2&gt;
&lt;p&gt;Under normal operation, ParticleDB was slow, deadlocked, and crashed without warning.&lt;/p&gt;
&lt;h3&gt;Abysmal Performance With &lt;code&gt;table-2pl&lt;/code&gt; (#62)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.4, &lt;code&gt;--txn-mode table-2pl&lt;/code&gt; was so slow as to be essentially unusable. While some transactions might complete in a few milliseconds, most took 10, 20, or even 30 seconds, then failed.&lt;/p&gt;
&lt;p&gt;For example, take &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260608T155928-table-2pl-perf.zip&quot;&gt;this test run&lt;/a&gt; with a single node running 1.1.18.4, transaction mode &lt;code&gt;table-2pl&lt;/code&gt;, and WAL mode &lt;code&gt;groupsync&lt;/code&gt;. With ten concurrent clients, most transactions blocked for tens of seconds before throwing &lt;code&gt;could not serialize access due to concurrent update&lt;/code&gt;, or an IO error.&lt;/p&gt;
&lt;p&gt;High latencies (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/62&quot;&gt;#62&lt;/a&gt;) appeared improved by 1.1.18.6, which could execute transactions with sub-100 millisecond latencies for at least a few seconds.&lt;/p&gt;
&lt;h3&gt;Mystery Crash (#81)&lt;/h3&gt;
&lt;p&gt;While 1.1.18.6 improved latencies, it also crashed frequently. Crashes occurred spontaneously with various transaction modes, WAL modes, and client interfaces; there was no obvious cause. Nodes would run for a few hundred to a few thousand seconds before exiting. No error messages were printed. These crashes (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/81&quot;&gt;#81&lt;/a&gt;) no longer appeared in 1.1.18.11.rc-18.&lt;/p&gt;
&lt;h3&gt;Deadlock After Two Transactions (#27)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.8, submitting as few as two concurrent transactions to a healthy, single-node deployment could cause it to deadlock. This occurred both using the Postgres and ParticleDB interfaces. The node would log an error message like &lt;code&gt;[DEADLOCK] 1 cycles detected at SystemTime ...&lt;/code&gt;, and an empty backtrace. In some cases new clients could attach and perform transactions; in others, new clients would also hang indefinitely. This may reflect multiple deadlock bugs.&lt;/p&gt;
&lt;p&gt;With the &lt;code&gt;append&lt;/code&gt; workload, version 1.1.18.9 generally could not run for more than 45 seconds without locking up. Version 1.1.18.11.rc-1 could reliably run for two minutes without lockups, and we declared the issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/27&quot;&gt;#27&lt;/a&gt;) resolved.&lt;/p&gt;
&lt;h3&gt;Deadlock With &lt;code&gt;table-2pl&lt;/code&gt; (#71)&lt;/h3&gt;
&lt;p&gt;While version 1.1.18.11.rc-1 fixed the most frequent deadlocks, we continued to find deadlocks through 1.1.18.11.rc-6 when running with &lt;code&gt;table-2pl&lt;/code&gt;. An otherwise healthy ParticleDB node would execute transactions for about 30 seconds, then get stuck forever. The node would log &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260610T085629-1.1.18.11.rc-1-table-2pl-deadlock.zip&quot;&gt;a similar deadlock error message&lt;/a&gt; to #27, and all requests with the ParticleDB client would throw &lt;code&gt;PDB JDBC: null&lt;/code&gt; after a 100 second default timeout. These deadlocks (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/71&quot;&gt;#71&lt;/a&gt;) appeared resolved by 1.1.18.11.rc-7.&lt;/p&gt;
&lt;h2&gt;Broken Features&lt;/h2&gt;
&lt;p&gt;Orkes’ &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260519195820/https://particledb.ai/connectivity/pg-wire/&quot;&gt;public documentation&lt;/a&gt; claimed broad support for Postgres features, and the internal operator guide concurred:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The engine speaks PostgreSQL v3 wire protocol natively — anything that speaks pgjdbc / libpq / psycopg2 / pq connects unchanged. &lt;code&gt;BEGIN&lt;/code&gt;, &lt;code&gt;COMMIT&lt;/code&gt;, &lt;code&gt;ROLLBACK&lt;/code&gt;, prepared statements, parameterised queries, and extended-query pipelining all work as expected.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These claims were somewhat optimistic. ParticleDB was missing support for a wide array of Postgres features, and others were badly broken.&lt;/p&gt;
&lt;h3&gt;No Aggregates Within &lt;code&gt;CASE&lt;/code&gt; (#20)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.4, aggregate expressions like &lt;code&gt;COUNT(*)&lt;/code&gt; were not supported within &lt;code&gt;CASE&lt;/code&gt; expressions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Dockerfile&quot;&gt;SELECT
  CASE
    WHEN COUNT(*) = 2
      THEN &amp;#39;CASE_COUNT_STAR_OK&amp;#39;
      ELSE &amp;#39;CASE_COUNT_STAR_BAD&amp;#39;
  END
FROM t;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This threw &lt;code&gt;Error: cannot evaluate wildcard expression&lt;/code&gt;. ParticleDB gained support for this feature (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/20&quot;&gt;#20&lt;/a&gt;) by version 1.1.18.6.&lt;/p&gt;
&lt;h3&gt;Unsupported Exponentiation (#28)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.4 through 1.1.18.8 did not support the Postgres exponentiation operator:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT 2 ^ 3;
ERROR:  unsupported binary operator: PGExp
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Version 1.1.18.9 added support for the &lt;code&gt;^&lt;/code&gt; operator (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/28&quot;&gt;#28&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Malformed Responses for Prepared Statements (#2)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.9, any attempt to execute a prepared statement using the Postgres JDBC driver caused an exception like &lt;code&gt;Execution error (NoSuchElementException) at java.util.ArrayDeque/removeFirst&lt;/code&gt;. Since the JDBC driver automatically switches to prepared statements after the first few calls to &lt;code&gt;execute()&lt;/code&gt;, this rendered the database essentially unusable. As a workaround, we disabled the client’s use of prepared statements.&lt;/p&gt;
&lt;p&gt;Prepared statements are executed using Postgres’ &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-EXT-QUERY&quot;&gt;extended query protocol&lt;/a&gt;, which takes place in several (potentially reusable) phases: Parse, Bind, Execute, and so on. Orkes believes that ParticleDB likely failed to return a response for one of those phases, or returned an extra message, causing the client’s queue to underflow. By version 1.1.18.11.rc-1, this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/2&quot;&gt;#2&lt;/a&gt;) appeared fixed.&lt;/p&gt;
&lt;h3&gt;Most Postgres Queries Fail (#83)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.11.rc-7 through 1.1.18.11.rc-19, many queries using the Postgres interface threw &lt;code&gt;Bad value for type long: some_column&lt;/code&gt;. This rendered the database largely unusable.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;CREATE TABLE ints (a int);
SELECT * FROM ints;
  -&amp;gt; Bad value for type long : a
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Orkes believes this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/83&quot;&gt;#83&lt;/a&gt;) was fixed in 1.1.19-rc.12.&lt;/p&gt;
&lt;h3&gt;Tuples Without Fields (#111)&lt;/h3&gt;
&lt;p&gt;In 1.1.18.11.rc-18 and rc-19, queries with the Postgres interface occasionally threw errors like &lt;code&gt;Received resultset tuples, but no field structure for them&lt;/code&gt;.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-5&quot; id=&quot;fnref-5&quot; data-footnote-ref&gt;5&lt;/a&gt;&lt;/sup&gt; For example, take &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/user-attachments/files/30419491/20260727T025222.565-0500.zip&quot;&gt;this test&lt;/a&gt; of version 1.1.18.11.rc-19, with no faults, a new &lt;code&gt;strict-serializable&lt;/code&gt; transaction mode, and WAL mode &lt;code&gt;sync&lt;/code&gt;. It executed a transaction like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;BEGIN;
SELECT * FROM t_38;
INSERT INTO u_38 (d, a, c, b) VALUES
  (null, null, false, &amp;quot;oudt1THxi&amp;quot;);
INSERT INTO t_38 (d, b) VALUES
  (false, &amp;quot;BCczDbueXLz9zXnpgbdCeFbC5y6XSEys3&amp;quot;);
COMMIT;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This complained of resultset tuples with no field structure, suggesting an error in ParticleDB’s Postgres protocol state machine. While Jepsen has not tested this version, Orkes reports this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/111&quot;&gt;#111&lt;/a&gt;) was fixed in version 1.1.19-rc.13.&lt;/p&gt;
&lt;h3&gt;Broken Postgres Authentication (#1)&lt;/h3&gt;
&lt;p&gt;The Postgres wire protocol usually authenticates clients using a username and password. ParticleDB had a &lt;code&gt;--pg-password&lt;/code&gt; option, but 1.1.4 through 1.1.18.8 lacked any option to set the username. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260519195820/https://particledb.ai/connectivity/pg-wire/&quot;&gt;Some of the documentation&lt;/a&gt; indicated the default username was &lt;code&gt;admin&lt;/code&gt;, while &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518193025/https://particledb.ai/getting-started/quickstart/&quot;&gt;other&lt;/a&gt; &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518203023/https://particledb.ai/sdks/java/&quot;&gt;pages&lt;/a&gt; said it was &lt;code&gt;particledb&lt;/code&gt;. Neither appeared to work; the only option was to use &lt;code&gt;--no-auth&lt;/code&gt; to disable authentication altogether. Version 1.1.18.11.rc-1 introduced a &lt;code&gt;--pg-username&lt;/code&gt; option (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/1&quot;&gt;#1&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Java Client Could Not Execute Transactions (#3)&lt;/h3&gt;
&lt;p&gt;Version 1.0.0 of the official ParticleDB Java client refused every attempt to execute a transaction. Any call to begin a JDBC transaction, or to execute a &lt;code&gt;BEGIN&lt;/code&gt; statement, would result in an error message: &lt;code&gt;PDB wire error 1064: SQL plan error: transaction/session statements do not produce a query plan&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;ParticleDB 1.1.4 could execute transactions via its Postgres interface, but using the official Java SDK was essentially impossible. This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/3&quot;&gt;#3&lt;/a&gt;) seems to have been resolved by ParticleDB 1.1.15.&lt;/p&gt;
&lt;h2&gt;Predicates&lt;/h2&gt;
&lt;p&gt;ParticleDB struggled to interpret predicates. &lt;code&gt;WHERE&lt;/code&gt; clauses failed to match rows they should have, or matched unexpected or nonexistent rows. These issues occurred with a single server, single client, and no faults.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;SELECT&lt;/code&gt; Ignores &lt;code&gt;WHERE&lt;/code&gt; Clause (#40)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.9, &lt;code&gt;SELECT&lt;/code&gt; queries without a table accepted a &lt;code&gt;WHERE&lt;/code&gt; clause, but ignored it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT 5 WHERE FALSE;
 5
---
 5
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should have returned zero rows. Version 1.1.18.11.rc-1 seems to have resolved this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/40&quot;&gt;#40&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;WHERE 1 &amp;lt; (a + 2)&lt;/code&gt; (#47)&lt;/h3&gt;
&lt;p&gt;Version 1.1.1.18.9 introduced a new problem with predicates: inside a transaction, a &lt;code&gt;WHERE&lt;/code&gt; clause which performed sufficiently complex floating-point operations would silently fail to match any rows. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a real);
root=&amp;gt; INSERT INTO t (a) VALUES (3);
root=&amp;gt; BEGIN;
root=*&amp;gt; SELECT * FROM t WHERE 1 &amp;lt; (a + 2);
 a
---
(0 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same problem applied to &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt;: predicates like &lt;code&gt;WHERE (a * 2) IS NOT NULL&lt;/code&gt; would silently fail to match. However, simpler predicates like &lt;code&gt;WHERE a IS NOT NULL&lt;/code&gt; would match correctly. The effect disappeared if the column was an integer. Moreover, some predicates which would throw errors outside a transaction would silently match zero rows within a transaction, suggesting these predicates were not being evaluated at all:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE 1 &amp;lt; (5.0 % 3);
ERROR:  internal error
root=&amp;gt; BEGIN;
root=*&amp;gt; SELECT * FROM t WHERE 1 &amp;lt; (5.0 % 3);
 a
---
(0 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/47&quot;&gt;#47&lt;/a&gt;) appeared resolved by 1.1.18.11.rc-1.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;WHERE 4 = 5&lt;/code&gt; (#18)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.4 through 1.1.17 aggressively interpreted &lt;code&gt;WHERE something = x&lt;/code&gt; as a primary-key lookup of &lt;code&gt;x&lt;/code&gt;, regardless of the value of &lt;code&gt;something&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t
       (a int primary key, b int);
root=&amp;gt; INSERT INTO t VALUES (4, 5), (5, 6);
root=&amp;gt; SELECT * FROM t WHERE b = 4;
 a | b
---+---
 4 | 5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ParticleDB would ignore a row with &lt;code&gt;b = 5&lt;/code&gt; in favor of &lt;code&gt;a = 5&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE b = 5;
 a | b
---+---
 5 | 6
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You could write predicates over non-existent columns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT * FROM t
       WHERE definitely_not_a_column = 4;
 a | b
---+----
 4 | 5
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or ask if &lt;code&gt;4 = 5&lt;/code&gt;:&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-6&quot; id=&quot;fnref-6&quot; data-footnote-ref&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE 4 = 5;
 a | b
---+----
 5 | 6
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By version 1.1.18.4, ParticleDB had learned that not every equality comparison involved a primary key (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/18&quot;&gt;#18&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;SELECT&lt;/code&gt; in a Transaction Returns Nonsense (#35)&lt;/h3&gt;
&lt;p&gt;The behavior of &lt;code&gt;SELECT&lt;/code&gt; within a transaction in ParticleDB versions 1.1.4 through 1.1.18.8 was thoroughly confusing. Consider a single client connected to a fresh single-node instance of ParticleDB 1.1.18.8. It creates a simple table of integers with two rows, &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;2&lt;/code&gt;, and begins a transaction:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a INT UNIQUE);
root=&amp;gt; INSERT INTO t VALUES (1), (2);
root=&amp;gt; BEGIN;
root=*&amp;gt; SELECT * FROM t;
 a
---
 1
 2
(2 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So far, so good. But now consider:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;YAML&quot;&gt;&lt;span class=&quot;syntax-source syntax-yaml&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=*&amp;gt; SELECT * FROM t WHERE a = 1;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;
 &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-yaml&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=*&amp;gt; SELECT * FROM t WHERE a = 3;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;
 &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-yaml&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=*&amp;gt; SELECT * FROM t WHERE a IN (1, 2);&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;
 &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-yaml&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=*&amp;gt; SELECT * FROM t WHERE FALSE;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;
 &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-yaml&quot;&gt;2&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One might conclude that every &lt;code&gt;SELECT&lt;/code&gt; with a predicate returned a single row from the requested table—perhaps the most recently inserted row. But this is not true, because many predicates evaluating to &lt;code&gt;TRUE&lt;/code&gt; returned nothing.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;YAML&quot;&gt;&lt;span class=&quot;syntax-source syntax-yaml&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=*&amp;gt; SELECT * FROM t WHERE TRUE;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(0 rows)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=*&amp;gt; SELECT * FROM t WHERE 3 = 3;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(0 rows)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This behavior (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/35&quot;&gt;#35&lt;/a&gt;) seemed improved by ParticleDB 1.1.18.9.&lt;/p&gt;
&lt;h2&gt;Constraints&lt;/h2&gt;
&lt;p&gt;ParticleDB often violated constraints like &lt;code&gt;NOT NULL&lt;/code&gt; or &lt;code&gt;UNIQUE&lt;/code&gt;, even with a single server, single client, and no faults.&lt;/p&gt;
&lt;h3&gt;Not-Null Constraints Allow Null (#8)&lt;/h3&gt;
&lt;p&gt;While ParticleDB allowed users to add &lt;code&gt;NOT NULL&lt;/code&gt; constraints to columns, versions 1.1.4 through 1.1.17 ignored them, resulting in bizarre states.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a BOOLEAN NOT NULL);
root=&amp;gt; INSERT INTO t (a) VALUES (NULL);
INSERT 0 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While the resulting row existed in some sense, it was partly invisible.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;YAML&quot;&gt;&lt;span class=&quot;syntax-source syntax-yaml&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT * FROM t;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;a&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(0 rows)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT COUNT(*) FROM t;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;COUNT&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;----&lt;/span&gt;
     &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-yaml&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT COUNT(*) FROM t WHERE a IS NULL;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;COUNT&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;----&lt;/span&gt;
     &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-yaml&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In version 1.1.18.4, ParticleDB started to enforce null constraints (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/8&quot;&gt;#8&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Unique Constraints Allow Duplicates (#9)&lt;/h3&gt;
&lt;p&gt;Similarly, while ParticleDB allowed users to add unique constraints to columns, versions 1.1.4 through 1.1.18.4 simply ignored them.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a boolean UNIQUE);
root=&amp;gt; INSERT INTO t (a) VALUES (TRUE);
INSERT 0 1
root=&amp;gt; INSERT INTO t (a) VALUES (TRUE);
INSERT 0 1
root=&amp;gt; SELECT * FROM t;
 a
---
 t
 t
(2 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In version 1.1.18.6, ParticleDB partially enforced unique constraints (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/9&quot;&gt;#9&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Duplicate Primary Keys (#29)&lt;/h3&gt;
&lt;p&gt;Although 1.1.18.6 made &lt;code&gt;UNIQUE&lt;/code&gt; constraints somewhat more robust, 1.1.4 through 1.1.18.8 allowed an &lt;code&gt;UPDATE&lt;/code&gt; to produce multiple rows with the same primary key.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (id text PRIMARY KEY,
                       desc text);
root=&amp;gt; INSERT INTO t VALUES (&amp;#39;a&amp;#39;, &amp;#39;a&amp;#39;);
root=&amp;gt; INSERT INTO t VALUES (&amp;#39;b&amp;#39;, &amp;#39;b&amp;#39;);
root=&amp;gt; UPDATE t SET id = &amp;#39;c&amp;#39;;
UPDATE 2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Selecting a row by primary key returned only one of the two rows, but other predicates could return both:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;YAML&quot;&gt;&lt;span class=&quot;syntax-source syntax-yaml&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE id = &amp;#39;c&amp;#39;;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;id | desc&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;-+------&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;c  | b&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE id LIKE &amp;#39;c&amp;#39;;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;id | desc&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;-+------&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;c  | a&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;c  | b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/29&quot;&gt;#29&lt;/a&gt;) appears to have been fixed by 1.1.18.9.&lt;/p&gt;
&lt;h3&gt;Null Primary Keys (#57)&lt;/h3&gt;
&lt;p&gt;Per the SQL standard, primary keys may not be null. This makes intuitive sense, since &lt;code&gt;NULL&lt;/code&gt; represents an unknown value. ParticleDB would not allow you to insert null values for primary keys. However, versions 1.1.4 through 1.1.18.11.rc-1 were perfectly happy to &lt;em&gt;update&lt;/em&gt; the primary key of one (or more!) rows to &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a int PRIMARY KEY);
root=&amp;gt; INSERT INTO t (a) VALUES (3), (4);
root=&amp;gt; UPDATE t SET a = NULL;
root=&amp;gt; SELECT * FROM t;
 a
---


(2 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ParticleDB 1.1.18.11.rc-3 rejected attempts to set primary keys to &lt;code&gt;NULL&lt;/code&gt; (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/57&quot;&gt;#57&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Primary Key Conflicts with Deleted Rows (#82)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.4 through 1.1.18.11.rc-11, a transaction which deleted a row, then inserted a new row with the same primary key, would abort due to a primary key constraint violation. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;
CREATE TABLE t (a int PRIMARY KEY);
INSERT INTO t (a) VALUES (7);
BEGIN;
DELETE FROM t;
INSERT INTO t (a) VALUES (7);
COMMIT;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second &lt;code&gt;INSERT&lt;/code&gt; would throw &lt;code&gt;PDB wire error 1064: SQL execution error: duplicate key value violates unique constraint (key=7)&lt;/code&gt;, but only when using the ParticleDB interface—the Postgres interface was unaffected. Orkes partially addressed this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/82&quot;&gt;#82&lt;/a&gt;) in version 1.1.18.11.rc-9, and fixed another case of it in 1.1.18.11.rc-18.&lt;/p&gt;
&lt;h3&gt;Lack of Primary Key Conflicts with Extant Rows (#90)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.11.rc-9, a transaction executing concurrently with other transactions could read a row with a primary key, then successfully insert a row with that same primary key. For example, take &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260618T140338-1.1.18.11.rc-9-internal.zip&quot;&gt;this &lt;code&gt;internal-sim&lt;/code&gt; test&lt;/a&gt;, which created a table with two integer columns:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;CREATE TABLE u_14 (b int, a int PRIMARY KEY);
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A transaction ran &lt;code&gt;SELECT * FROM u_14&lt;/code&gt; and observed several rows, including &lt;code&gt;{:b 1020026655, :a -1}&lt;/code&gt;. It then ran &lt;code&gt;INSERT INTO u_14 (b, a) VALUES (-27, -1)&lt;/code&gt;, which should have conflicted with the extant primary key, but succeeded instead. It went on to &lt;code&gt;SELECT * FROM u_14&lt;/code&gt; again, and observed the newly inserted row, but not the previously existent one. This is a violation of internal consistency.&lt;/p&gt;
&lt;p&gt;As of version 1.1.18.11.rc-19, we no longer observed these internal consistency violations (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/90&quot;&gt;#90&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Joins&lt;/h2&gt;
&lt;p&gt;ParticleDB struggled to join tables together. The following issues occurred with a single server, single client, and no faults.&lt;/p&gt;
&lt;h3&gt;Can’t Join Integers to Floats (#58)&lt;/h3&gt;
&lt;p&gt;Even after agreeing that &lt;code&gt;2.0 = 2&lt;/code&gt;, ParticleDB 1.1.4 through 1.1.18.9 refused to join two tables on an &lt;code&gt;int&lt;/code&gt; and a &lt;code&gt;real&lt;/code&gt; column:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a real);
root=&amp;gt; CREATE TABLE u (a int);
root=&amp;gt; INSERT INTO t (a) VALUES (2.0);
root=&amp;gt; INSERT INTO u (a) VALUES (2);
root=&amp;gt; SELECT t.a FROM t JOIN u ON u.a = t.a;
ERROR:  join key must be integer type
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Databases like Postgres would return a single row here, since &lt;code&gt;2.0 = 2&lt;/code&gt;. ParticleDB gained the ability to join floats to integers (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/58&quot;&gt;#58&lt;/a&gt;) by 1.1.18.11.rc-1.&lt;/p&gt;
&lt;h3&gt;Incorrect Column Names (#54)&lt;/h3&gt;
&lt;p&gt;Because two tables can have a column with the same name, SQL databases generally return both table and column names, so you can tell which column belonged to which table. ParticleDB, with both the ParticleDB and Postgres interfaces, neglected to return table names. Instead, versions 1.1.4 through 1.1.18.11.rc-1 renamed column &lt;code&gt;a&lt;/code&gt; in joined tables to &lt;code&gt;right.a&lt;/code&gt;, &lt;code&gt;right.right.a&lt;/code&gt;, and so on. Consider the following simple join across two tables &lt;code&gt;t&lt;/code&gt; and &lt;code&gt;u&lt;/code&gt;, both with an integer column &lt;code&gt;a&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;SQL&quot;&gt;&lt;span class=&quot;syntax-source syntax-sql&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-star syntax-sql&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;FROM&lt;/span&gt; t FULL OUTER &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;JOIN&lt;/span&gt; u
  &lt;span class=&quot;syntax-keyword syntax-other syntax-DDL syntax-create syntax-II syntax-sql&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-other syntax-database-name syntax-sql&quot;&gt;t&lt;/span&gt;.&lt;span class=&quot;syntax-constant syntax-other syntax-table-name syntax-sql&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-comparison syntax-sql&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-other syntax-database-name syntax-sql&quot;&gt;u&lt;/span&gt;.&lt;span class=&quot;syntax-constant syntax-other syntax-table-name syntax-sql&quot;&gt;a&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Postgres this produced rows like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;[{:t/a 1, :u/a 1.0}
 {:t/a 2, :u/a 2.0}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here the expression &lt;code&gt;:t/a&lt;/code&gt; denotes column &lt;code&gt;a&lt;/code&gt; of table &lt;code&gt;t&lt;/code&gt;. In ParticleDB, this same query returned:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;[{:a 1, :right.a 1.0}
 {:a 2, :right.a 2.0}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The table names are completely missing, and ParticleDB has altered the name of column &lt;code&gt;a&lt;/code&gt; to &lt;code&gt;right.a&lt;/code&gt;. Programs which expected typical SQL behavior would throw an error as they attempted to read columns which were not present, or could silently read null values.&lt;/p&gt;
&lt;p&gt;ParticleDB 1.1.18.11.rc-3 no longer munged columns, but the database still omitted table names. The resulting name collisions destroyed information.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;[{:a 1}
 {:a 2}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While Jepsen has not had a chance to test this version, Orkes claims this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/54&quot;&gt;#54&lt;/a&gt;) was resolved by version 1.1.19-rc.13.&lt;/p&gt;
&lt;h3&gt;Coercing to Strings (#76)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.11.rc-19, joins would coerce some values to strings in ParticleDB’s Postgres interface. For example, consider the following join:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;SQL&quot;&gt;&lt;span class=&quot;syntax-source syntax-sql&quot;&gt;&lt;span class=&quot;syntax-meta syntax-create syntax-sql&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-other syntax-create syntax-sql&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-other syntax-sql&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-sql&quot;&gt;t&lt;/span&gt;&lt;/span&gt; (a &lt;span class=&quot;syntax-storage syntax-type syntax-sql&quot;&gt;int&lt;/span&gt;);
&lt;span class=&quot;syntax-meta syntax-create syntax-sql&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-other syntax-create syntax-sql&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-other syntax-sql&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-sql&quot;&gt;u&lt;/span&gt;&lt;/span&gt; (a &lt;span class=&quot;syntax-storage syntax-type syntax-sql&quot;&gt;real&lt;/span&gt;);
&lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;INSERT INTO&lt;/span&gt; u (a) &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-II syntax-sql&quot;&gt;VALUES&lt;/span&gt; (&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;3&lt;/span&gt;.&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;2&lt;/span&gt;);
&lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;INSERT INTO&lt;/span&gt; t (a) &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-II syntax-sql&quot;&gt;VALUES&lt;/span&gt; (&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;5&lt;/span&gt;);
&lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-other syntax-database-name syntax-sql&quot;&gt;u&lt;/span&gt;.&lt;span class=&quot;syntax-constant syntax-other syntax-table-name syntax-sql&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;FROM&lt;/span&gt; t &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;CROSS JOIN&lt;/span&gt; u;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the ParticleDB interface, this produced the number &lt;code&gt;3.2&lt;/code&gt;. With the Postgres interface, it produced the string &lt;code&gt;&amp;quot;3.2&amp;quot;&lt;/code&gt;. This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/76&quot;&gt;#76&lt;/a&gt;) seemed fixed by 1.1.18.11.rc-21.&lt;/p&gt;
&lt;h3&gt;Missing Rows (#59)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.11.rc-1, outer joins of a non-empty table to an empty one often returned no rows, even though the query should have produced all rows in the non-empty table. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Dockerfile&quot;&gt;root=&amp;gt; CREATE TABLE t (a int);
root=&amp;gt; CREATE TABLE u (a int);
root=&amp;gt; INSERT INTO t (a) VALUES (1), (2);
root=&amp;gt; SELECT t.a AS t, u.a AS u
       FROM t LEFT JOIN u ON u.a = t.a;
 t | u
---+---
(0 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By version 1.1.18.11.rc-3, ParticleDB correctly returned rows when joining to empty tables (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/59&quot;&gt;#59&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Column Confusion (#77)&lt;/h3&gt;
&lt;p&gt;Many tables in real databases contain columns with identical names, like &lt;code&gt;id&lt;/code&gt; or &lt;code&gt;created_at&lt;/code&gt;. When asked to join two tables like this, ParticleDB 1.1.4 through 1.1.18.11.rc-6 became severely confused, even when all columns were fully qualified. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a text);
root=&amp;gt; CREATE TABLE u (a text);
root=&amp;gt; INSERT INTO t VALUES (&amp;#39;this&amp;#39;), (&amp;#39;that&amp;#39;);
root=&amp;gt; INSERT INTO u VALUES
       (&amp;#39;is not the right column&amp;#39;);
root=&amp;gt; SELECT t.a AS ta, u.a AS ua FROM t
       CROSS JOIN u WHERE (u.a = &amp;#39;this&amp;#39;);
  ta  |           ua
------+-------------------------
 this | is not the right column
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should not have matched anything—there are no rows in table &lt;code&gt;u&lt;/code&gt; where &lt;code&gt;u.a = &#39;this&#39;&lt;/code&gt;. Instead ParticleDB matched a row in table &lt;code&gt;t&lt;/code&gt; which satisfied &lt;code&gt;t.a = &#39;this&#39;&lt;/code&gt;, then cross-joined it to all rows in &lt;code&gt;u&lt;/code&gt;. To add insult to injury, 1.1.4 through 1.1.18.9 appeared to swap around the output columns, producing the following table:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;           ta            |  ua
-------------------------+------
 is not the right column | this
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If column &lt;code&gt;t.a&lt;/code&gt; was of a different type than &lt;code&gt;u.a&lt;/code&gt;, ParticleDB would try to coerce the types into compatible forms, producing bizarre error messages or delightfully weird results. Version 1.1.18.11.rc-7 was much less confused when joining tables containing the same column name (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/77&quot;&gt;#77&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Zombies&lt;/h2&gt;
&lt;p&gt;ParticleDB often wound up with rows that could not be deleted or updated, or which persisted in some (perhaps mangled) form after their deletion. These issues occurred with a single server, single client, and no faults.&lt;/p&gt;
&lt;h3&gt;Total Recall (#26)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.17 through 1.1.18.8, it appeared impossible to delete rows within a transaction. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a boolean);
root=&amp;gt; INSERT INTO t (a) VALUES (TRUE);
root=&amp;gt; BEGIN;
root=*&amp;gt; DELETE FROM t;
DELETE 0
root=*&amp;gt; COMMIT;
root=&amp;gt; SELECT * FROM t;
 a
---
 t
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We were unable to find a predicate that allowed one to delete records (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/26&quot;&gt;#26&lt;/a&gt;). Version 1.1.18.9 allowed transactions to mostly&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-7&quot; id=&quot;fnref-7&quot; data-footnote-ref&gt;7&lt;/a&gt;&lt;/sup&gt; delete rows.&lt;/p&gt;
&lt;h3&gt;Memento Delere (#45)&lt;/h3&gt;
&lt;p&gt;1.1.18.9 resolved issue #26, but deleted rows still lingered on in some capacity. While a transaction could not directly select deleted rows, it could observe them via aggregate functions and various predicates.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;YAML&quot;&gt;&lt;span class=&quot;syntax-source syntax-yaml&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; CREATE TABLE t (a boolean);&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; INSERT INTO t (a) VALUES (TRUE);&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; BEGIN;&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=*&amp;gt; DELETE FROM t;&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;DELETE 1&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=*&amp;gt; SELECT COUNT(*) FROM t WHERE a = TRUE;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;COUNT&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;----&lt;/span&gt;
     &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-yaml&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=*&amp;gt; SELECT COUNT(*) FROM t WHERE a = FALSE;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;COUNT&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;----&lt;/span&gt;
     &lt;span class=&quot;syntax-constant syntax-numeric syntax-integer syntax-yaml&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The row must still be present in some sense, because these aggregate queries could observe its value via predicates. However, trying to read the row directly returned nothing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=*&amp;gt; SELECT a FROM t WHERE a = TRUE;
 a
---
(0 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Version 1.1.18.11.rc-1 no longer allowed shades of deleted records to persist in aggregate queries (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/45&quot;&gt;#45&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Night of the Living Integers (#23)&lt;/h3&gt;
&lt;p&gt;In versions 1.1.4 through 1.1.17, updating an integer column could make rows impossible to delete. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (i int);
root=&amp;gt; INSERT INTO t VALUES (0);
root=&amp;gt; UPDATE t SET i = 1.5;
root=&amp;gt; DELETE FROM t;
DELETE 0
root=&amp;gt; SELECT * FROM t;
 i
---
 1
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While this row appears to contain the integer &lt;code&gt;1&lt;/code&gt;, that integer is &lt;em&gt;special&lt;/em&gt;, since the &lt;code&gt;UPDATE&lt;/code&gt; statement provided the value &lt;code&gt;1.5&lt;/code&gt;. This row cannot be deleted. It does appear in &lt;code&gt;SELECT&lt;/code&gt;, and is apparently equal to &lt;code&gt;1&lt;/code&gt;, rather than &lt;code&gt;1.5&lt;/code&gt;. Oddly, updating a row by primary key with the same &lt;code&gt;1.5&lt;/code&gt; value did not create zombies; the problem (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/23&quot;&gt;#23&lt;/a&gt;) was somehow linked to predicates. ParticleDB 1.1.18.4 no longer exhibited this behavior.&lt;/p&gt;
&lt;h3&gt;Team Rocket (#84)&lt;/h3&gt;
&lt;p&gt;Tables with a &lt;code&gt;double precision&lt;/code&gt; column suffered from a similar problem: they could be updated with integer values, but doing so would produce zombie rows that could neither be updated nor deleted again.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;ROOT=&amp;gt; CREATE TABLE t (a double precision);
ROOT=&amp;gt; INSERT INTO t (a) VALUES (1.0);
ROOT=&amp;gt; UPDATE t SET a = 2;
ROOT=&amp;gt; DELETE FROM t;
DELETE 0
root=&amp;gt; SELECT * FROM t;
 a
---
 2
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the double precision column was a primary key, these updates would produce duplicate copies of the updated row. This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/84&quot;&gt;#84&lt;/a&gt;) affected ParticleDB 1.1.18.11.rc-1 through 1.1.18.11.rc-8, and was fixed in 1.1.18.11.rc-9.&lt;/p&gt;
&lt;h3&gt;A Ghost is Technically Not a Number (#78)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.11.rc-1 through 1.1.18.11.rc-6, &lt;code&gt;NaN&lt;/code&gt; values stored in &lt;code&gt;real&lt;/code&gt; or &lt;code&gt;double precision&lt;/code&gt; columns could neither be updated nor deleted.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;DROP TABLE IF EXISTS t;
CREATE TABLE t (a real);
INSERT INTO t (a) VALUES (&amp;#39;NaN&amp;#39;);
UPDATE t SET a = 5.0;
  -&amp;gt; #:next.jdbc{:update-count 0}
DELETE FROM t;
  -&amp;gt; #:next.jdbc{:update-count 0}
SELECT * FROM t;
  -&amp;gt; [{:a ##NaN}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/78&quot;&gt;#78&lt;/a&gt;) did not occur with the &lt;code&gt;psql&lt;/code&gt; shell, but did happen with both ParticleDB and Postgres JDBC clients. Version 1.1.18.11.rc-7 seems to have fixed the problem.&lt;/p&gt;
&lt;h3&gt;Beached Strings (#74)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.11.rc-1 through 1.1.18.11.rc-6, fixed-width strings were unmodifiable unless created with exactly the right number of characters. While ParticleDB would correctly pad the string &lt;code&gt;&#39;hi&#39;&lt;/code&gt; to &lt;code&gt;&#39;hi &#39;&lt;/code&gt;, the resulting row could neither be updated nor deleted.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a character(3));
root=&amp;gt; INSERT INTO t (a) VALUES (&amp;#39;hi&amp;#39;);
root=&amp;gt; DELETE FROM t;
DELETE 0
root=&amp;gt; UPDATE t SET a = &amp;#39;sup&amp;#39;;
UPDATE 0
root=&amp;gt; SELECT * FROM t;
  a
-----
 hi
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a fixed-width column was used as a primary key, it could be updated, but only once. Both old and new copies would appear as distinct rows.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;CREATE TABLE t
       (a character(3) PRIMARY KEY);
INSERT INTO t (a) VALUES (&amp;#39;um&amp;#39;);
UPDATE t SET a = &amp;#39;no&amp;#39;;
SELECT a FROM t;
-&amp;gt; [{:a &amp;quot;um &amp;quot;} {:a &amp;quot;no &amp;quot;}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Any further attempt to modify those rows would produce a duplicate key error, despite all the values involved being distinct. Fixed-width strings were also incomparable using the &lt;code&gt;=&lt;/code&gt; operator. Version 1.1.18.11.rc-7 resolved these issues (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/74&quot;&gt;#74&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Pet Sematary (#46)&lt;/h3&gt;
&lt;p&gt;In SQL, the &lt;code&gt;DROP TABLE&lt;/code&gt; statement destroys a table completely. In ParticleDB 1.1.18.9 through 1.1.18.11.rc-1, it left a shadow of the table behind, whose rows would reappear in later tables with the same name and more-or-less compatible schemata. To demonstrate this behavior, let us create a table with a single row:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a text, b real);
root=&amp;gt; BEGIN;
root=*&amp;gt; INSERT INTO t VALUES (&amp;#39;boo&amp;#39;, 18.83);
root=*&amp;gt; COMMIT;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We then drop the table, and demonstrate that it no longer exists:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; DROP TABLE t;
root=&amp;gt; SELECT * FROM t;
ERROR:  table not found
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we then create a new table with the same name and a somewhat compatible schema, rows from the deleted table reappear in the new one. However, the resurrected rows are not exactly their former selves. The floating-point value &lt;code&gt;18.83&lt;/code&gt; has become the integer &lt;code&gt;19&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a text, b int);
root=&amp;gt; BEGIN;
root=*&amp;gt; SELECT * FROM t;
  a  | b
-----+----
 boo | 19
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This behavior (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/46&quot;&gt;#46&lt;/a&gt;) seemed linked to transactions, and no longer appeared in 1.1.18.11.rc-3.&lt;/p&gt;
&lt;h2&gt;State Corruption&lt;/h2&gt;
&lt;p&gt;ParticleDB had many ways to create values which could not be read, alter values unexpectedly, create rows with incorrect or missing columns, or otherwise corrupt logical state. The issues in this section involved only a single server, single client, and no faults.&lt;/p&gt;
&lt;h3&gt;Unreadable Infinities (#15)&lt;/h3&gt;
&lt;p&gt;In versions 1.1.4 through 1.1.17, ParticleDB’s Postgres interface did not correctly serialize floating-point values like &lt;code&gt;Infinity&lt;/code&gt;. One could write infinite values, but trying to read them would cause the client to throw an error.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;CREATE TABLE t (a real);
INSERT INTO t (a) VALUES (&amp;#39;Infinity&amp;#39;);
SELECT * FROM t
   =&amp;gt; PSQLException: &amp;quot;Bad value for type
      double : inf&amp;quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With Postgres, this correctly returned the JVM’s &lt;code&gt;Double/INFINITY&lt;/code&gt; value. &lt;code&gt;NaN&lt;/code&gt; was serialized correctly. At the &lt;code&gt;psql&lt;/code&gt; shell, stored values came back as &lt;code&gt;inf&lt;/code&gt;, rather than &lt;code&gt;Infinity&lt;/code&gt;. In version 1.1.18.6, ParticleDB serialized infinite values correctly (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/15&quot;&gt;#15&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;NaN&lt;/code&gt; Sometimes Converted to 0.0 (#21)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.4 through 1.1.18.4 would sometimes (but not always!) convert the floating-point value &lt;code&gt;NaN&lt;/code&gt; to &lt;code&gt;0.0&lt;/code&gt;. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a real);
root=&amp;gt; INSERT INTO t (a) VALUES (2.5);
root=&amp;gt; INSERT INTO t (a) VALUES (&amp;#39;NaN&amp;#39;);
root=&amp;gt; SELECT * FROM t;
  a
-----
 2.5
   0
(2 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Delightfully, whether this &lt;code&gt;NaN&lt;/code&gt; corruption occurred depended on the order in which rows were inserted. If the first value inserted into the table was &lt;code&gt;NaN&lt;/code&gt;, that and later &lt;code&gt;NaN&lt;/code&gt;s would be preserved. Version 1.1.18.6 seemed to store &lt;code&gt;NaN&lt;/code&gt; correctly (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/21&quot;&gt;#21&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Assignment of Incompatible Types (#7)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.4, you could update a column of one type to a column of a different, incompatible type—producing values like &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;0&lt;/code&gt;. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a INT, b BOOLEAN);
root=&amp;gt; INSERT INTO t VALUES (2, FALSE);
root=&amp;gt; SELECT * FROM t WHERE a = b;
ERROR:  arrow compute: Invalid argument error:
Invalid comparison operation: Int64 == Boolean
root=&amp;gt; UPDATE t SET a = b;
root=&amp;gt; SELECT * FROM t;
 a | b
---+---
 0 | f
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Postgres, this UPDATE would throw &lt;code&gt;ERROR: column &amp;quot;a&amp;quot; is of type text but expression is of type int&lt;/code&gt;. ParticleDB refused to &lt;em&gt;compare&lt;/em&gt; this string to an integer (at least in this case), but was perfectly happy to make one equal to the other, resulting in &lt;code&gt;NULL&lt;/code&gt;. Version 1.1.18.6 seemed to resolve this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/7&quot;&gt;#7&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Insert Column Order Table Corruption (#16)&lt;/h3&gt;
&lt;p&gt;In SQL, one usually inserts a row into a table using a statement like &lt;code&gt;INSERT INTO people (name, age) VALUES (&#39;xanthippe&#39;, 15)&lt;/code&gt;. ParticleDB 1.1.4 through 1.1.17 ignored the &lt;code&gt;INSERT&lt;/code&gt; statement’s columns, using whatever column order had been used to create the table originally. This produced rows with titillating properties. Consider the following pair of inserts: one with columns in the same order as the table’s schema, and a second in a different order:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t
       (a TEXT NOT NULL PRIMARY KEY,
        b boolean);
root=&amp;gt; INSERT INTO t (a, b) VALUES (&amp;#39;x&amp;#39;, TRUE);
root=&amp;gt; INSERT INTO t (b, a) VALUES (FALSE, &amp;#39;y&amp;#39;);
root=&amp;gt; SELECT COUNT(*) FROM t;
 COUNT
-------
     2
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While ParticleDB thinks this table contains two rows, neither can be read via &lt;code&gt;SELECT *&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT * FROM t;
 a | b
---+---
(0 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In general, any query which happened to encounter a row inserted with a different column order would silently produce zero results. However, each row could be read by primary key! Note the different column orders in the output:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;YAML&quot;&gt;&lt;span class=&quot;syntax-source syntax-yaml&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE a = &amp;#39;x&amp;#39;;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;a | b&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;+---&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;x | t&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE a = &amp;#39;y&amp;#39;;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;b | a&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;+---&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;f | y&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the preceding example, the two columns were of (somewhat) incompatible types. What happens if the types are compatible?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a INT, b REAL);
root=&amp;gt; INSERT INTO t (a, b)
       VALUES (1, 1.23E32);
root=&amp;gt; INSERT INTO t (b, a)
       VALUES (2.34E32, 2);
root=&amp;gt; SELECT * FROM t;
          a          |    b
---------------------+---------
                   1 | 1.23e32
 9223372036854775807 |       2
(2 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the table is readable, but the data is corrupt. The second row’s &lt;code&gt;a&lt;/code&gt; value appears under &lt;code&gt;b&lt;/code&gt;, and vice-versa. The floating-point value &lt;code&gt;2.34E32&lt;/code&gt; has become the integer 263 − 1.&lt;/p&gt;
&lt;p&gt;If we attempt to delete every row from the table, something very strange happens. The &lt;code&gt;DELETE&lt;/code&gt; leaves the differently-ordered row in place, and its originally inserted values now appear intact!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; DELETE FROM t;
DELETE 1
root=&amp;gt; SELECT * FROM t;
 a |    b
---+---------
 2 | 2.34e32
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A second &lt;code&gt;DELETE&lt;/code&gt; statement—now that the table contains only rows inserted with column order &lt;code&gt;(b, a)&lt;/code&gt;—could successfully delete the remaining row. These column-order issues (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/16&quot;&gt;#16&lt;/a&gt;) no longer appeared in version 1.1.18.4.&lt;/p&gt;
&lt;h3&gt;Updates Can Insert Corrupt Rows (#24, #25)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.8, an update which matched no rows could instead create a new, possibly corrupt row in the table, with baffling effects. Consider a table mapping integer keys to integer values:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t
       (id int not null primary key,
        val integer);
root=&amp;gt; BEGIN;
root=*&amp;gt; UPDATE t SET val = 10 WHERE id = 0;
UPDATE 1
root=*&amp;gt; COMMIT;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This table was just created. It contains no rows, so the update should have affected zero, not one row. And yet it now contains a row!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT COUNT (*) FROM t;
 COUNT
-------
     1
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, that row is invisible to typical queries:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;YAML&quot;&gt;&lt;span class=&quot;syntax-source syntax-yaml&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT * FROM t;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;id | val&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;-+-----&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(0 rows)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE id = 0;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;id | val&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;-+-----&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(0 rows)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE val = 10;&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;id | val&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;-+-----&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(0 rows)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although one cannot directly see the row by primary key, it can conflict with some inserts.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; INSERT INTO t (id, val) VALUES (0, 100);
INSERT 0 1
root=&amp;gt; INSERT INTO t (id, val) VALUES (10, 110);
ERROR:  duplicate key value violates unique
        constraint (key=10)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;“Hang on,” an astute reader might exclaim. “The row we supposedly updated had &lt;code&gt;id = 0&lt;/code&gt; and set &lt;code&gt;val = 10&lt;/code&gt;. Why did it not conflict with an insert of &lt;code&gt;id = 0&lt;/code&gt;?”&lt;/p&gt;
&lt;p&gt;Quite right, dear reader! And why did it instead conflict with &lt;code&gt;id = 10&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;These are excellent questions—which we do not know the answer to. Pressing on:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t
       (id int not null primary key,
        val integer);
root=&amp;gt; BEGIN;
root=*&amp;gt; UPDATE t SET val = 50 WHERE id = 5;
UPDATE 1
root=*&amp;gt; SELECT * FROM t WHERE id = 5;
 val
-----
  50
(1 row)

root=*&amp;gt; COMMIT;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Within the transaction, the row produced by this update is readable by its primary key &lt;code&gt;id = 5&lt;/code&gt;. Although this table has two columns, the row is printed as if it had only one: &lt;code&gt;val = 50&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As in #16, these corrupt rows caused queries that encountered them to silently return zero rows, even if there were other valid rows that would have matched.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;YAML&quot;&gt;&lt;span class=&quot;syntax-source syntax-yaml&quot;&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; INSERT INTO t VALUES (6, 60);&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE id IN (6);&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;id | val&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;-+-----&lt;/span&gt;
  &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;6 |  60&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(1 row)&lt;/span&gt;

&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;root=&amp;gt; SELECT * FROM t WHERE id IN (6, 7);&lt;/span&gt;
 &lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;id | val&lt;/span&gt;
&lt;span class=&quot;syntax-entity syntax-other syntax-document syntax-begin syntax-yaml&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;-+-----&lt;/span&gt;
&lt;span class=&quot;syntax-string syntax-unquoted syntax-plain syntax-out syntax-yaml&quot;&gt;(0 rows)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although our corrupt row had &lt;code&gt;id = 5&lt;/code&gt; (or perhaps &lt;code&gt;50&lt;/code&gt;), adding a second value to the &lt;code&gt;IN&lt;/code&gt; predicate seems to have caused ParticleDB to “notice” the corrupt row. It then failed to return the valid row with &lt;code&gt;id = 6&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Depending on the table structure and &lt;code&gt;UPDATE&lt;/code&gt; statement, rows created by updates might be structurally valid, but logically corrupt:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a INT UNIQUE);
root=&amp;gt; INSERT INTO t VALUES (1);
root=&amp;gt; BEGIN;
root=*&amp;gt; UPDATE t SET a = 3 WHERE a = 2;
UPDATE 1
root=*&amp;gt; COMMIT;
root=&amp;gt; SELECT * FROM t;
 a
---
 1
 3
(2 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This bug (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/24&quot;&gt;#24&lt;/a&gt;) also allowed updates which would have left a row’s value unchanged to instead throw a duplicate key exception, as they (presumably) conflicted with themselves (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/25&quot;&gt;#25&lt;/a&gt;). Both issues appeared resolved as of version 1.1.18.9.&lt;/p&gt;
&lt;h3&gt;Sometimes, Strings That Are Extensive… Are Cursed (#37)&lt;/h3&gt;
&lt;p&gt;In versions 1.1.15 through 1.1.17, &lt;code&gt;text&lt;/code&gt; strings could be unexpectedly transmogrified into eight-bit integers. We encountered this in &lt;code&gt;append&lt;/code&gt; tests using the official ParticleDB client, like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260521T190337-strings-as-ints.zip&quot;&gt;this one&lt;/a&gt;. The &lt;code&gt;append&lt;/code&gt; workload stores a comma-separated list of integers in an SQL &lt;code&gt;TEXT&lt;/code&gt; column called &lt;code&gt;val&lt;/code&gt;, and appends values to it using &lt;code&gt;CONCAT&lt;/code&gt;. Most reads of &lt;code&gt;val&lt;/code&gt; sensibly returned strings:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;[&amp;quot;select (val) from txn2 where id = ? &amp;quot; 2946]
  -&amp;gt; [{:val &amp;quot;1&amp;quot;}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, after approximately 30 seconds, the ParticleDB client began returning eight-bit integers for key &lt;code&gt;2946&lt;/code&gt;. Here, the key’s value has become the JVM &lt;code&gt;Byte&lt;/code&gt; &lt;code&gt;1&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;[&amp;quot;select (val) from txn2 where sk = ? &amp;quot; 2946]
  -&amp;gt; [{:val 1}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From this point on, some keys appeared as strings, while others returned integers. A read of key &lt;code&gt;2961&lt;/code&gt; returned the string &lt;code&gt;&amp;quot;2&amp;quot;&lt;/code&gt;, while key &lt;code&gt;2865&lt;/code&gt; returned the integer &lt;code&gt;1&lt;/code&gt;—then moments later, the string &lt;code&gt;&amp;quot;1,2&amp;quot;&lt;/code&gt;. Sometimes the integer values corresponded to the string value produced by earlier writes, but not always. For example, a transaction read key &lt;code&gt;2980&lt;/code&gt; as the integer &lt;code&gt;0&lt;/code&gt;—when the only elements appended to its string were &lt;code&gt;&amp;quot;1&amp;quot;&lt;/code&gt; and &lt;code&gt;&amp;quot;2&amp;quot;&lt;/code&gt;. Shortly thereafter, a single transaction read key &lt;code&gt;2980&lt;/code&gt; twice and found a string, then an apparently unrelated integer:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;TOML&quot;&gt;[&amp;quot;select (val) from txn2 where id = ? &amp;quot; 2980]
  -&amp;gt; [{:val &amp;quot;2,1&amp;quot;}]
[&amp;quot;select (val) from txn0 where sk = ? &amp;quot; 2981]
  -&amp;gt; [{:val &amp;quot;1&amp;quot;}]
[&amp;quot;select (val) from txn2 where sk = ? &amp;quot; 2980]
  -&amp;gt; [{:val 3}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/37&quot;&gt;#37&lt;/a&gt;) seemed to disappear by 1.1.18.11.rc-1.&lt;/p&gt;
&lt;h3&gt;Eternal Sunshine of the Spotless Table (#72)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.11.rc-1, &lt;code&gt;append&lt;/code&gt; tests of the &lt;code&gt;table-2pl&lt;/code&gt; transaction mode often threw errors like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PDB wire error 1064: SQL execution error: failed to create RecordBatch: Invalid argument error: Column ‘id’ is declared as non-nullable but contains null values&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is odd. The &lt;code&gt;append&lt;/code&gt; workload always picks a unique integer ID for every write, and never tries to write a &lt;code&gt;NULL&lt;/code&gt; ID. It also never updates an ID—they are set only once, when the row is created. Here’s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260610T110736-contains-null-values.zip&quot;&gt;an example&lt;/a&gt; where a single &lt;code&gt;SELECT&lt;/code&gt; statement, issued outside of a transaction, complained of null primary keys. This suggests that the table state had somehow become corrupt.&lt;/p&gt;
&lt;p&gt;We no longer observed this error (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/72&quot;&gt;#72&lt;/a&gt;) in 1.1.18.11.rc-7.&lt;/p&gt;
&lt;h2&gt;Transactions and Concurrency&lt;/h2&gt;
&lt;p&gt;We found several problems with ParticleDB’s transactions and concurrency control. All of these occurred in single-node instances without fault injection.&lt;/p&gt;
&lt;h3&gt;Nested Transactions Destroy Ongoing Ones (#44)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.4 through 1.1.18.9 allowed clients to begin nested transactions, instead of warning that a transaction was already ongoing. Any writes performed by the enclosing transaction would be lost, both within the nested transaction and after commit.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a int);
root=&amp;gt; BEGIN;
root=*&amp;gt; INSERT INTO t VALUES (3);
root=*&amp;gt; BEGIN;
root=*&amp;gt; SELECT * FROM t;
 a
---
(0 rows)
root=*&amp;gt; COMMIT;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ParticleDB 1.1.18.11.rc-1 seems to have resolved this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/44&quot;&gt;#44&lt;/a&gt;): issuing &lt;code&gt;BEGIN&lt;/code&gt; within a transaction warned &lt;code&gt;there is already a transaction in progress&lt;/code&gt;, and preserved the ongoing transaction’s writes.&lt;/p&gt;
&lt;h3&gt;Aborted Transactions Can Commit (#43)&lt;/h3&gt;
&lt;p&gt;Between versions 1.1.17 and 1.1.18.4, ParticleDB handled aborted transactions irregularly. With the Postgres interface, an attempt to commit an aborted transaction would throw an error, as one would expect:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; BEGIN;
root=&amp;gt; INSERT INTO t (a) VALUES (3);
root=&amp;gt; THIS IS NOT VALID SQL;
ERROR:  sql parser error: Expected: an SQL
statement, found: THIS at Line: 1, Column: 1
root=&amp;gt; COMMIT;
ROLLBACK
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nevertheless, the writes performed by the aborted transaction persisted:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;SELECT * FROM t;
  -&amp;gt; [{:a 3}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ParticleDB client had a different, also unexpected behavior: executing a &lt;code&gt;COMMIT&lt;/code&gt; statement on a failed transaction returned &lt;code&gt;[#:next.jdbc{:update-count 0}]&lt;/code&gt;, rather than an error. Although the transaction apparently committed, its effects were invisible. Both interfaces, as of version 1.1.18.11.rc-18, returned errors and did not expose the effects of aborted transactions (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/43&quot;&gt;#43&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Aggregates Sometimes Fail to Observe a Transaction’s Rows (#48)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.9, transactions which performed aggregate queries, like &lt;code&gt;SELECT COUNT(*)&lt;/code&gt;, could miss rows they should have matched, and return duplicate copies of rows, depending on the predicate and previous queries in the transaction. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a int);
root=&amp;gt; INSERT INTO t (a) VALUES (2);
root=&amp;gt; BEGIN;
root=*&amp;gt; INSERT INTO t (a) VALUES (3);
root=*&amp;gt; SELECT COUNT(*) FROM t;
 COUNT 
-------
     1
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This table clearly contains two rows: &lt;code&gt;2&lt;/code&gt; and &lt;code&gt;3&lt;/code&gt;. Yet while the second row was visible to &lt;code&gt;SELECT *&lt;/code&gt;, it was invisible to &lt;code&gt;SELECT COUNT(*)&lt;/code&gt;. Oddly enough, aggregate functions which interacted with the column’s value, like &lt;code&gt;SELECT COUNT(*) FROM t WHERE a &amp;lt; 5&lt;/code&gt;, sometimes observed correct state. In other circumstances, &lt;code&gt;SELECT&lt;/code&gt; queries appeared to create duplicate copies of rows within the transaction, causing each query to observe more and more rows. These behaviors (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/48&quot;&gt;#48&lt;/a&gt;) no longer appeared in version 1.1.18.11.rc-1.&lt;/p&gt;
&lt;h3&gt;Double Trouble (#89)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.18.11.rc-9 would occasionally return duplicate copies of a single row, even though the row had a primary key which ought to have ensured uniqueness. For example, take &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260617T165351-duplicate-rows.zip&quot;&gt;this append test&lt;/a&gt;, in which a read of key &lt;code&gt;10&lt;/code&gt; returned two rows, rather than one.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;[{:val &amp;quot;1,2,5,10&amp;quot;}
 {:val &amp;quot;1,2,5,10&amp;quot;}]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We no longer observed duplicate rows (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/89&quot;&gt;#89&lt;/a&gt;) in 1.1.18.11.rc-18.&lt;/p&gt;
&lt;h3&gt;Updates to Zero Values Sometimes Fail Silently (#91)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.4, a transaction could set all rows in a table to some value, receive an acknowledgement that every row had been updated, then read the table to find that some rows still had their previous values. This bug seemed to hinge on inserting rows with a special “zero” value: &lt;code&gt;0&lt;/code&gt; for integers, &lt;code&gt;0.0&lt;/code&gt; for floats, and &lt;code&gt;&#39;&#39;&lt;/code&gt; for various kinds of string columns. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a INT);
root=&amp;gt; INSERT INTO t (a) VALUES (0);
root=&amp;gt; BEGIN;
root=*&amp;gt; UPDATE t SET a = 1;
root=*&amp;gt; INSERT INTO t (a) VALUES (0);
root=*&amp;gt; UPDATE t SET a = 2;
UPDATE 2
root=*&amp;gt; SELECT t.a FROM t;
 a
---
 2
 0
(2 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ParticleDB claimed that it updated all two rows in this table, but showed the original value of &lt;code&gt;0&lt;/code&gt; for one of them. This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/91&quot;&gt;#91&lt;/a&gt;) seemed fixed by 1.1.18.11.rc-9.2.&lt;/p&gt;
&lt;h3&gt;Tables With a &lt;code&gt;TEXT&lt;/code&gt; Primary Key Are Mostly Invisible (#41)&lt;/h3&gt;
&lt;p&gt;Version 1.1.18.9 fixed several safety issues, but also introduced new ones. For example, tables with a &lt;code&gt;text&lt;/code&gt; primary key were mostly invisible to transactions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a TEXT PRIMARY KEY);
root=&amp;gt; INSERT INTO t (a) VALUES (&amp;#39;mood&amp;#39;);
root=&amp;gt; BEGIN;
root=*&amp;gt; SELECT * FROM t;
 a
---
(0 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; were all affected. Rows were invisible to a broad variety of predicates, including &lt;code&gt;WHERE a LIKE &#39;moo%&#39;&lt;/code&gt; and &lt;code&gt;WHERE a IS NOT NULL&lt;/code&gt;. However, rows were still accessible by primary key:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=*&amp;gt; SELECT * FROM t WHERE a = &amp;#39;mood&amp;#39;;
  a
------
 mood
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Version 1.1.18.11.rc-1 no longer exhibited these mostly-invisible tables (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/41&quot;&gt;#41&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;A Thousand Tableaus (#38, #66, #68, #69)&lt;/h3&gt;
&lt;p&gt;Perhaps owing to the single-threaded issues discussed above, ParticleDB 1.1.4 through 1.1.18.9 exhibited frequent, profound transactional anomalies in essentially every way we could measure. We routinely observed the loss of committed writes, internal consistency violations, G1a (Aborted Read), G1b (Intermediate Read), and P4 (Lost Update). In the &lt;code&gt;append&lt;/code&gt; workload, successfully appended values would routinely disappear from later reads of a given key. Our test suite inferred every kind of cyclic anomaly including G0, G1c, G-Single, and G2-item, but many of these can be attributed to more basic issues, like write loss. These issues occurred with a single server and without faults, and affected every transaction mode, interface, and WAL mode tested.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-8&quot; id=&quot;fnref-8&quot; data-footnote-ref&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Since we have already discussed single-threaded anomalies at length, we omit detailed discussions of these issues. By version 1.1.18.11.rc-1, ParticleDB’s most glaring examples of G1a (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/66&quot;&gt;#66&lt;/a&gt;), G1b (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/38&quot;&gt;#38&lt;/a&gt;), internal consistency anomalies (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/68&quot;&gt;#68&lt;/a&gt;), write loss (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/69&quot;&gt;#69&lt;/a&gt;), and so on were much diminished.&lt;/p&gt;
&lt;h3&gt;G-Single (#73)&lt;/h3&gt;
&lt;p&gt;With versions 1.1.18.11.rc-1 through 1.1.18.11.rc-22, we occasionally observed &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/phenomena/g-single&quot;&gt;G-single&lt;/a&gt;, a violation of Snapshot Isolation, in single nodes without faults. For example, here are two transactions from &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260610T113409-g-single.zip&quot;&gt;an &lt;code&gt;append&lt;/code&gt; test&lt;/a&gt; using transaction mode &lt;code&gt;occ-v3&lt;/code&gt; and WAL mode &lt;code&gt;groupsync&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;The top transaction read key &lt;code&gt;9&lt;/code&gt; and found it did not exist, then appended &lt;code&gt;8&lt;/code&gt; to it. The bottom transaction appended &lt;code&gt;6&lt;/code&gt; to key &lt;code&gt;9&lt;/code&gt;. Since every read of key &lt;code&gt;9&lt;/code&gt; observed a value like &lt;code&gt;[6 8 12 ...]&lt;/code&gt;, we know that the append of &lt;code&gt;6&lt;/code&gt; happened before the append of &lt;code&gt;8&lt;/code&gt;. The top transaction failed to observe the bottom, even though the bottom must have occurred before the top. This is a similar phenomenon to P4 (Lost Update), and violates Snapshot Isolation.&lt;/p&gt;
&lt;p&gt;Similar behavior (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/73&quot;&gt;#73&lt;/a&gt;) occurred in &lt;code&gt;zero-copy-occ&lt;/code&gt; and 1.1.18.11.rc-22’s &lt;code&gt;occ&lt;/code&gt;. Orkes is investigating. Following our collaboration, Orkes reports version 1.1.19-rc.13 no longer exhibits G-Single.&lt;/p&gt;
&lt;h3&gt;Brazil (#67)&lt;/h3&gt;
&lt;p&gt;In version 1.1.18.9, we found that queries for a specific row by a unique, constant integer ID would occasionally return the value of a completely different row. For example, consider &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260609T162039-g1a-g1b-write-loss.zip&quot;&gt;this &lt;code&gt;append&lt;/code&gt; test&lt;/a&gt; running against a single, healthy node with transaction mode &lt;code&gt;occ-v3&lt;/code&gt; and WAL mode &lt;code&gt;groupsync&lt;/code&gt;. Reads of key &lt;code&gt;12&lt;/code&gt; mostly observed values consistent with the appends to key &lt;code&gt;12&lt;/code&gt;, but for a brief time observed nothing, and then the value of key &lt;code&gt;7&lt;/code&gt; instead:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time (s)&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;6.02&lt;/td&gt;
&lt;td&gt;1, 2, 7, … 82&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.05&lt;/td&gt;
&lt;td&gt;1, 2, 7, … 82&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.17&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.22&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.24&lt;/td&gt;
&lt;td&gt;1, 2, 7, … 82, 92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.24&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.25&lt;/td&gt;
&lt;td&gt;1, 5, 8, … 181, 86&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.29&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.29&lt;/td&gt;
&lt;td&gt;1, 5, 8, … 181, 86&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.40&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.49&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6.68&lt;/td&gt;
&lt;td&gt;1, 2, 7, … 82, 92, … 139, 142&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The value &lt;code&gt;[1, 5, 8, ... 181, 86]&lt;/code&gt; was consistent with reads of key &lt;code&gt;7&lt;/code&gt;, not key &lt;code&gt;12&lt;/code&gt;. For the remainder of the test, reads observed values consistent with the original version of key &lt;code&gt;12&lt;/code&gt;: &lt;code&gt;[1, 2, 7, ...]&lt;/code&gt;. This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/67&quot;&gt;#67&lt;/a&gt;) did not appear in our tests after version 1.1.18.11.rc-1.&lt;/p&gt;
&lt;h3&gt;Lost Writes With &lt;code&gt;table-2pl&lt;/code&gt; (#70)&lt;/h3&gt;
&lt;p&gt;In versions 1.1.18.11.rc-1 through 1.1.18.11.rc-6, ParticleDB’s &lt;code&gt;table-2pl&lt;/code&gt; transaction mode routinely lost committed writes during normal operation. For example &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260610T085702.901-0500-table-2pl-write-loss-deadlock.zip&quot;&gt;this &lt;code&gt;append&lt;/code&gt; test&lt;/a&gt;, using the ParticleDB Java client, lost writes to dozens of keys in just a few seconds. Here are some of its reads of key &lt;code&gt;187&lt;/code&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time (s)&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10.89&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.89&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.92&lt;/td&gt;
&lt;td&gt;1, 6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.92&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.92&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.92&lt;/td&gt;
&lt;td&gt;1, 6, 10, 11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.93&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11.09&lt;/td&gt;
&lt;td&gt;2, 13&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Since the &lt;code&gt;append&lt;/code&gt; workload only appends to rows and never deletes them, every read should have observed a prefix of the longest version of the list. Instead it appears that the append of &lt;code&gt;5&lt;/code&gt;, then the appends of &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;6&lt;/code&gt;, &lt;code&gt;10&lt;/code&gt;, and &lt;code&gt;11&lt;/code&gt;, were lost. Moreover, some of these writes were lost in the middle of a transaction, resulting in internal consistency errors.&lt;/p&gt;
&lt;p&gt;By version 1.1.18.11.rc-7, &lt;code&gt;table-2pl&lt;/code&gt; no longer lost writes this frequently (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/70&quot;&gt;#70&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Internal Anomalies With &lt;code&gt;occ-v3&lt;/code&gt; (#96)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.18.11.rc-9.2 occasionally violated internal consistency using transaction mode &lt;code&gt;occ-v3&lt;/code&gt; and WAL sync mode &lt;code&gt;groupsync&lt;/code&gt;. For example, take &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260620T151000-1.1.18.11.rc-9-internal.zip&quot;&gt;this &lt;code&gt;append&lt;/code&gt; test&lt;/a&gt; where a single transaction read &lt;code&gt;nil&lt;/code&gt;, then &lt;code&gt;[1, 4]&lt;/code&gt; for key &lt;code&gt;8&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;[[:r 8 nil] [:r 8 [1 4]]]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At Read Atomic and above, two successive reads of the same key should not return different results. This also violates Snapshot Isolation and Serializability. This bug (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/96&quot;&gt;#96&lt;/a&gt;) may have been resolved by 1.1.18.11.rc-19.&lt;/p&gt;
&lt;h3&gt;There is No Anti-Memetics Division (#65)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.18.11.rc-1 through 1.1.18.11.rc-9.1 would often forget a column existed. A query like &lt;code&gt;SELECT * FROM snacks WHERE sk = 4&lt;/code&gt; would throw an error like &lt;code&gt;PDB wire error 1064: column not found: sk&lt;/code&gt;. This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/65&quot;&gt;#65&lt;/a&gt;) occurred in every transaction mode, but seemed to be resolved by version 1.1.18.11.rc-9.2.&lt;/p&gt;
&lt;h3&gt;Six Data Blockers and a Large Iced Coffee (#31)&lt;/h3&gt;
&lt;p&gt;Versions 1.1.18.8 through 1.1.18.11.rc-22 exhibited perplexing nondeterministic failures in the &lt;code&gt;compare&lt;/code&gt; workload. Each test case established a fresh database client, created a globally unique table, and performed operations against that table synchronously. At no point should there have been two clients concurrently accessing a table. And yet sometimes, rows failed to appear in queries:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t_876 (a boolean);
root=&amp;gt; INSERT INTO t_876 (a) VALUES (NULL);
root=&amp;gt; INSERT INTO t_876 (a) VALUES (NULL);
root=&amp;gt; SELECT * FROM t_876 WHERE TRUE;
  a
------
 NULL
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One could update rows using the predicate &lt;code&gt;WHERE NULL&lt;/code&gt;, which should never match anything:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t_40 (a boolean);
root=&amp;gt; INSERT INTO t_40 (a) VALUES (FALSE);
root=&amp;gt; UPDATE t_40 SET a = (a &amp;lt;= a) WHERE NULL;
UPDATE 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In many cases single SQL statements threw errors complaining of concurrent updates, even though at most one client should ever have accessed a given table.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t_101 (
  a text COLLATE unicode PRIMARY KEY
);
root=&amp;gt; INSERT INTO t_101 (a) VALUES (&amp;#39; &amp;#39;);
root=&amp;gt; UPDATE t_101 SET a = NULL;
ERROR: could not serialize access due to
       concurrent update on table &amp;quot;t_101&amp;quot;
       key &amp;quot; &amp;quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deleted rows could also remain visible:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t_27763 (
  a boolean,
  b boolean NOT NULL UNIQUE PRIMARY KEY
);
root=&amp;gt; INSERT INTO t_27763 (a, b)
       VALUES (NULL, TRUE);
root=&amp;gt; DELETE FROM t_27763;
root=&amp;gt; SELECT t_27763.a AS j FROM t_27763
       WHERE t_27763.b ORDER BY t_27763.b;
  j
------
 NULL
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These anomalies (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/31&quot;&gt;#31&lt;/a&gt;) occurred with all transaction modes and WAL sync modes, but attempts to reproduce them deterministically, or to narrow down their cause, were unsuccessful. They may be artifacts of a bug in the test suite, but we lacked time to investigate fully. After our collaboration, Orkes reported 1.1.19-rc.13 resolved this issue.&lt;/p&gt;
&lt;h3&gt;Read Skew With &lt;code&gt;table-2pl&lt;/code&gt; (#106)&lt;/h3&gt;
&lt;p&gt;With the ParticleDB interface, WAL mode &lt;code&gt;groupsync&lt;/code&gt;, and &lt;code&gt;table-2pl&lt;/code&gt;, version 1.1.18.11.rc-18 exhibited &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/phenomena/g-single&quot;&gt;G-single&lt;/a&gt; under normal operation. Here’s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260721T143917-table-2pl-g-single.zip&quot;&gt;an example&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;T1: [[:r 112 nil]
     [:append 112 1]
     [:append 103 29]
     [:append 67 105]]

T2: [[:r 110 [10 14 13 20 16 28 30 33 48 35]]
     [:r 112 [1]]
     [:r 110 [10 14 13 20 16 28 30 33 48 35]]
     [:r 103 [1 4 11 15 17 20 24 25]]]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first transaction appended &lt;code&gt;1&lt;/code&gt; to key &lt;code&gt;112&lt;/code&gt; and &lt;code&gt;29&lt;/code&gt; to key &lt;code&gt;103&lt;/code&gt;. The second transaction observed the append to key &lt;code&gt;112&lt;/code&gt;, but missed the append to key &lt;code&gt;103&lt;/code&gt;. Since it observed but also failed to observe another transaction, this is &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/phenomena/a5a&quot;&gt;A5A (Read Skew)&lt;/a&gt;. Since the first transaction read-write depends on the second, and the second write-read depends on the first, it is also G-single. Both are violations of Snapshot Isolation.&lt;/p&gt;
&lt;p&gt;We no longer observed Read Skew with &lt;code&gt;table-2pl&lt;/code&gt; (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/106&quot;&gt;#106&lt;/a&gt;) by version 1.1.18.11.rc-19.&lt;/p&gt;
&lt;h3&gt;Stale Read With &lt;code&gt;strict-serializable&lt;/code&gt; (#109)&lt;/h3&gt;
&lt;p&gt;Version 1.1.18.11.rc-18 added a new transaction mode, &lt;code&gt;strict-serializable&lt;/code&gt;, which may have been renamed to &lt;code&gt;serializable&lt;/code&gt; in 1.1.18.11.rc-22.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-9&quot; id=&quot;fnref-9&quot; data-footnote-ref&gt;9&lt;/a&gt;&lt;/sup&gt; Strict Serializability (also known as Strong Serializability) guarantees that all transactions appear to execute in real-time order: if &lt;em&gt;T&lt;/em&gt;1 completes before &lt;em&gt;T&lt;/em&gt;2 begins, then &lt;em&gt;T&lt;/em&gt;1 appears to execute before &lt;em&gt;T&lt;/em&gt;2. However, this was not true in 1.1.18.11.rc-18. Healthy ParticleDB nodes infrequently but regularly exhibited &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/phenomena/stale-read&quot;&gt;Stale Read&lt;/a&gt;, even with WAL mode &lt;code&gt;sync&lt;/code&gt;. In one of our testing environments, these anomalies appeared roughly every 300,000 transactions, using Elle’s exponential key distribution and 10 concurrent clients pushing ~80 transactions per second.&lt;/p&gt;
&lt;p&gt;Stale reads all seemed to share the same structure: a cycle comprising a real-time, read-write, then write-read edge. Here’s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260723T023218-strict-serializable-stale-read.zip&quot;&gt;an example&lt;/a&gt; involving a single key:&lt;/p&gt;
&lt;p&gt;The top transaction appended &lt;code&gt;66&lt;/code&gt; to key &lt;code&gt;22209&lt;/code&gt;, which was read by the middle transaction. After the middle transaction completed, the bottom transaction began, and read key &lt;code&gt;22209&lt;/code&gt; without &lt;code&gt;66&lt;/code&gt;. This is a violation of Strong Snapshot Isolation, and therefore Strong Serializability as well.&lt;/p&gt;
&lt;p&gt;After the conclusion of our testing work, Orkes reported that version 1.1.19-rc.10 no longer exhibits stale read (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/109&quot;&gt;#109&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Lost Writes (#110)&lt;/h3&gt;
&lt;p&gt;In version 1.1.18.11.rc-18 through 1.1.18.11.rc-22, a healthy ParticleDB node running &lt;code&gt;occ-v3&lt;/code&gt;, &lt;code&gt;zero-copy-occ&lt;/code&gt;, &lt;code&gt;table-2pl&lt;/code&gt;, or &lt;code&gt;serializable-ssi&lt;/code&gt; would occasionally discard acknowledged writes. For example, take &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260724T083315-occ-v3-data-loss.zip&quot;&gt;this append test&lt;/a&gt;, which ran with WAL mode &lt;code&gt;sync&lt;/code&gt;. Out of approximately 20,000 keys, 31 lost writes. Here are some of the reads of key &lt;code&gt;16647&lt;/code&gt;:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time (s)&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2798.88&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2798.88&lt;/td&gt;
&lt;td&gt;1, 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2798.88&lt;/td&gt;
&lt;td&gt;1, 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2798.90&lt;/td&gt;
&lt;td&gt;1, 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2798.90&lt;/td&gt;
&lt;td&gt;1, 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2798.93&lt;/td&gt;
&lt;td&gt;1, 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2798.96&lt;/td&gt;
&lt;td&gt;1, 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2798.98&lt;/td&gt;
&lt;td&gt;4, 5, 6, 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2798.98&lt;/td&gt;
&lt;td&gt;4, 5, 6, 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2799.02&lt;/td&gt;
&lt;td&gt;4, 5, 6, 7, 27&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2799.02&lt;/td&gt;
&lt;td&gt;1, 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2799.02&lt;/td&gt;
&lt;td&gt;1, 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2799.02&lt;/td&gt;
&lt;td&gt;1, 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2799.02&lt;/td&gt;
&lt;td&gt;4, 5, 6, 7, 27&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;While the transaction which wrote &lt;code&gt;3&lt;/code&gt; committed successfully, and its write was visible to several other transactions, the elements &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;, and &lt;code&gt;3&lt;/code&gt; were replaced by &lt;code&gt;4, 5, 6, ...&lt;/code&gt; after 2799.02 seconds, and never reappeared. This occurred every few minutes in our tests, with both the Postgres and ParticleDB interfaces.&lt;/p&gt;
&lt;p&gt;A transaction could also read an acknowledged write, modify that row, and find that the modification apparently destroyed the written value, leading to internal anomalies as well as data loss. After we finished testing Orkes released version 1.1.19-rc.13, and believes that it fixes this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/110&quot;&gt;#110&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;With Faults&lt;/h2&gt;
&lt;p&gt;The issues described above occurred in healthy, single-node instances of ParticleDB. When we introduced faults, new behaviors emerged.&lt;/p&gt;
&lt;h3&gt;Data Loss on WAL Bitflips (#108)&lt;/h3&gt;
&lt;p&gt;ParticleDB uses checksums to detect corruption or incomplete writes to its Write-Ahead Log (WAL) files. Sometimes these checks prevented the server from starting up with corrupt data. In other cases, ParticleDB would discover that its data files were corrupt, then start up anyway, serving data to clients.&lt;/p&gt;
&lt;p&gt;Take &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260722T092352-bitflip-data-loss.zip&quot;&gt;this &lt;code&gt;append&lt;/code&gt; test&lt;/a&gt; of 1.1.18.11.rc-18. At 09:25:09 we introduced two single-bit errors into one of ParticleDB’s &lt;code&gt;.wal&lt;/code&gt; files. At 09:25:39 we read key &lt;code&gt;1754&lt;/code&gt; and observed a list of 44 elements:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;[[:r 1754 [3 5 7 9 12 13 18 19 20 22 24 25 28
           30 31 33 34 36 35 37 38 39 42 43 44
           45 47 50 51 53 58 59 62 63 64 65 67
           68 69 70 73 75 79 80]] ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few milliseconds later, we killed and restarted ParticleDB a few times. When the server came back online at 09:26:07, it logged:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;2026-07-22T14:26:07.985223Z  WARN ThreadId(01)
recover_streaming{dir=/opt/particledb/data/wal}:
spanner_wal::segment: checksum mismatch in
segment tail, stopping read segment_id=0

2026-07-22T14:26:07.985235Z  WARN ThreadId(01)
recover_streaming{dir=/opt/particledb/data/wal}:
spanner_wal::recovery: replaying valid prefix
of already-sealed corrupt WAL segment (.corrupt
marker present) and continuing with later
segments of this stripe stripe_id=0 segment_id=0
corruption_offset=25184 valid_size=25184
recovered_entries=242 detail=&amp;quot;checksum mismatch:
stored 0xf3b8aa37, computed 0x4a774da2&amp;quot;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, the server went on to start up and serve client requests normally. At 09:26:09, we read key &lt;code&gt;1754&lt;/code&gt; and found those 44 elements had disappeared:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;[... [:r 1754 [81]] ...]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Several other keys also lost some or all of their acknowledged writes. Orkes is investigating this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/108&quot;&gt;#108&lt;/a&gt;). While Jepsen has not tested this version, Orkes reports 1.1.19-rc.12 detects bitflips correctly and refuses to start.&lt;/p&gt;
&lt;h3&gt;Total Data Loss With Simulated Misdirected Reads/Writes (#112)&lt;/h3&gt;
&lt;p&gt;One of our file corruption faults divided a file into four-kilobyte chunks, then randomly replaced a small fraction of those chunks with other chunks from the same file. The goal is to simulate &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.cse.cuhk.edu.hk/~pclee/www/pubs/msst14.pdf&quot;&gt;misdirected reads or writes&lt;/a&gt;, a type of silent data corruption that happens in some storage systems. In 1.1.18.11.rc-22, ParticleDB sometimes responded to this type of fault by forgetting every table and (apparently) deleting everything in its WAL file.&lt;/p&gt;
&lt;p&gt;For example, take &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260730T095243-copy-file-chunks-table-lost.zip&quot;&gt;this test run&lt;/a&gt;. At 09:53:56, the test harness shuffled 13 four-kilobyte chunks of the first &lt;code&gt;.wal&lt;/code&gt; file around—approximately one-tenth of the file’s contents. It then killed and restarted the node. Although this file had hundreds of kilobytes in it, when ParticleDB started up it logged that there was no data on disk:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;2026-07-30T14:53:58.715589Z  INFO ThreadId(01)
spanner_exec::engine::context: No data found in
storage — starting fresh
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At 09:53:58, ParticleDB began throwing &lt;code&gt;ERROR: table not found&lt;/code&gt; for every query. No request ever succeeded after this time.&lt;/p&gt;
&lt;p&gt;It appears that ParticleDB may have deleted the contents of its WAL file. A few seconds later, the test found its size was zero bytes, and the same held true at the end of the test. After we finished testing Orkes released version 1.1.19-rc.12; they believe it resolves this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/112&quot;&gt;#112&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Internal Anomalies With &lt;code&gt;strict-serializable&lt;/code&gt; and Process Kills (#107)&lt;/h3&gt;
&lt;p&gt;With &lt;code&gt;strict-serializable&lt;/code&gt;, WAL mode &lt;code&gt;groupsync&lt;/code&gt;, and the ParticleDB interface, single nodes running 1.1.18.11.rc-18 exhibited internal anomalies with process kills. Here’s &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://s3.amazonaws.com/jepsen.io/analyses/particledb-1.1.4/20260721T150149-strict-serializable-internal.zip&quot;&gt;an example&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;{:op {:index 190704,
      :time 100211183125,
      :type :ok,
      :process 59,
      :f :txn,
      :value [[:r 2879 nil]
              [:append 2879 16]
              [:r
               2879
               [1 2 3 4 5 7 8 16]]]},
 :mop [:r 2879 [1 2 3 4 5 7 8 16]],
 :expected [16]}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This transaction read key &lt;code&gt;2879&lt;/code&gt; as &lt;code&gt;nil&lt;/code&gt;, then appended to it, and read its value—only to find that seven elements had been appended in the intervening time. Transactions could also exhibit G-single and P4 (G-single), but we suspect these could be due to the same underlying bug. While Jepsen has not had a chance to test this release, Orkes claims this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/107&quot;&gt;#107&lt;/a&gt;) was resolved in version 1.1.19-rc.12.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;№&lt;/th&gt;
&lt;th&gt;Summary&lt;/th&gt;
&lt;th&gt;Event Required&lt;/th&gt;
&lt;th&gt;Fixed in&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/62&quot;&gt;#62&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Abysmal performance with &lt;code&gt;table-2pl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/81&quot;&gt;#81&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Mystery Crash&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/27&quot;&gt;#27&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Deadlock after two transactions&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/71&quot;&gt;#71&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Deadlock with &lt;code&gt;table-2pl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/33&quot;&gt;#33&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ordering by an unused column&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/6&quot;&gt;#6&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;No static check for comparators of incomparable types&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/94&quot;&gt;#94&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Decimals not comparable to floats&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/12&quot;&gt;#12&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Filter predicates reject &lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/13&quot;&gt;#13&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Cannot compare values to &lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/30&quot;&gt;#30&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Boolean logic on integers&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/50&quot;&gt;#50&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;More boolean logic on integers&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/5&quot;&gt;#5&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Multiple assignment to a single column&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/10&quot;&gt;#10&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Predicates can refer to nonexistent columns&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/20&quot;&gt;#20&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;No aggregates within &lt;code&gt;CASE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/28&quot;&gt;#28&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;No exponent operator (&lt;code&gt;^&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/2&quot;&gt;#2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Malformed responses for prepared statements&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/83&quot;&gt;#83&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;select&lt;/code&gt; throws &lt;code&gt;Bad value for type long&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.19-rc.12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/111&quot;&gt;#111&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Tuples with no field structure&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.19-rc.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/1&quot;&gt;#1&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Broken Postgres authentication&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/3&quot;&gt;#3&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Java client could not execute transactions&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/14&quot;&gt;#14&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;No large integer literals&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/85&quot;&gt;#85&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;No exponentiation on &lt;code&gt;Infinity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/49&quot;&gt;#49&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-1 ^ 0 = -1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/88&quot;&gt;#88&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1.0 + 1E-39 = 1.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-9.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/56&quot;&gt;#56&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Large literals become &lt;code&gt;Infinity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/61&quot;&gt;#61&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Exponent underflow&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/34&quot;&gt;#34&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Division by integer &lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/51&quot;&gt;#51&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Modulo operator cannot handle floats&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/36&quot;&gt;#36&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NaN&lt;/code&gt; arithmetic throws errors&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/92&quot;&gt;#92&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NULL&lt;/code&gt; arithmetic throws&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/60&quot;&gt;#60&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UPDATE&lt;/code&gt; arithmetic throws errors&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/32&quot;&gt;#32&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Same precision for &lt;code&gt;real&lt;/code&gt; and &lt;code&gt;double precision&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/11&quot;&gt;#11&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Integers allow out-of-range values&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/86&quot;&gt;#86&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Dividing nearby numbers fails&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/87&quot;&gt;#87&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Floating-point errors in integer values&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/19&quot;&gt;#19&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Floats not equal to quoted representation&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/63&quot;&gt;#63&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt; is not equal to &lt;code&gt;0.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/80&quot;&gt;#80&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;5&lt;/code&gt; appears equal to &lt;code&gt;0.90...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/64&quot;&gt;#64&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CHARACTER(n)&lt;/code&gt; allows more than &lt;code&gt;n&lt;/code&gt; characters&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/52&quot;&gt;#52&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CHARACTER(n)&lt;/code&gt; allows more than &lt;code&gt;n&lt;/code&gt; characters again&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/55&quot;&gt;#55&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CHARACTER(n)&lt;/code&gt; allows fewer than &lt;code&gt;n&lt;/code&gt; characters&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/22&quot;&gt;#22&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Default text collation ignores &lt;code&gt;LOCALE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/42&quot;&gt;#42&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Mis-sorted text&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/93&quot;&gt;#93&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;More mis-sorted text&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/40&quot;&gt;#40&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SELECT 5 WHERE FALSE&lt;/code&gt; returns &lt;code&gt;5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/47&quot;&gt;#47&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WHERE&lt;/code&gt; clauses with math match nothing&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/18&quot;&gt;#18&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WHERE 4 = 5&lt;/code&gt; matches primary key &lt;code&gt;5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/35&quot;&gt;#35&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Transactional selects return nonsense&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/8&quot;&gt;#8&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NOT NULL&lt;/code&gt; constraints don’t&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/9&quot;&gt;#9&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UNIQUE&lt;/code&gt; constraints don’t&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/29&quot;&gt;#29&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Updates can create duplicate primary keys&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/57&quot;&gt;#57&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Primary keys may be &lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/82&quot;&gt;#82&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Primary key conflicts with deleted rows&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/90&quot;&gt;#90&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Duplicate primary keys&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/58&quot;&gt;#58&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Can’t join integers to floats&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/54&quot;&gt;#54&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Joins omit table names, mangle columns instead&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.19-rc.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/76&quot;&gt;#76&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Joins coerce values to strings&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/59&quot;&gt;#59&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Outer joins to empty tables often produce no rows&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/77&quot;&gt;#77&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Joins confuse columns with same name&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/26&quot;&gt;#26&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Transactions can’t delete anything&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/45&quot;&gt;#45&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Transactions which delete leave shades behind&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/23&quot;&gt;#23&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Updating integers makes rows not-deletable&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/84&quot;&gt;#84&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;double precision&lt;/code&gt; primary keys yield zombie twins&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/78&quot;&gt;#78&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NaN&lt;/code&gt; values are immutable&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/74&quot;&gt;#74&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Fixed-width strings are deeply cursed&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/46&quot;&gt;#46&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Dropped tables can be resurrected, in distorted forms&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/15&quot;&gt;#15&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Infinity&lt;/code&gt; is writable, but unreadable&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/21&quot;&gt;#21&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NaN&lt;/code&gt; sometimes becomes &lt;code&gt;0.0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/7&quot;&gt;#7&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Assignment of incompatible types yields (e.g.) &lt;code&gt;NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/16&quot;&gt;#16&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Values assigned to wrong columns, yielding cursed rows&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/24&quot;&gt;#24&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Updates of no rows create new, corrupt rows instead&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/25&quot;&gt;#25&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;No-op updates cause duplicate key violations&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/37&quot;&gt;#37&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Strings are sometimes returned as 8-bit integers&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/72&quot;&gt;#72&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Primary keys mysteriously become &lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/44&quot;&gt;#44&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Nested transactions destroy ongoing ones&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/43&quot;&gt;#43&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Aborted transactions can commit&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/48&quot;&gt;#48&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Aggregates sometimes miss a transaction’s rows&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/89&quot;&gt;#89&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Queries sometimes observe duplicate copies of rows&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/91&quot;&gt;#91&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Updates to special values can silently fail&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-9.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/41&quot;&gt;#41&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Tables with &lt;code&gt;text&lt;/code&gt; primary keys are mostly invisible&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/66&quot;&gt;#66&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;G1a (Aborted Read)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/38&quot;&gt;#38&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;G1b (Intermediate Read)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/68&quot;&gt;#68&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Internal consistency anomalies&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/69&quot;&gt;#69&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Lost write&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/73&quot;&gt;#73&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;G-single&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.19-rc.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/67&quot;&gt;#67&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Reads observe data from different row&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/70&quot;&gt;#70&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Lost write with &lt;code&gt;table-2pl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/96&quot;&gt;#96&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Occasional internal consistency anomalies with &lt;code&gt;occ-v3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/65&quot;&gt;#65&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Columns sometimes vanish&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-9.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/31&quot;&gt;#31&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Odd nondeterministic single-threaded anomalies&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.19-rc.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/106&quot;&gt;#106&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A5A (Read Skew) with &lt;code&gt;table-2pl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.18.11.rc-19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/109&quot;&gt;#109&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Stale Read with &lt;code&gt;strict-serializable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.19-rc.10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/110&quot;&gt;#110&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Lost writes&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;1.1.19-rc.13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/108&quot;&gt;#108&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Silent(ish) data loss&lt;/td&gt;
&lt;td&gt;WAL truncation/bitflips&lt;/td&gt;
&lt;td&gt;1.1.19-rc.12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/112&quot;&gt;#112&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Total data loss&lt;/td&gt;
&lt;td&gt;Misdirected reads/writes&lt;/td&gt;
&lt;td&gt;1.1.19-rc.12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/107&quot;&gt;#107&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Spurious &lt;code&gt;nil&lt;/code&gt; reads&lt;/td&gt;
&lt;td&gt;Process crash&lt;/td&gt;
&lt;td&gt;1.1.19-rc.12&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Discussion&lt;/h2&gt;
&lt;p&gt;ParticleDB 1.1.4 claimed to be a Postgres-compatible database which offered Snapshot Isolation with its &lt;code&gt;occ-v3&lt;/code&gt; and &lt;code&gt;zero-copy-occ&lt;/code&gt; transaction modes, Serializability with &lt;code&gt;serializable-ssi&lt;/code&gt;, and Strong Serializability with &lt;code&gt;table-2pl&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Instead, we found a library of confusion.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-10&quot; id=&quot;fnref-10&quot; data-footnote-ref&gt;10&lt;/a&gt;&lt;/sup&gt; In versions 1.1.4 through 1.1.18.11.rc-19, basic functionality like transactions (#3), prepared statements (#2), and Postgres auth (#1) were unusably broken. The server spontaneously deadlocked (#27, #71) or crashed (#81) every few minutes. Integers stood in for booleans (#30, #50), comparisons to &lt;code&gt;NULL&lt;/code&gt; threw errors (#13), and you could assign multiple values to the same column (#5). Large floating-point numbers quietly became infinite (#56); &lt;code&gt;Infinity&lt;/code&gt;, &lt;code&gt;NaN&lt;/code&gt;, and &lt;code&gt;NULL&lt;/code&gt; arithmetic were broken (#36, #92, #15, #21); and out-of-range values were improperly stored or clipped to fit (#32, #11). In ParticleDB, &lt;code&gt;0.0&lt;/code&gt; was not equal to &lt;code&gt;0&lt;/code&gt; (#63), but &lt;code&gt;0.90...&lt;/code&gt; wasn’t &lt;em&gt;not&lt;/em&gt; equal to &lt;code&gt;5&lt;/code&gt; (#80). Strings allowed too many (#52, #64) and too few (#55) characters. A &lt;code&gt;WHERE&lt;/code&gt; clause with floating-point operations would silently fail to match any rows (#47), &lt;code&gt;WHERE 4 = 5&lt;/code&gt; returned rows with primary key &lt;code&gt;5&lt;/code&gt; (#18), &lt;code&gt;WHERE a = 1&lt;/code&gt; matched the row &lt;code&gt;{a: 2}&lt;/code&gt;, and &lt;code&gt;WHERE TRUE&lt;/code&gt; matched nothing (#35). Null (#8) and unique (#9) constraints did nothing, and primary keys could be duplicated (#29), null (#57), or both. Primary keys failed to conflict with rows that did exist (#90), and conflicted with rows that didn’t (#82). Joins returned incorrect column names (#54), coerced values to strings (#76), failed to return rows (#59), and confused similarly-named columns (#77). It was impossible to delete rows within a transaction (#26, #45), rows with integers set to floats (#23), or with floats set to integers (#84), or those containing &lt;code&gt;NaN&lt;/code&gt;s (#78), or fixed-width strings (#74).&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-11&quot; id=&quot;fnref-11&quot; data-footnote-ref&gt;11&lt;/a&gt;&lt;/sup&gt; Rows from dropped tables could re-emerge in mangled forms (#46). Columns could be set to values of the wrong type, silently altering or corrupting data (#7). Inserts ignored provided columns, creating rows best described as “deeply cursed” (#16).&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-12&quot; id=&quot;fnref-12&quot; data-footnote-ref&gt;12&lt;/a&gt;&lt;/sup&gt; Updates could create new, shambolic rows where none existed (#24, #25). Strings sometimes turned into eight-bit integers (#37), and primary keys occasionally became &lt;code&gt;NULL&lt;/code&gt; (#72). Aborted transactions could commit, or fail with their effects still visible (#43). Queries sometimes missed (#48), duplicated (#89), or returned the wrong (#67) rows. Tables with a &lt;code&gt;text&lt;/code&gt; primary key were mostly invisible (#41). Special values were sometimes inalterable (#91). Entire columns occasionally vanished for no obvious reason (#65). Uncontended statements on freshly created tables observed nonsensical results or conflicted (apparently) with themselves (#31). Each transaction and WAL mode exhibited essentially every transactional anomaly our tests could detect: write loss, internal consistency violations, G0, G1a, G1b, G1c, G-Single, G-nonadjacent, P4, A5A, G2, and real-time variants thereof (#38, #66, #68, #69, #73, #70, #96, #109, #110, etc.).&lt;/p&gt;
&lt;p&gt;These behaviors occurred in single-node instances of ParticleDB, without fault injection, often with just a single client. When we killed ParticleDB, it allowed internal anomalies, P4 (Lost Update), and G-single (#107), even with the new &lt;code&gt;serializable&lt;/code&gt; transaction mode. Corruption or truncation of WAL files caused partial data loss (#108). Simulating misdirected reads or writes could cause total data loss (#112).&lt;/p&gt;
&lt;p&gt;As we concluded our engagement, version 1.1.18.11.rc-22 had just become available. It abandoned the &lt;code&gt;table-2pl&lt;/code&gt; and &lt;code&gt;serializable-ssi&lt;/code&gt; transaction modes in favor of &lt;code&gt;fast&lt;/code&gt;, &lt;code&gt;occ&lt;/code&gt;, and &lt;code&gt;serializable&lt;/code&gt;, which were intended to offer no consistency, Snapshot Isolation, and Strong Serializability, respectively. While 1.1.18.11.rc-22 was significantly safer than 1.1.4, we were still finding new issues. The default &lt;code&gt;occ&lt;/code&gt; mode regularly lost data in normal operation (#110). The new &lt;code&gt;serializable&lt;/code&gt; mode appeared significantly safer, but still allowed stale read (#109) in normal operation, and with process kills, sometimes failed to read extant rows. This led to internal anomalies, Lost Update, and more (#107).&lt;/p&gt;
&lt;p&gt;After we finished testing, Orkes updated ParticleDB’s documentation to claim that every transaction mode discussed here was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://particledb.ai/operations/modes/#experimental-modes-not-in-current-release-build&quot;&gt;“experimental”&lt;/a&gt;. This claim is difficult to support. To the best of Jepsen’s knowledge, none of the ParticleDB documentation &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518193025/https://particledb.ai/getting-started/concepts/#transaction-modes&quot;&gt;described these modes&lt;/a&gt; as experimental or unsupported during our engagement. Orkes’ unpublished operator guide did describe one mode, &lt;code&gt;serializable-occ&lt;/code&gt;, as an “internal A/B variant… do not use for correctness runs”. We considered that mode experimental and omitted it from this report. However, the others had no such caveats.&lt;/p&gt;
&lt;p&gt;Orkes &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://particledb.ai/operations/modes/&quot;&gt;appears to have reworked&lt;/a&gt; ParticleDB’s approach to consistency. It now supports the standard SQL isolation level statements for &lt;code&gt;READ COMMITTED&lt;/code&gt;, &lt;code&gt;REPEATABLE READ&lt;/code&gt; (meaning Snapshot Isolation), and &lt;code&gt;SERIALIZABLE&lt;/code&gt;. The &lt;code&gt;--txn-mode&lt;/code&gt; option is gone, and a new &lt;code&gt;--concurrency-control&lt;/code&gt; setting has &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;optimistic&lt;/code&gt;, and &lt;code&gt;strict-optimistic&lt;/code&gt; options. The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://particledb.ai/operations/modes/#experimental-modes-not-in-current-release-builds&quot;&gt;documentation claims&lt;/a&gt; that every mode discussed in this report has been removed from version 1.1.19-rc.13. However, it goes on to say that &lt;code&gt;occ-v3&lt;/code&gt; and &lt;code&gt;strict-serializable&lt;/code&gt; were “earlier names” of the “optimistic engine” and “strict serializable mode”, respectively. It is not clear whether these modes were removed or simply renamed.&lt;/p&gt;
&lt;p&gt;Jepsen has not evaluated versions of ParticleDB after 1.1.18.11.rc-22, and cannot speak to these isolation levels or concurrency control modes. However, Orkes claims that all issues from this report have been addressed as of version 1.1.19-rc.13.&lt;/p&gt;
&lt;p&gt;As always, Jepsen takes an experimental approach to safety verification: we can prove the presence of bugs, but not their absence. While we make extensive efforts to find problems, we cannot prove correctness.&lt;/p&gt;
&lt;h2&gt;Impact&lt;/h2&gt;
&lt;p&gt;Orkes believes that the issues we found in 1.1.4 were likely due to an extensive refactor of ParticleDB in spring 2026, and that Orkes and their customers, who continued to run an earlier version, were likely unaffected. Near the end of our collaboration we briefly evaluated an internal build of version 1.0.0. While it exhibited some string and arithmetic issues, that version did not seem susceptible to most of the serious safety errors we found in 1.1.4, and passed cursory &lt;code&gt;rw&lt;/code&gt; and &lt;code&gt;append&lt;/code&gt; tests. We tested only a single node at a time, so we cannot speak to the safety of the replication system.&lt;/p&gt;
&lt;p&gt;From May through July 2026, the ParticleDB &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://particledb.ai/installation/binary/&quot;&gt;download page&lt;/a&gt; offered users (at least) versions 1.1.4 through 1.1.18.11.rc-18.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-13&quot; id=&quot;fnref-13&quot; data-footnote-ref&gt;13&lt;/a&gt;&lt;/sup&gt; All exhibited serious safety errors during normal operation. It seems likely that users who ran these versions of ParticleDB would have lost or corrupted data. In version 1.1.18.11.rc-22, using WAL mode &lt;code&gt;sync&lt;/code&gt; and transaction mode &lt;code&gt;serializable&lt;/code&gt; significantly reduced the frequency and severity of these issues, but did not prevent internal anomalies or Lost Update.&lt;/p&gt;
&lt;p&gt;Given the severity and extent of the safety errors we found, and the process which produced them, Jepsen recommends users halt further deployment of ParticleDB and migrate to other databases as soon as possible. Consider checking and/or rebuilding database state from more reliable sources, if available.&lt;/p&gt;
&lt;h2&gt;The Unreasonable Effectiveness of Generative Testing&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;compare&lt;/code&gt; workload generated random schemas and SQL statements over them, then ran both against ParticleDB and Postgres to see if they produced equivalent results. This should not have worked. There are many ways in which two “Postgres-compatible” databases can legitimately differ, such as floating-point details, which error message takes precedence, or the order of unsorted results. The space of SQL queries is large and difficult to cover. Many queries trivially return equivalent results (e.g. &lt;code&gt;SELECT ... WHERE FALSE&lt;/code&gt;) and therefore tell us little information. We can test only sequential behavior, which is easy for a database’s standard test suite to check.&lt;/p&gt;
&lt;p&gt;Nevertheless, roughly &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/particledb/blob/d4721aeef9435af0131eacfb2163059272b13d57/src/jepsen/particledb/gen/tc.clj&quot;&gt;two thousand lines&lt;/a&gt; of generative tests found a wealth of bugs in ParticleDB, and unlike Jepsen’s typical workloads, the examples generated were usually easy to reproduce. ParticleDB offered a good deal of “low-hanging fruit” that should have been caught by example-based tests—but the &lt;code&gt;compare&lt;/code&gt; workload also found more subtle errors. For example, the fact that &lt;code&gt;0.90...&lt;/code&gt; was apparently equal to &lt;code&gt;5&lt;/code&gt; hinged on a high-precision floating-point literal. Inspired by this success, we extracted part of the ParticleDB SQL generator into &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/jepsen-io/sql&quot;&gt;a shared library&lt;/a&gt; so it could be used in other tests, like the new &lt;code&gt;internal-sim&lt;/code&gt; workload.&lt;/p&gt;
&lt;p&gt;Jepsen has traditionally focused on thorough analysis of a small set of carefully chosen transactions. This is partly because checking (e.g.) Serializability in general &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://dl.acm.org/doi/10.1145/322154.322158&quot;&gt;is NP-complete&lt;/a&gt;, and also because we assume that if transactions of (e.g.) reads and appends work correctly on strings, so will other operations on other datatypes. Our intuition is that the “hard part” is in the concurrency control mechanism, and sequential datatype behavior is generally covered by existing tests. This was not the case in ParticleDB. We’ve expanded Jepsen’s &lt;code&gt;list-append&lt;/code&gt; and &lt;code&gt;rw&lt;/code&gt; tests to use alternate encodings and transaction structures, in an attempt to cover more codepaths. This revealed new bugs in ParticleDB, and we hope it will in other databases too.&lt;/p&gt;
&lt;p&gt;Unfortunately, &lt;code&gt;compare&lt;/code&gt; became less useful as Orkes fixed major bugs and left smaller incompatibilities in place. For example, Postgres checks unique constraints when updating each row of a table, but ParticleDB only checks after all have been updated. This means that a statement like &lt;code&gt;UPDATE bools SET x = NOT x&lt;/code&gt; can swap the values of &lt;code&gt;TRUE&lt;/code&gt; and &lt;code&gt;FALSE&lt;/code&gt; in ParticleDB without violating a unique constraint. Toward the end of our engagement, the &lt;code&gt;compare&lt;/code&gt; test generally hit these minor inconsistencies early, then spent most of its time searching for minimal cases of them—potentially missing more serious bugs.&lt;/p&gt;
&lt;h2&gt;Quality Assurance&lt;/h2&gt;
&lt;p&gt;Orkes said they ran Jepsen’s existing Postgres test harness to find and resolve issues in ParticleDB prior to this work. However, that same test harness failed immediately when run against ParticleDB 1.1.4. Orkes also stated they tested ParticleDB with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;http://www.sqlancer.com/&quot;&gt;SQLancer&lt;/a&gt;, which generates far more complex queries than the ones we tested. It is therefore perplexing that nearly every feature we tested exhibited glaring errors, and that our findings seemed to come as a surprise.&lt;/p&gt;
&lt;p&gt;Most databases have a test suite which exercises at least basic examples of each feature, and it is customary to run those tests before release, if not continuously. Where were those tests for ParticleDB? How does one add support for &lt;code&gt;NOT NULL&lt;/code&gt; constraints without a test which tries to insert at least one &lt;code&gt;NULL&lt;/code&gt; value? What development process adds a &lt;code&gt;CHARACTER(10)&lt;/code&gt; type without trying to write a string of (e.g.) five characters?&lt;/p&gt;
&lt;p&gt;Our findings suggest structural deficiencies in the ParticleDB QA process. Perhaps tests were not run prior to release. Perhaps the test suite was run, but failed to exercise basic functionality. Perhaps engineers entrusted Large Language Models with the work of testing.&lt;/p&gt;
&lt;p&gt;Jepsen recommends that Orkes (and all database vendors!) build and exercise an automated test suite for their database. This suite should include at least simple, example-based tests covering important database features. These tests should fail when the feature is unimplemented or malformed, and pass when it is complete. Observing these failures is an important part of the development process: it &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Mutation_testing&quot;&gt;validates that the test measures something&lt;/a&gt;. Tests should be run at least once before each release, and results disseminated to the team. Afterwards consider more sophisticated generative tests, establishing adversarial QA and implementation teams, and subtly breaking safety mechanisms to determine whether the tests are still effective.&lt;/p&gt;
&lt;h2&gt;Documentation&lt;/h2&gt;
&lt;p&gt;ParticleDB’s website gave the appearance of a mature database project. It made strong claims: a general-purpose OLTP/OLAP SQL database a thousand times faster than PostgreSQL, “formally verified” by a TLA+ proof, multiple APIs, and client libraries in thirteen languages. It offered a variety of deployment options and extensive documentation on database features and operations.&lt;/p&gt;
&lt;p&gt;On closer inspection, that documentation was riddled with omissions, inconsistencies, and confabulations. The TLA+ proof was not published, and in any event did not prevent disaster—perhaps it was incorrect, formalized the wrong properties, or did not describe the system as built. Safety-critical configuration settings like the transaction mode were described in vague terms, like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518193025/particledb.ai/getting-started/concepts&quot;&gt;“safe under concurrency”&lt;/a&gt;. The documentation for the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518203023/https://particledb.ai/sdks/java/&quot;&gt;Java client library&lt;/a&gt; referred to non-existent dependency coordinates and seemed to make up class and package names which were nowhere to be found in the library itself. The quickstart and client examples made up a variety of default usernames for ParticleDB, all of which were wrong. The stored procedure documentation even fabricated an &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260727225950/https://particledb.ai/particles/rust/#run-it&quot;&gt;imaginary email address&lt;/a&gt; for the author of this report. In Orkes’ unpublished operator guide, the descriptions of transaction modes omitted basic phenomena like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/phenomena/g0&quot;&gt;G0&lt;/a&gt; and confused &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/phenomena/g1a&quot;&gt;G1a&lt;/a&gt; (Aborted Read) with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/phenomena/g1c&quot;&gt;G1c&lt;/a&gt; (Circular Information Flow). The Snapshot Isolation modes made no mention of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/phenomena/g-single&quot;&gt;G-single&lt;/a&gt; or the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/consistency/phenomena/g-nonadjacent&quot;&gt;other boundary phenomena&lt;/a&gt; proscribed by Snapshot Isolation, and non-real-time models made no mention of the real-time phenomena they allowed, like Stale Read.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-14&quot; id=&quot;fnref-14&quot; data-footnote-ref&gt;14&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Technical writing is important work that requires skill and care. Jepsen recommends that Orkes work with a human technical writer to rewrite ParticleDB’s documentation, with an eye toward concisely and accurately communicating intended behavior and the guarantees users should expect.&lt;/p&gt;
&lt;p&gt;Jepsen is no stranger to working with early software projects, and there is nothing intrinsically wrong with offering buggy or badly broken software. However, it would be polite for a maintainer aware of serious safety issues to display a prominent warning on the website. One might place a large red box on the download page which reads, “This project is in early development and is not suitable for production”, or more bluntly, “This software has lost data and will do so again”. Orkes should clearly explain the general status of ParticleDB, and advise users of specific known safety issues.&lt;/p&gt;
&lt;p&gt;Orkes has since published &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://particledb.ai/installation/releases/#release-history&quot;&gt;a release history&lt;/a&gt; which describes versions 1.1.4 through 1.1.18.11.rc-22 as “unsupported pre-release builds”. It is not clear how users would have understood versions like 1.1.4 or 1.1.18.9 as “pre-release”. Pre-release software generally has a version number beginning with 0, or including “rc”, “alpha”, etc. The documentation &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518201315/https://particledb.ai/installation/binary/&quot;&gt;did not describe these versions&lt;/a&gt; as pre-release or experimental at the time. Indeed, the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://web.archive.org/web/20260518200305/https://particledb.ai/installation/docker/&quot;&gt;Docker install page&lt;/a&gt; told users how to deploy these versions “for production”.&lt;/p&gt;
&lt;h2&gt;LLM Tradeoffs&lt;/h2&gt;
&lt;p&gt;Large Language Models enable small teams to produce software and documentation of staggering scope and complexity in a remarkably short time. Five years ago, writing a distributed SQL database of this scope might have taken hundreds of person-years.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-15&quot; id=&quot;fnref-15&quot; data-footnote-ref&gt;15&lt;/a&gt;&lt;/sup&gt; With LLM assistance, a handful of engineers at Orkes were able to produce a highly sophisticated database system.&lt;/p&gt;
&lt;p&gt;However, our work on ParticleDB (and other databases) suggests there are serious tradeoffs to relying on LLMs. Quality suffered: ParticleDB was a fractal of correctness errors. LLM tooling repeatedly claimed that bugs had been fixed when, in fact, they had not. ParticleDB also seemed more difficult to understand than typical databases. As with most Jepsen analyses, Orkes engineers offered detailed explanations of the causes and resolutions of the bugs we found in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/orkes-conductor-5.3.2&quot;&gt;Conductor&lt;/a&gt;. However, Orkes seemed unable to explain the causes or resolutions of nearly every issue we found in ParticleDB. New builds simply materialized, sans changelogs, each hopefully containing a slew of improvements. Why did ParticleDB sometimes turn strings into integers? Why did it stop in 1.1.18.11.rc-1? Perhaps no one knows. With LLMs, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.tiktok.com/@charlixcx/video/7603137654682570006&quot;&gt;perhaps no one needs to&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;More broadly, the use of LLMs erodes trust in documentation, in software, and in the organizations which produce them. Perhaps engineers will begin to treat any new software project with suspicion—the signals we used to rely on as markers of a mature system are now easier to counterfeit.&lt;/p&gt;
&lt;p&gt;Where correctness matters—and surely it ought to matter for databases!—our industry must acknowledge and mitigate these tradeoffs.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-16&quot; id=&quot;fnref-16&quot; data-footnote-ref&gt;16&lt;/a&gt;&lt;/sup&gt; In the age of LLMs, what can maintainers do to ensure correct software and accurate documentation? Having done that work, how can they demonstrate its quality to users?&lt;/p&gt;
&lt;h2&gt;Future Work&lt;/h2&gt;
&lt;p&gt;As of 1.1.18.11.rc-22, our &lt;code&gt;append&lt;/code&gt; and &lt;code&gt;rw&lt;/code&gt; workloads were still finding anomalies in every transaction mode. We recommend further testing as development continues. The &lt;code&gt;compare&lt;/code&gt; workload also revealed a number of minor compatibility bugs which we have not discussed here. These small differences could mask more serious errors. Bringing ParticleDB into closer alignment with Postgres could help the &lt;code&gt;compare&lt;/code&gt; workload reveal them.&lt;/p&gt;
&lt;p&gt;Toward the end of our collaboration we tested ParticleDB’s on-disk format, and found that corruption in WAL files could cause data loss. The variety of error messages we found suggests that there might be multiple paths to data loss—further tests should stress on-disk storage more thoroughly. Furthermore, in almost all our tests ParticleDB wrote only WAL files, and stored its tables entirely in memory. We recommend Orkes add a configuration option to reduce the thresholds for on-disk storage, so that future tests can exercise those codepaths.&lt;/p&gt;
&lt;p&gt;The ParticleDB client has a number of slow network timeouts we did not know how to change. These timeouts could conceal safety errors by preventing the test from observing the database during a critical moment. We worked around this by using the Postgres client (whose timeouts are configurable), but we recommend testing the ParticleDB client with lower timeouts as well.&lt;/p&gt;
&lt;p&gt;ParticleDB offered a massive set of features, and we only tested a small portion of them. The SQL specification and Postgres’ dialect of it are astonishingly complex languages, and we used only a small fraction of their syntax and vocabulary. We also did not evaluate ParticleDB’s Redis or HTTP APIs, temporal or geospatial queries, materialized views, vector search, or multi-tenant features. The replication system remains to be tested.&lt;/p&gt;
&lt;p&gt;Orkes is now adding stored procedures to ParticleDB. These user-defined functions, called &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://particledb.ai/particles/overview/&quot;&gt;&lt;em&gt;Particles&lt;/em&gt;&lt;/a&gt;, are written in Python or Rust and executed by the database server. Both Rust and Python have large surface areas, and executing user-provided binaries in a database environment seems like an exciting opportunity. Our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/capela-dda5892&quot;&gt;previous work&lt;/a&gt; with stored procedures suggests this feature is worth further investigation.&lt;/p&gt;
&lt;p&gt;ParticleDB claimed to be “1087x faster than PostgreSQL”. In our tests ParticleDB was nowhere near this fast, and over the course of an hour we observed a linear decline in throughput and a significant increase in median latencies—even over small datasets. Once ParticleDB reaches a baseline level of correctness, revisiting performance benchmarks could be worthwhile.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Our thanks to the ParticleDB team at Orkes, including Jeu George and Nikhil Kala. Jepsen also wishes to thank &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.irenekannyo.com/&quot;&gt;Irene Kannyo&lt;/a&gt; for her editorial support. This research was funded by Orkes, Inc., and conducted in accordance with the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://jepsen.io/analyses/ethics&quot;&gt;Jepsen ethics policy&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Appendix A&lt;/h2&gt;
&lt;p&gt;This appendix contains additional results involving types, syntax, numbers, and strings.&lt;/p&gt;
&lt;h2&gt;Type Safety and Syntax&lt;/h2&gt;
&lt;p&gt;We found several incompatibilities or bugs involving static or dynamic type safety, or behaviors that would normally have been caught by static analysis of a query, such as referring to a nonexistent column. These issues occurred with a single server, single client, and no faults.&lt;/p&gt;
&lt;h3&gt;Ordering By an Unused Column (#33)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.8, you could order an aggregate query’s results by a column that was not used in the aggregate expressions or &lt;code&gt;GROUP BY&lt;/code&gt; clause. This seems like a minor issue, but it does differ from Postgres behavior:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a boolean);
root=&amp;gt; SELECT COUNT(*) FROM t
       WHERE (a &amp;gt;= a) ORDER BY a LIMIT 0;
 COUNT
-------
(0 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Orkes resolved this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/33&quot;&gt;#33&lt;/a&gt;) in 1.1.18.9.&lt;/p&gt;
&lt;h3&gt;Comparison of Incomparable Types (#6)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.4, you could write predicates for &lt;code&gt;UPDATE&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt; which made equality comparisons between columns of different types.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a text, b smallint);
root=&amp;gt; DELETE FROM t WHERE (a = b);
DELETE 0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Postgres, this threw an error like &lt;code&gt;ERROR: operator does not exist: text = smallint&lt;/code&gt;. You could compare strings to boolean columns, or literal values like &lt;code&gt;false&lt;/code&gt;. ParticleDB allowed these comparisons on empty tables, but would throw an error if a record was present. In 1.1.18.6, ParticleDB threw an error for empty tables as well (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/6&quot;&gt;#6&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Cannot Compare Decimals to Floats (#94)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.18.11.rc-9.2 forgot how to compare large decimal literals to 64-bit floating-point numbers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a double precision);
root=&amp;gt; INSERT INTO t (a) VALUES (1.0);
root=&amp;gt; SELECT * FROM t WHERE
       ((0 / 1.2002480805601245E26) &amp;gt;= t.a);
ERROR: arrow compute: Invalid argument error:
       Invalid comparison operation:
       Decimal256(76, 44) &amp;gt;= Float64
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/94&quot;&gt;#94&lt;/a&gt;) no longer appeared by version 1.1.18.11.rc-18.&lt;/p&gt;
&lt;h3&gt;Filter Predicates Reject &lt;code&gt;NULL&lt;/code&gt; (#12)&lt;/h3&gt;
&lt;p&gt;SQL has three boolean values: &lt;code&gt;TRUE&lt;/code&gt;, &lt;code&gt;FALSE&lt;/code&gt;, and &lt;code&gt;NULL&lt;/code&gt;, which represents an unknown value. In ParticleDB 1.1.4 through 1.1.18.4, predicates in SELECT, UPDATE, etc. could not evaluate to &lt;code&gt;NULL&lt;/code&gt;, even though the SQL specification states that search conditions are boolean expressions. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a boolean);
root=&amp;gt; INSERT INTO t (a) VALUES (TRUE);
root=&amp;gt; SELECT * FROM t WHERE NULL;
ERROR:  filter predicate must produce boolean
result
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Postgres this would return an empty relation, because &lt;code&gt;NULL&lt;/code&gt; is not &lt;code&gt;TRUE&lt;/code&gt;. In 1.1.18.6, &lt;code&gt;NULL&lt;/code&gt; predicates evaluated correctly (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/12&quot;&gt;#12&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Cannot Compare Values to &lt;code&gt;NULL&lt;/code&gt; (#13)&lt;/h3&gt;
&lt;p&gt;Per the ANSI SQL standard, the value of comparing anything to &lt;code&gt;NULL&lt;/code&gt; is &lt;em&gt;unknown&lt;/em&gt;, represented as the boolean value &lt;code&gt;NULL&lt;/code&gt;. However, ParticleDB 1.1.4 through 1.1.17 sometimes refused to evaluate predicates which compared (e.g.) integers to &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a int);
root=&amp;gt; INSERT INTO t (a) VALUES (2), (NULL);
root=&amp;gt; UPDATE t SET a = 5 WHERE a = NULL;
ERROR:  arrow compute: Invalid argument error:
Invalid comparison operation: Int64 == Null
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Oddly enough, ParticleDB correctly evaluated statements like &lt;code&gt;SELECT 1 = NULL&lt;/code&gt;. Version 1.1.18.6 correctly compared integers to &lt;code&gt;NULL&lt;/code&gt; (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/13&quot;&gt;#13&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Boolean Logic on Integers (#30)&lt;/h3&gt;
&lt;p&gt;The SQL standard is clear that boolean logical expressions like &lt;code&gt;A AND B&lt;/code&gt; evaluate to booleans. In ParticleDB 1.1.4 through 1.1.18.8, they could sometimes be other things:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT TRUE AND 5;      # =&amp;gt; 5
root=&amp;gt; SELECT 5 AND TRUE;      # =&amp;gt; 5
root=&amp;gt; SELECT 5 AND FALSE;     # =&amp;gt; FALSE
root=&amp;gt; SELECT 5 OR TRUE;       # =&amp;gt; TRUE
root=&amp;gt; SELECT &amp;#39;cat&amp;#39; OR FALSE;  # =&amp;gt; &amp;#39;cat&amp;#39;
root=&amp;gt; SELECT &amp;#39;cat&amp;#39; OR &amp;#39;dog&amp;#39;;
ERROR:  OR requires boolean operands
root=&amp;gt; SELECT 1 AND 2;
ERROR:  AND conjunct must produce boolean
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By version 1.1.18.9, ParticleDB rejected boolean expressions over non-boolean literals (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/30&quot;&gt;#30&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;More Boolean/Integer Confusion (#50)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.18.9 disallowed boolean logic on integer literals, but allowed it on columns.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a int);
root=&amp;gt; UPDATE t SET a = NOT a;
UPDATE 0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Likewise, you could do integer operations on booleans:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a boolean);
root=&amp;gt; UPDATE t SET a = ((a + a) IS NULL)
       WHERE a;
UPDATE 0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Orkes seems to have resolved this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/50&quot;&gt;#50&lt;/a&gt;) by version 1.1.18.11.rc-8.&lt;/p&gt;
&lt;h3&gt;Multiple Assignment to a Single Column (#5)&lt;/h3&gt;
&lt;p&gt;The SQL standard specifies that an &lt;code&gt;UPDATE&lt;/code&gt; statement shall not contain a column name more than once. However, ParticleDB 1.1.4 through 1.1.18.4 allowed an &lt;code&gt;UPDATE&lt;/code&gt; to assign multiple values to the same column. It seemed to pick the last value, lexically.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a boolean, b boolean);
root=&amp;gt; INSERT INTO t (a, b)
       VALUES (FALSE, FALSE);
root=&amp;gt; UPDATE t SET a = FALSE, a = TRUE;
UPDATE 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Postgres this returned an error: &lt;code&gt;multiple assignments to same column &amp;quot;a&amp;quot;&lt;/code&gt;. However, ParticleDB applied the update, producing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; select * from t;
 a | b
---+---
 t | f
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By version 1.1.18.6, multiple updates to the same column threw an error (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/5&quot;&gt;#5&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Predicates Referring to Non-Existent Columns (#10)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.17, predicates in &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; statements could refer to columns which did not exist. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a INT);
root=&amp;gt; SELECT * FROM t WHERE b = 1;
 a
---
(0 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Postgres, expressions like this would throw &lt;code&gt;ERROR: column &amp;quot;b&amp;quot; does not exist&lt;/code&gt;. By version 1.1.18.6, ParticleDB did as well (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/10&quot;&gt;#10&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Arithmetic&lt;/h2&gt;
&lt;p&gt;ParticleDB often performed arithmetic in unexpected ways. These issues occurred with a single server, single client, and no faults.&lt;/p&gt;
&lt;h3&gt;No Large Integer Literals (#14)&lt;/h3&gt;
&lt;p&gt;Postgres allows large integer literals like &lt;code&gt;9223372036854775808&lt;/code&gt; (263). However, ParticleDB 1.1.4 through 1.1.17 refused these literals, throwing an &lt;code&gt;invalid integer&lt;/code&gt; error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT 9223372036854775808;
ERROR:  invalid integer: 9223372036854775808
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In ParticleDB 1.1.18.6, one could write large integer literals (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/14&quot;&gt;#14&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;To Infinity, and Beyond (#85)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.18.9 gained an exponentiation operator &lt;code&gt;^&lt;/code&gt;, but could not apply it to &lt;code&gt;Infinity&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;SELECT &amp;#39;Infinity&amp;#39; ^ 2;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This returned &lt;code&gt;&#39;Infinity&#39;&lt;/code&gt; in Postgres, but threw &lt;code&gt;operator does not exist: ^ requires numeric operands&lt;/code&gt; in ParticleDB. By 1.1.18.11.rc-9, Orkes seems to have resolved this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/85&quot;&gt;#85&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;-1 ^ 0 = -1&lt;/code&gt; (#49)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.18.9 considered &lt;code&gt;-1 ^ 0&lt;/code&gt; to be equal to &lt;code&gt;-1&lt;/code&gt;, rather than &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; select -1 ^ 0;
 -1
----
 -1
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By version 1.1.18.11.rc-3, it correctly returned &lt;code&gt;1&lt;/code&gt; (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/49&quot;&gt;#49&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;1.0 + 1E-39 = 1.0&lt;/code&gt; (#88)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.18.6 gained support for literals using scientific notation—for example, using &lt;code&gt;3E12&lt;/code&gt; to denote 3 × 1012. However, the parser turned this notation into floating-point values, rather than precise decimals, as in Postgres. This resulted in floating-point errors:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT 1.0 + 1E-39 = 1.0;
 ?column?
----------
 t
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/88&quot;&gt;#88&lt;/a&gt;) was fixed by 1.1.18.11.rc-9.2.&lt;/p&gt;
&lt;h3&gt;What’s the Largest Number You Can Think Of? (#56)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.4 through 1.1.18.11.rc-1 silently converted large floating-point literals to infinities:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT -4.56E500;
 ?column?
-----------
 -Infinity
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By version 1.1.18.11.rc-3, ParticleDB threw an error like &lt;code&gt;&amp;quot;4.56E500&amp;quot; is out of range for type double precision&lt;/code&gt; (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/56&quot;&gt;#56&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Exponent Underflow (#61)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.9, the exponent operator checked to see if its arguments would cause an overflow, but did not check for underflow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT 200 ^ 200;
ERROR:  value out of range: overflow
root=&amp;gt; SELECT -200 ^ -200;
 -0
----
  0
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By version 1.1.18.11.rc-3, ParticleDB threw an underflow error (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/61&quot;&gt;#61&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Division by Integer Zero (#34)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.8, you could divide floating-point values by integer &lt;code&gt;0&lt;/code&gt; and receive &lt;code&gt;Infinity&lt;/code&gt;, rather than a &lt;code&gt;division by zero&lt;/code&gt; error, as in Postgres.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT 1.0 / 0;
 1 / 0
-------
   inf
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ParticleDB 1.1.18.9 returned an error instead (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/34&quot;&gt;#34&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Modulo Operator Cannot Handle Floats (#51)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.9, the modulo operator &lt;code&gt;%&lt;/code&gt; threw an internal error when either argument was a floating-point number.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT 3.0 % 2;
ERROR:  internal error
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;MOD&lt;/code&gt; function was unaffected, and this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/51&quot;&gt;#51&lt;/a&gt;) was fixed by 1.1.18.11.rc-1.&lt;/p&gt;
&lt;h3&gt;Broken &lt;code&gt;NaN&lt;/code&gt; Arithmetic (#36)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.8, arithmetic operations on &lt;code&gt;NaN&lt;/code&gt; would throw an error:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT &amp;#39;NaN&amp;#39; - 1.0;
ERROR:  arrow compute: Invalid argument error:
Invalid arithmetic operation: Utf8 - Float64
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By 1.1.18.9, arithmetic on &lt;code&gt;NaN&lt;/code&gt; correctly returned &lt;code&gt;NaN&lt;/code&gt; (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/36&quot;&gt;#36&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Broken &lt;code&gt;NULL&lt;/code&gt; Arithmetic (#92)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.11.rc-9.2, arithmetic operations involving sufficiently detailed scientific notation literals and &lt;code&gt;NULL&lt;/code&gt; threw internal errors:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a int);
root=&amp;gt; INSERT INTO t (a) VALUES (NULL);
root=&amp;gt; SELECT * FROM t WHERE
       (t.a + 2.0000000000000000E-23) IS NULL;
ERROR:  internal error: called `Option::unwrap()`
        on a `None` value (at crates/exec/...)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This error (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/92&quot;&gt;#92&lt;/a&gt;) no longer appeared in version 1.1.18.11.rc-18.&lt;/p&gt;
&lt;h3&gt;UPDATE Can’t Do Math (#60)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.18.9 introduced a new bug which rendered it unable to do arithmetic operations on floating-point columns in an &lt;code&gt;UPDATE&lt;/code&gt; statement.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-17&quot; id=&quot;fnref-17&quot; data-footnote-ref&gt;17&lt;/a&gt;&lt;/sup&gt; For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a real);
CREATE TABLE
root=&amp;gt; INSERT INTO t (a) VALUES (1.2);
INSERT 0 1
root=&amp;gt; UPDATE t SET a = a + 1;
ERROR:  internal error
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In 1.1.18.11.rc-1, ParticleDB was (mostly) able to add numbers (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/60&quot;&gt;#60&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Same Precision For &lt;code&gt;real&lt;/code&gt; and &lt;code&gt;double precision&lt;/code&gt; (#32)&lt;/h3&gt;
&lt;p&gt;Per the SQL standard, the &lt;code&gt;double precision&lt;/code&gt; data type should have a greater precision than &lt;code&gt;real&lt;/code&gt;. In Postgres, &lt;code&gt;real&lt;/code&gt; and &lt;code&gt;double precision&lt;/code&gt; are (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.postgresql.org/docs/8.1/datatype.html&quot;&gt;usually&lt;/a&gt;) a 32-bit and 64-bit floating-point value, respectively. In ParticleDB, both appear to be implemented as 64-bit floats.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;CREATE TABLE t (a real NOT NULL);
INSERT INTO t (a) VALUES (4.134917993754883E8);
SELECT * FROM t ORDER BY a;
  ParticleDB:
    [{:a 4.134917993754883E8}]
  Postgres:
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Version 1.1.18.9 seemed to introduce a 32-bit &lt;code&gt;real&lt;/code&gt; type (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/32&quot;&gt;#32&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Big Integers (#11)&lt;/h3&gt;
&lt;p&gt;Similarly, integer fields (e.g. &lt;code&gt;smallint&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt;, …) in ParticleDB 1.1.4 through 1.1.17 allowed values much bigger than their typical ranges. It appeared as if ParticleDB internally represented all integer types as signed 64-bit integers. For example, here are four values too big to fit in a Postgres &lt;code&gt;smallint&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a smallint);
root=&amp;gt; INSERT INTO t (a) VALUES (32768);
root=&amp;gt; INSERT INTO t (a)
       VALUES (9223372036854775807);
root=&amp;gt; INSERT INTO t (a)
       VALUES (9223372036854775808);
root=&amp;gt; INSERT INTO t (a) VALUES (1.234E40);
root=&amp;gt; SELECT * FROM t;
          a
---------------------
               32768
 9223372036854775807
                   0
 9223372036854775807
(5 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Postgres &lt;code&gt;smallint&lt;/code&gt; is a 16-bit integer; each &lt;code&gt;INSERT&lt;/code&gt; would throw &lt;code&gt;ERROR: smallint out of range&lt;/code&gt;. However, ParticleDB stored each of these values with varying results. It preserved 32,768 and 263 − 1 (the largest two’s-complement signed 64-bit integer). 263 and larger integers were silently converted to &lt;code&gt;0&lt;/code&gt;. On the other hand, the large floating-point number was clipped to 263 − 1.&lt;/p&gt;
&lt;p&gt;Similar behavior occurred with &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;bigint&lt;/code&gt;. ParticleDB 1.1.18.6 rejected oversized integers (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/11&quot;&gt;#11&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Dividing Nearby Numbers (#86)&lt;/h3&gt;
&lt;p&gt;ParticleDB 1.1.18.9 through 1.1.18.11.rc-8 struggled to divide floating-point numbers, often throwing &lt;code&gt;Numeric out of range&lt;/code&gt; when asked to divide values which were well within the range of a 32-bit float. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT 81938.0 / 81937.82012987136848;
ERROR:  numeric out of range
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This value should be approximately &lt;code&gt;1.00000219&lt;/code&gt;. Orkes seems to have resolved this issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/86&quot;&gt;#86&lt;/a&gt;) in version 1.1.18.11.rc-9.&lt;/p&gt;
&lt;h3&gt;Floating-Point Errors in Integer Values (#87)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.18.11.rc-1 through 1.1.18.11.rc-8, adding small numbers (e.g. &lt;code&gt;0.1&lt;/code&gt;) to large integer columns could cause their values to change by surprising amounts. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a bigint);
root=&amp;gt; INSERT INTO t (a) VALUES
       (58950791971326900);
root=&amp;gt; UPDATE t SET a = (a + 1E-1);
root=&amp;gt; SELECT a FROM t;
         a
-------------------
 58950791971326896
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding &lt;code&gt;0.1&lt;/code&gt; to this column actually subtracted &lt;code&gt;4&lt;/code&gt;. Jepsen suspects this bug (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/87&quot;&gt;#87&lt;/a&gt;) was caused by performing these operations on floating-point values (which cannot represent all integers) then coercing the result back to the nearest integer value. Version 1.1.18.11.rc-9 seems to have fixed this.&lt;/p&gt;
&lt;h3&gt;Floats Are Not Equal to Quoted Forms (#19)&lt;/h3&gt;
&lt;p&gt;Postgres considers floating-point numbers like &lt;code&gt;0.0&lt;/code&gt; to be equal to their quoted representations—this is useful because some floating-point values, like &lt;code&gt;&#39;Infinity&#39;&lt;/code&gt;, are written with quotes. ParticleDB 1.1.4 through 1.1.18.4 thought otherwise:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;
root=&amp;gt; SELECT 0.0 = &amp;#39;0.0&amp;#39;;
 false
-------
 f
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/19&quot;&gt;#19&lt;/a&gt;) appeared resolved as of version 1.1.18.6.&lt;/p&gt;
&lt;h3&gt;Floats Are Not Equal to Integers (#63)&lt;/h3&gt;
&lt;p&gt;In ParticleDB 1.1.4 through 1.1.18.6, floats were not equal to integers.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; SELECT 0.0 = 0;
 false
-------
 f
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By version 1.1.18.8, reasonably-sized integers were equal to their floating-point counterparts (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/63&quot;&gt;#63&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Double Precision Floats Appear Equal to Almost All Integers (#80)&lt;/h3&gt;
&lt;p&gt;Version 1.1.18.9 introduced a new bug which made predicates like &lt;code&gt;WHERE x != y&lt;/code&gt; fail to match for most double precision floats in (-1.0, 1.0). It was as if all small floats were equal to all nonzero integers. If we took a table containing a few integers, and performed some simple queries that should return every row…&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;SQL&quot;&gt;&lt;span class=&quot;syntax-source syntax-sql&quot;&gt;&lt;span class=&quot;syntax-meta syntax-create syntax-sql&quot;&gt;&lt;span class=&quot;syntax-keyword syntax-other syntax-create syntax-sql&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-other syntax-sql&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;syntax-entity syntax-name syntax-function syntax-sql&quot;&gt;t&lt;/span&gt;&lt;/span&gt; (a &lt;span class=&quot;syntax-storage syntax-type syntax-sql&quot;&gt;int&lt;/span&gt;);
&lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;INSERT INTO&lt;/span&gt; t (a) &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-II syntax-sql&quot;&gt;VALUES&lt;/span&gt; (&lt;span class=&quot;syntax-keyword syntax-operator syntax-math syntax-sql&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;1&lt;/span&gt;), (&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;0&lt;/span&gt;), (&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;5&lt;/span&gt;);
&lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-star syntax-sql&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;FROM&lt;/span&gt; t &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;WHERE&lt;/span&gt; a &lt;span class=&quot;syntax-keyword syntax-operator syntax-comparison syntax-sql&quot;&gt;!=&lt;/span&gt; (&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;1&lt;/span&gt; ^ &lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;25&lt;/span&gt;);
&lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-star syntax-sql&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;FROM&lt;/span&gt; t &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;WHERE&lt;/span&gt;
a &lt;span class=&quot;syntax-keyword syntax-operator syntax-comparison syntax-sql&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;90000000000000000000000000000000000000&lt;/span&gt;;
&lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-star syntax-sql&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;FROM&lt;/span&gt; t &lt;span class=&quot;syntax-keyword syntax-other syntax-DML syntax-sql&quot;&gt;WHERE&lt;/span&gt;
a &lt;span class=&quot;syntax-keyword syntax-operator syntax-comparison syntax-sql&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-math syntax-sql&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;0&lt;/span&gt;.&lt;span class=&quot;syntax-constant syntax-numeric syntax-sql&quot;&gt;34656654456890453890613489540952456545&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All three of these queries returned only a single row, &lt;code&gt;0&lt;/code&gt;—implying that &lt;code&gt;0.90...&lt;/code&gt; was equal to &lt;code&gt;-1&lt;/code&gt; and &lt;code&gt;5&lt;/code&gt;. The only exception seemed to be &lt;code&gt;WHERE a != 0.0...&lt;/code&gt;, which returned no rows at all. This was also wrong, but somewhat less so. Version 1.1.18.11.rc-9 seems to have resolved the matter (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/80&quot;&gt;#80&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Strings&lt;/h2&gt;
&lt;p&gt;We found several problems with strings in ParticleDB, including datatypes that could hold more or fewer characters than they ought, and improper sort orders. These issues occurred with a single server, single client, and no faults.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;CHARACTER(n)&lt;/code&gt; Allows More Than &lt;code&gt;n&lt;/code&gt; Characters (#64, #52)&lt;/h3&gt;
&lt;p&gt;The SQL standard defines a variety of fixed-width string datatypes like &lt;code&gt;character(3)&lt;/code&gt;, which holds a string of exactly three characters. Unfortunately, ParticleDB 1.1.4 through 1.1.18.4 were happy to store much larger strings in those columns.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-18&quot; id=&quot;fnref-18&quot; data-footnote-ref&gt;18&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a character(3));
root=&amp;gt; INSERT INTO t
       VALUES (&amp;#39;large language model&amp;#39;);
root=&amp;gt; SELECT * FROM t;
          a
----------------------
 large language model
(1 row)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Version 1.1.18.6 rejected inserts and updates of too-long string literals (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/64&quot;&gt;#64&lt;/a&gt;), but appeared to perform these checks only at runtime; one could still say &lt;code&gt;UPDATE t SET a = &#39;far too long&#39;&lt;/code&gt; so long as it matched no rows (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/52&quot;&gt;#52&lt;/a&gt;). That issue was resolved in version 1.1.18.11.rc-3.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;CHARACTER(n)&lt;/code&gt; Allows Fewer Than &lt;code&gt;n&lt;/code&gt; Characters (#55)&lt;/h3&gt;
&lt;p&gt;While 1.1.18.6 rejected strings that were too large for a fixed-width string column, 1.1.4 through 1.1.18.9 also allowed strings that were too short. Normally, shorter strings are padded with spaces to the specified length, but in ParticleDB, short strings were returned unchanged.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;CREATE TABLE t (a character(16));
INSERT INTO t VALUES (&amp;#39;smol&amp;#39;);
SELECT * FROM t;
  -&amp;gt; {:a &amp;quot;smol&amp;quot;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should have returned &lt;code&gt;&amp;quot;smol            &amp;quot;&lt;/code&gt;, with padding spaces. ParticleDB 1.1.18.11.rc-1 seems to have resolved the issue (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/55&quot;&gt;#55&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Default Text Collation (#22)&lt;/h3&gt;
&lt;p&gt;On a platform with &lt;code&gt;LC_COLLATE = en_US.UTF-8&lt;/code&gt;, Postgres uses that collation by default. ParticleDB appeared to collate strings differently. Sorting via queries like &lt;code&gt;SELECT ... ORDER BY name&lt;/code&gt;, or comparisons like &lt;code&gt;&#39;a&#39; &amp;lt; &#39;B&#39;&lt;/code&gt;, returned different results between the two databases. Where Postgres considered &lt;code&gt;&#39;a&#39; &amp;lt; &#39;B&#39;&lt;/code&gt;, ParticleDB believed the opposite.&lt;/p&gt;
&lt;p&gt;Experiments suggest ParticleDB sorted by Unicode code point order, rather than the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.unicode.org/reports/tr10/&quot;&gt;Unicode Collation Algorithm&lt;/a&gt; (UCA) with the locale’s collation rules. It may have been compatible with &lt;code&gt;ucs_basic&lt;/code&gt;, &lt;code&gt;C&lt;/code&gt;, or &lt;code&gt;pg_c_utf8&lt;/code&gt;. ParticleDB version 1.1.18.9 seemed to choose a default collation more like UCA (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/22&quot;&gt;#22&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;Mis-Sorted Text (#42, #93)&lt;/h3&gt;
&lt;p&gt;Throughout our collaboration ParticleDB struggled to sort things. Versions 1.1.4 through 1.1.18.8 accepted an explicit &lt;code&gt;unicode&lt;/code&gt; text collation, then ignored it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (b text COLLATE unicode);
root=&amp;gt; INSERT INTO t (b)
       VALUES (&amp;#39;a&amp;#39;), (&amp;#39;b&amp;#39;), (&amp;#39;B&amp;#39;);
root=&amp;gt; SELECT * FROM t ORDER BY b;
 b
---
 B
 a
 b
(3 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Postgres’ &lt;code&gt;unicode&lt;/code&gt; collation, letters like &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;A&lt;/code&gt; sort similarly, despite being of different cases. This query would have returned &lt;code&gt;&#39;a&#39;, &#39;b&#39;, &#39;B&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;ParticleDB 1.1.18.9 sorted English uppercase and lowercase letters correctly, but only for some queries—adding a &lt;code&gt;LIMIT&lt;/code&gt; clause to a &lt;code&gt;SELECT&lt;/code&gt; statement would cause it to revert to the old sort order. Version 1.1.18.11.rc-1 resolved that, but sorted other characters, like punctuation, oddly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;root=&amp;gt; CREATE TABLE t (a text COLLATE unicode);
root=&amp;gt; INSERT INTO t VALUES
       (&amp;#39;_aa&amp;#39;), (&amp;#39;a&amp;#39;), (&amp;#39;aa&amp;#39;), (&amp;#39;aaa&amp;#39;);
root=&amp;gt; SELECT * FROM t ORDER BY a ASC;
  a
-----
 a
 _aa
 aa
 aaa
(4 rows)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Different sorting of whitespace and punctuation (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/42&quot;&gt;#42&lt;/a&gt;) seemed better in version 1.1.18.11.rc-7, but reappeared in 1.1.18.11.rc-9 through 1.1.18.11.rc-19 (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/particledbai/jepsen/issues/93&quot;&gt;#93&lt;/a&gt;). Text sorting seemed improved in 1.1.18.11.rc-21.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;There are also a handful of unrelated databases called ParticleDB, mainly used in particle physics—for example, the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://software.belle2.org/light-2409-toyger/doxygen/dir_49ab761b69e05c004232ce029feed737.html&quot;&gt;database of particles&lt;/a&gt; used in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Belle_II_experiment&quot;&gt;the Belle II physics experiment&lt;/a&gt;, or &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/MannyMoo/gaudi-script-builder/blob/master/python/GaudiScriptBuilder/ParticleDB.py&quot;&gt;this one&lt;/a&gt; at the Large Hadron Collider. &lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;1&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;The Particles feature was not available during our work; we focus on ParticleDB’s SQL interfaces. &lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;2&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;One envisions a world of meta-recursive database documentation in the style of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://raymondsmullyan.com/&quot;&gt;Raymond Smullyan&lt;/a&gt;. “One page of the documentation always tells the truth. Another tells only lies…” &lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;3&quot; aria-label=&quot;Back to reference 3&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-4&quot;&gt;
&lt;p&gt;Presumably &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Two-phase_locking&quot;&gt;Two-Phase Locking&lt;/a&gt;. &lt;a href=&quot;#fnref-4&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;4&quot; aria-label=&quot;Back to reference 4&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-5&quot;&gt;
&lt;p&gt;&lt;em&gt;With apologies to Deleuze &amp;amp; Guattari&lt;/em&gt;—Thus, the tuple without fields is opposed less to fields as such than to the organization of the fields insofar as it composes a row. &lt;em&gt;Le n-uplet sans composants&lt;/em&gt; is not a deleted tuple but a living tuple all the more alive and teeming once it has blown apart the client and its memory model. Structs commingling on the heap. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/spencertipping/writing-self-modifying-perl/blob/c25d7374dccac01413725acea4d0776badaf9233/writing-self-modifying-perl.pdf&quot;&gt;Self-modifying Perl&lt;/a&gt;. The full tuple without fields is a tuple populated by multiplicities. &lt;a href=&quot;#fnref-5&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;5&quot; aria-label=&quot;Back to reference 5&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-6&quot;&gt;
&lt;p&gt;Madred, 2369. &lt;a href=&quot;#fnref-6&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;6&quot; aria-label=&quot;Back to reference 6&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-7&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=kfPwJKz_rlo&quot;&gt;Cameron, 1986&lt;/a&gt;. &lt;a href=&quot;#fnref-7&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;7&quot; aria-label=&quot;Back to reference 7&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-8&quot;&gt;
&lt;p&gt;He explained that the Database—the Denormalized, the Data Lakehouse, the giant Monad—is a table without tuples. &lt;em&gt;Le tableau sans n-uplets&lt;/em&gt; is permeated by unformed, unstable values, by dataflows in all directions, by free JSON or nomadic singletons, by mad or transitory transactions. That, however, was not the question at hand. For there simultaneously occurs upon the database a very important, inevitable phenomenon that is beneficial in many respects and unfortunate in many others: schematization. Schemata are Layers, Belts. They consist of giving form to values, of imprisoning arrays or locking singletons into systems of record and replication, of producing upon the body of the database records large and small and organizing them into normal aggregates. Schemata are acts of capture, they are like “black holes” or occlusions striving to seize whatever comes within their reach. They operate by coding and normalization upon the database; they proceed simultaneously by code and normality. The Schemata are judgements of God; schematization in general is the entire system of the judgement of God (but the database, or the table without tuples, constantly eludes that judgement, flees and becomes deschematized, destructured, denormalized). &lt;a href=&quot;#fnref-8&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;8&quot; aria-label=&quot;Back to reference 8&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-9&quot;&gt;
&lt;p&gt;To make matters more confusing, &lt;code&gt;serializable&lt;/code&gt; in versions 1.1.4 through 1.1.18.11.rc-11.2 was (depending on which version of the binary you asked) either an alias for &lt;code&gt;table-2pl&lt;/code&gt; or possibly &lt;code&gt;occ-v3&lt;/code&gt;. In 1.1.18.11.rc-22, &lt;code&gt;serializable&lt;/code&gt; reappeared as a new Strong Serializable transaction mode, also based on Serializable Snapshot Isolation (SSI), but perhaps not the &lt;code&gt;serializable-ssi&lt;/code&gt; level present in earlier builds, which was distinct from &lt;code&gt;strict-serializable&lt;/code&gt;. &lt;a href=&quot;#fnref-9&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;9&quot; aria-label=&quot;Back to reference 9&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-10&quot;&gt;
&lt;p&gt;De Lioncourt, 1793, presaging Borges, 1941. &lt;a href=&quot;#fnref-10&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;10&quot; aria-label=&quot;Back to reference 10&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-11&quot;&gt;
&lt;p&gt;See ANSI/NISO Z39.19-2005 (R2010) “Celestial Emporium of Benevolent Knowledge” for the full taxonomy of database records: rows belonging to the DBA, tombstones, those which from afar look like JSON, et cetera, etc. &lt;a href=&quot;#fnref-11&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;11&quot; aria-label=&quot;Back to reference 11&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-12&quot;&gt;
&lt;p&gt;Jepsen encourages the consistency model research community to come up with more official terms for this. &lt;a href=&quot;#fnref-12&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;12&quot; aria-label=&quot;Back to reference 12&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-13&quot;&gt;
&lt;p&gt;Running &lt;code&gt;particledb --version&lt;/code&gt; might not return the actual version. For instance, 1.1.16 through 1.1.18.11.rc-18 all identified themselves as “spanner 1.1.16”. &lt;a href=&quot;#fnref-13&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;13&quot; aria-label=&quot;Back to reference 13&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-14&quot;&gt;
&lt;p&gt;Barthes can rejoice: not only is the author dead, but they were never alive to begin with. Nothing is true, everything is permitted. &lt;a href=&quot;#fnref-14&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;14&quot; aria-label=&quot;Back to reference 14&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-15&quot;&gt;
&lt;p&gt;MongoDB had &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.sec.gov/Archives/edgar/data/1441816/000144181618000028/mdb-013118x10k.htm&quot;&gt;962 employees in 2018&lt;/a&gt;, and Cockroach Labs had &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.cockroachlabs.com/blog/why-join-cockroach-labs/&quot;&gt;400 in 2022&lt;/a&gt;. Databases are hard! &lt;a href=&quot;#fnref-15&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;15&quot; aria-label=&quot;Back to reference 15&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-16&quot;&gt;
&lt;p&gt;Or perhaps not. Not every application requires perfect correctness. Vibrational Database Management Systems might be fine, at least some of the time. &lt;a href=&quot;#fnref-16&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;16&quot; aria-label=&quot;Back to reference 16&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-17&quot;&gt;
&lt;p&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;http://itre.cis.upenn.edu/~myl/languagelog/archives/002892.html&quot;&gt;Mattel, 1992&lt;/a&gt;. &lt;a href=&quot;#fnref-17&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;17&quot; aria-label=&quot;Back to reference 17&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-18&quot;&gt;
&lt;p&gt;Danielewski, 2000. &lt;a href=&quot;#fnref-18&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;18&quot; aria-label=&quot;Back to reference 18&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
  </entry>
</feed>
