Performance
pledge is benchmarked against pgledger, a double-entry ledger written in PL/pgSQL, with pgbench. Both run in the same PostgreSQL 18 container on the same laptop, with the same accounts and the same workload. Higher is better.
Transfers per second #
One client, synchronous_commit off so the numbers show the work each ledger does, not the disk:
- pledge
- 5361
- pgledger
- 3547
- pledge
- 6841
- pgledger
- 4327
- pledge
- 4606
- pgledger
- 3141
Eight clients at once, same settings:
- pledge
- 19517
- pgledger
- 13579
- pledge
- 18005
- pgledger
- 10089
Bigger batches #
With durable commits on, larger batches pay off most: create_transfers writes a whole batch in
one statement. Transfers per second at 8 clients, median of three runs:
- pledge
- 7956
- pgledger
- 5362
- pledge
- 10633
- pgledger
- 4913
Storage #
Each transfer and its two entries take about 16% less space:
- pledge
- 627B
- pgledger
- 748B
Where the speed comes from #
- One statement per call. Locking the accounts, checking balances and writing the transfer and both entries happen in one SQL statement, prepared once per connection.
- No replanning. The write function always uses its cached plan; before, PostgreSQL replanned every call, which cost about 30% of the server time.
- 16-byte ids. Ids are stored as UUIDv7 values and shown as text like
pla_01K9...only when you read them.
Tested for correctness #
Speed only counts if the books stay straight. Besides the unit tests, three validation suites run
against a real database (just validate):
| Suite | What it does | Result |
|---|---|---|
| Bank | Concurrent transfers while connections are killed at random | 18,726 transfers, 197 killed connections, 0 violations |
| Model | Random operations checked against a simple in-memory ledger | 8,000 operations, 0 mismatches |
| Berka | Replays the PKDD’99 Czech bank dataset | 1,056,304 transfers, 904,603 running balances match the dataset exactly |
After each suite pledge.verify() passes, every currency sums to zero and every balance matches
the expected value.
Run it yourself #
just docker-bench PROFILE=quick # about a minute
just docker-bench PROFILE=cpu # the numbers above
just validate # the three validation suites
Results are written to bench/results/. See
bench/README.md for every option.