AtomicX
Financial Ledger API
An ACID-compliant double-entry ledger REST API that transfers funds with exactly-once semantics, staying correct under hundreds of concurrent threads and deadlock-free by construction.
- 1,000
- RPS at 13 ms average transfer latency
- 1,600
- RPS sustained under k6 load testing
- 27
- unit tests: concurrency, precision, idempotency
- 500+
- concurrent threads handled
Overview
AtomicX implements canonical double-entry bookkeeping behind a REST API. Every transfer is a single database transaction that writes one transaction record and exactly two ledger entries — one DEBIT, one CREDIT — with four-decimal BigDecimal precision throughout.
The problem
Money movement is the classic case where a small inconsistency compounds: a lost update quietly changes a balance, a deadlock stalls the service, and a retried request can move funds twice.
AtomicX is deliberately correctness-first. It spends a little extra latency per transfer to guarantee that the double-entry invariant — the sum of debits equals the sum of credits — always holds.
Architecture
Transfer pipeline
Request arrives → idempotency lookup → fail-fast limit check → lock accounts in UUID order → check balance from the ledger → write transaction + two ledger entries, all inside one transactional boundary.
Transaction manager
@Transactional(timeout = 10) wraps the whole path, so locks are never held indefinitely and any exception releases them through an automatic rollback.
Persistence
PostgreSQL with HikariCP (50 max connections) and Flyway-managed schema migrations. Idempotency keys and account ids are indexed for O(1) lookups.
Immutable ledger
Ledger entries are append-only: no update or delete paths exist. Balance is a SUM over the ledger, which makes the invariants observable rather than assumed.
Design decisions
Pessimistic write locks
Both participating accounts are locked with PESSIMISTIC_WRITE for the critical section. This blocks conflicting writers at the database level and gives deterministic, predictable latency instead of optimistic retry storms.
Deterministic UUID lock ordering
Locks are always acquired lower-UUID first. If every transfer follows the same order, a circular wait — and therefore a deadlock — becomes structurally impossible.
Idempotency keys
Header-supplied keys are persisted under a unique index. Duplicate requests short-circuit on an O(1) indexed lookup before any lock is taken, so a network retry can never double-spend.
Immutable append-only ledger
Balances are derived by summing append-only DEBIT/CREDIT entries. The ledger is the single source of truth, every mutation is auditable, and there is no separate balance row that can drift out of sync.
LOCK → CHECK → ACT in one transaction
Validation, locking, and the debit/credit pair all commit inside a single @Transactional(timeout = 10) boundary. Any failure rolls the whole transfer back within a ten-second guardrail.
Failure & concurrency
Two threads transferring between the same accounts
Pessimistic locks serialise access; the second thread waits and then reads a consistent, committed balance.
Opposite transfer directions at the same time
Deterministic UUID ordering means both threads request the same lock first, so neither can hold what the other needs — no deadlock.
Client retries a transfer after a timeout
The repeated idempotency key returns the cached transaction and creates no new ledger entries, so funds move once.
Failure mid-transfer
If anything throws before commit, the debit and credit entries are both rolled back, leaving the ledger untouched.
Testing & verification
- 27 unit tests grouped by concern: concurrency, transactional integrity, BigDecimal precision, idempotency, validation, limits, and retrieval.
- Concurrency tests drive dozens of parallel threads and assert the final balance is exactly correct.
- Integration tests boot the full Spring context against an ephemeral PostgreSQL container to exercise real locking.
Measured on the project's own k6 load tests (Hikari pool of 50, Tomcat pool of 500). These are results from that environment, not a universal guarantee.