I created this article as part of my submission to the H0 Hackathon. #H0Hackathon
Every exchange — equities, prediction markets, ticketing — reduces to one hard problem: a correct, always-on, globally consistent ledger. Traditionally that demands three separate things before a single order is matched:
- a clearinghouse to guarantee trades,
- a distributed ledger to record them, and
- an infrastructure team for replication, failover, and reconciliation.
Parity is an experiment in collapsing all three into one primitive using Amazon Aurora DSQL — a serverless, distributed SQL database with strong consistency and optimistic concurrency. Orders match and money settles in the same transaction, with zero operational overhead.
- 🔗 Live app: https://parity-exchange.vercel.app
- 🧪 Live consistency proof: https://parity-exchange.vercel.app/api/proof
- 💻 Source: https://github.com/skypank-coder/parity-exchange
The interesting part is the design.
Design with optimistic concurrency, not against it
Aurora DSQL uses optimistic concurrency control (OCC) with snapshot isolation: a transaction reads a consistent snapshot and commits only if no one else modified its rows. That makes it excellent when transactions touch distinct rows, and poor under hot-row contention.
A naïve exchange — one shared order-book row everyone mutates — is the worst case. Parity is built around the grain instead:
- Matching is scoped per market. Each market is its own contention domain; unrelated markets never conflict.
- Every fill is a double-entry across distinct rows — debit the buyer's account, credit the seller's, update both positions, append two ledger rows. Distinct counterparties are the OCC sweet spot.
-
Conflicts retry the whole transaction on
SQLSTATE 40001.
The matching transaction
A fill moves cash from buyer to seller and a contract the other way, so cash is conserved on every trade. The retry wrapper is the backbone:
const SERIALIZATION_FAILURE = "40001";
export async function withTransaction<T>(fn: (c: PoolClient) => Promise<T>): Promise<T> {
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
const client = await getPool().connect();
try {
await client.query("BEGIN");
const result = await fn(client);
await client.query("COMMIT");
return result;
} catch (err) {
await client.query("ROLLBACK").catch(() => {});
if ((err as { code?: string }).code !== SERIALIZATION_FAILURE) throw err; // a real error
// OCC conflict → exponential backoff + full jitter, then retry the whole block
await sleep(Math.random() * BASE_DELAY_MS * 2 ** (attempt - 1));
} finally {
client.release();
}
}
throw new ConflictRetryExhausted();
}
Keeping the transaction body short narrows the conflict window, which keeps retries rare.
No double-spend, enforced by the database
The debit is conditional:
UPDATE accounts
SET balance = balance - $cost, updated_at = now()
WHERE user_id = $buyer
AND balance >= $cost; -- 0 rows affected ⇒ insufficient funds, fill is skipped
If it affects zero rows, the buyer can't fund the fill, so it doesn't happen. "No negative balance, no double-spend" becomes a property enforced by the database, not a convention. Under a race, two concurrent debits cannot both commit — one hits the OCC conflict and retries against the updated balance.
Settlement in one transaction
Resolving a market pays every winning contract 100¢ and losers 0¢ in one consistent transaction set — no overnight batch, no reconciliation pass. Because contracts are conserved (+qty to the buyer, −qty to the seller on every fill), total payout nets to exactly zero across all accounts.
Verifiable consistency
A stress test fires 200+ concurrent, conflicting trades across markets, then checks the books on the live cluster:
=== consistency proof ===
orders fired : 216 concurrent across 3 markets
trade ledger sum : 0 (must be 0)
cash before/after : equal (conserved)
negative balances : 0 (must be 0)
PASS — strong consistency under contention, not eventual.
Because every fill is a balanced double-entry, those invariants are queryable in real time. A /api/proof endpoint computes them directly from Aurora DSQL on each request:
{
"backend": "Aurora DSQL",
"trades": 23,
"ledgerImbalance": 0,
"negativeBalances": 0,
"cashConserved": true,
"consistent": true
}
Working with DSQL's constraints
Aurora DSQL is PostgreSQL-compatible but not full Postgres. The schema is designed around it:
-
No sequences /
SERIAL→ UUID primary keys. - No foreign keys → relationships enforced in application logic.
-
Asynchronous index creation →
CREATE INDEX ASYNC. -
IAM-token authentication, no static secrets → the database password is a short-lived token minted on demand by
@aws-sdk/dsql-signer; on Vercel, OIDC federates to IAM.
DSQL's Postgres compatibility also enabled a dual-mode data layer: the identical application runs on local Postgres in development and Aurora DSQL in production with a single environment-variable change.
Takeaways
- Schema design determines whether OCC is your best case or your worst case — an exchange is a precise test of that boundary.
- Financial invariants can be enforced in the database rather than in application discipline.
- "Strong consistency under contention" stops being a claim and becomes something you can verify with a single request.
Stack
Amazon Aurora DSQL · Next.js (App Router) on Vercel · TypeScript · node-postgres · @aws-sdk/dsql-signer · React Three Fiber.
I created this article as part of my submission to the H0 Hackathon. #H0Hackathon
- Live: https://parity-exchange.vercel.app
- Consistency proof: https://parity-exchange.vercel.app/api/proof
- Source: https://github.com/skypank-coder/parity-exchange
Top comments (0)