Handling Hotspot Ledger Accounts

Same posting load, two fates: a single contended balance row vs. sharded sub-accounts with buffered posting and periodic roll-up

Handling Hotspot Ledger Accounts Same posting load, two fates: a single contended balance row vs. sharded sub-accounts with buffered posting and periodic roll-up 01 / Postings 02 / Route 03 / Sub-accounts 04 / Aggregate 05 / Balance Postings · high write rate · 01 / Postings · concurrent Postings high write rate concurrent Hot Balance Row · single UPDATE target · 02 / Route · contention Hot Balance Row single UPDATE target contention Shard Router · hash(txn) -> bucket · 02 / Route · fan-out Shard Router hash(txn) -> bucket fan-out Sub-acct 0 · buffered balance · 03 / Sub-accounts Sub-acct 0 buffered balance Sub-acct N · buffered balance · 03 / Sub-accounts Sub-acct N buffered balance Roll-up Job · periodic aggregate · 04 / Aggregate · scheduled Roll-up Job periodic aggregate scheduled Logical Balance · = sum(sub-accts) · 05 / Balance · source of truth Logical Balance = sum(sub-accts) source of truth all writes -> 1 row serialized sharded writes fan-out hash -> 0 append hash -> N append read 0 read read N read reconcile to total invariant Legend primary data policy / PII async batch data store

The problem: hot-row contention

  • • Every posting is an UPDATE of the same balance row
  • • Row locks serialize concurrent writers into a queue
  • • Throughput is capped no matter how many app servers you add

The fix: shard the balance

  • • Split one logical account into N sub-account buckets
  • • Route each posting to a bucket by hash, so writes spread out
  • • Contention drops ~N-fold; buckets accept writes in parallel

Buffered posting + roll-up

  • • Buckets hold buffered partial balances, not the truth on their own
  • • A periodic roll-up job sums the buckets
  • • Invariant: logical balance = sum of all sub-accounts