A database evaluation checklist should connect the rules your business depends on to evidence that a candidate database can preserve them. Check client compatibility, SQL behavior, concurrent transactions and durability separately. Record the exact build, settings, observed results and remaining gaps before putting authoritative records in the store.

[Jepsen's October 31 ParticleDB report](https://jepsen.io/analyses/particledb-1.1.4) makes that distinction timely. The Orkes-funded investigation covered single-node versions 1.1.4 through 1.1.18.11.rc-22 and reported 96 issues; all but ten appeared resolved in the final tested build. Jepsen attributes a later fix claim to Orkes for 1.1.19-rc.13, while [Orkes' October 30 post](https://orkes.io/blog/particledb-database-built-for-ai-agents) reports fixes as of 1.1.19. Those newer builds were outside Jepsen's published test scope.

For your evaluation, the useful question is what a result establishes for your application. The framework below is a proposed method, not a database test we performed.

## Start with a business rule

Write down something that must remain true when requests overlap or a process stops. That is your starting requirement, often called an invariant.

For a booking product, confirmed reservations must not exceed capacity. For an account ledger, a transfer must not leave only one of its two entries committed. Choose rules from your application, including what a user may see after success or failure.

State where each rule is enforced: a database constraint, a transaction, application code or an external system. A database cannot preserve a rule your implementation never expresses. This also helps you investigate a failure without immediately blaming the storage engine.

## Keep separate evidence for each layer

Use this table to plan the evaluation. Each row answers a different question; a successful result in one row leaves the others open.

| Layer | Evidence to collect | What remains unestablished |
| --- | --- | --- |
| Protocol and API compatibility | Actual driver and connection-pool operations, prepared statements, returned types and error handling | Correct SQL results or transaction guarantees |
| SQL and constraints | Representative queries and deliberately invalid writes, with expected results and constraint timing | Behavior when transactions overlap |
| Transaction concurrency | Overlapping operations checked against the documented isolation level and your business rule | Survival of acknowledged writes after failure |
| Durability and recovery | Which acknowledged commits remain after the specific failures you exercise | Power-loss or replication behavior absent from that test |
| Version and configuration | Build or image digest, driver version, schema, settings, topology and test revision | Equivalent behavior on another build or deployment |
| Unresolved verification | Untested requirements, reported fixes, retest owner and adoption consequence | A pass where evidence is still missing |

For every result, retain the starting state, operations, expected outcome and observed outcome. Keep enough logs or transaction history for another engineer to challenge your interpretation. A green dashboard is less useful when nobody can identify what the suite exercised.

## Check PostgreSQL compatibility through your application

Treat compatibility as a set of requirements. PostgreSQL's [protocol documentation](https://www.postgresql.org/docs/18/protocol.html) describes the messages exchanged between clients and servers. A connection through that protocol is only one part of what your application needs.

Exercise the driver paths you will use, then check the SQL features behind them. Include constraints, null values, numeric boundaries and error handling where they affect your rules. Run the application's actual queries, including less frequent operations such as an administrative export.

If you compare results with PostgreSQL, declare acceptable differences first. Unsorted rows need not arrive in the same order. Numeric precision, constraint timing and error precedence need requirements of their own. Classify a difference against the documented contract and your application, rather than treating every mismatch as a defect.

## Work through a capacity rule

Consider a **hypothetical workshop with 30 seats and 29 confirmed reservations**. Each reservation transaction counts confirmed bookings, inserts a new one only if capacity remains, then commits. This example is synthetic; we have not executed it against any database.

Plan an evaluation with two separate clients:

1. Start both transactions together. If both can count 29 reservations before either inserts, retain that history. Record blocking if the engine serializes access earlier.
2. Let each attempt its conditional insert and commit. Record statement results, commit acknowledgements and errors separately.
3. Read the authoritative final state through a fresh connection and check the capacity rule.
4. If a transaction fails with a serialization error, retry its entire read-check-write transaction and record the resulting decision.

Under a serializable contract, successfully committed transactions must have an effect consistent with some sequential execution. With this transaction logic, two new confirmations taking the total to 31 would not satisfy that contract. One transaction might instead fail and, on retry, observe that the workshop is full.

[PostgreSQL's isolation documentation](https://www.postgresql.org/docs/18/transaction-iso.html) explains both serializability and the need to retry whole transactions. It also documents that Repeatable Read can permit serialization anomalies. If you choose a weaker contract that allows the problematic interaction, overbooking points to a mismatch between your application and that contract; it does not by itself establish a database bug.

A client timeout needs a separate outcome in your record. Losing the connection around commit may leave the client unsure whether its reservation was stored. Reconcile that uncertainty using a stable operation identifier before repeating the business action.

For this design, record the identifier atomically with the reservation, enforce its uniqueness and reuse it on retries. A temporarily absent record alone does not prove that the original timed-out transaction rolled back. A database isolation setting alone cannot prevent your application from sending duplicate confirmation emails or charging a payment twice.

## Make durability claims name the failure

Ask what the database promises when it acknowledges a commit, using the settings you intend to deploy. Then choose tests that address that promise.

PostgreSQL's [asynchronous-commit documentation](https://www.postgresql.org/docs/18/wal-async-commit.html) provides a concrete example: that configuration can acknowledge transactions that are subsequently lost in a crash. This is a documented tradeoff, not a failure to provide its configured guarantee.

In an isolated evaluation, record acknowledged operation identifiers, trigger the chosen failure and compare the recovered records. Identify ambiguous operations separately from confirmed commits. Stopping a server process does not reproduce loss of power to storage, and restarting one node does not exercise replica failover. PostgreSQL's [reliability documentation](https://www.postgresql.org/docs/18/wal-reliability.html) explains why the storage stack matters to persistence.

Match the evidence to the deployment you are considering. If you need regional failover, a successful local restart leaves that requirement unanswered.

## Treat fixed and retested as separate states

A supplier's fix report is useful information. Keep its attribution and exact release beside the result until the required behavior has been checked again.

Record which reproduction was rerun, whether the surrounding workload also ran and whether the build or configuration changed. Keep outcomes such as **observed pass**, **observed failure**, **vendor-reported fix awaiting retest** and **untested** distinct. Passing a reproduction does not prove every related case is covered; passing a broader suite does not prove universal correctness.

Make missing evidence change the decision. Suppose a candidate passes your SQL checks but the deployment you need lacks evidence for acknowledged-write survival during failover. If that survival is mandatory, keep authoritative bookings on the existing store. You could still evaluate the candidate for a rebuildable search index, with its own requirements. Do not average the unresolved requirement into a general score and approve both uses.

## Write a decision another engineer can review

Finish with the proposed use, required guarantees, exact evaluated deployment, evidence links and unresolved requirements. Name who accepts each remaining risk and which new evidence would change the decision. Record performance results against the same configuration so speed comparisons remain relevant.

If you then approve a move, our [Deno Deploy migration article](/blog/deno-deploy-shutdown-migration-tests) covers the separate work of testing a destination and preparing cutover. The decision here comes earlier: whether this database has enough evidence for this role in your application. Start with the rule you cannot afford to break, and keep the adoption decision open until its required evidence exists.