Platform sharding
An account can only commit as fast as its one lock turns over, and every operation meets the same few platform accounts. PLATFORM_SHARDS splits each hot account into rows: routing is deterministic per operation, and readers sum the rows.
One cash register can only serve one customer at a time, no matter how short the line. Opening four registers lets four lines move at once — and as long as every return goes back to the register that made the sale, the day's books still add up.
Source src/accounts.ts#L151platformShardtest/sharding.test.ts
Per-account locking has a built-in ceiling: an account can only commit as fast as its one lock turns over. Users never feel it, because their wallets are all different accounts and the load spreads out.
The platform’s accounts sit on the other side of every one of those operations. Every top-up debits STORED_VALUE and every sale credits REVENUE, so a thousand concurrent buyers spread across a thousand wallets still meet at the same revenue row, and they wait there in line.
Splitting the line
PLATFORM_SHARDS breaks up that line. Set to four, say, each hot platform account becomes four rows (the bare id plus platform:revenue#1 through #3), and concurrent postings spread across them instead of queueing on one.
The seven accounts that take this traffic shard: REVENUE, STORED_VALUE, TRUST_CASH, USD_CLEARING, REVENUE_USD, PROMO_FLOAT, and PAYOUT_RESERVE. RECEIVABLE and OPENING_EQUITY move rarely and stay single rows.
A shard row behaves like any other account under the machinery on the concurrency page: it takes its own lock and extends its own hash chain. The point of the split is precisely that those are different locks and different chains.
Routing is deterministic
The routing can’t be random, because a retry has to land on the same row the first attempt locked. So a posting picks its shard by hashing the operation’s idempotency key, which is stable across retries and already known both when locks are taken and when legs are built.
PAYOUT_RESERVE is the exception, and the reason is time. Credits enter the reserve when a payout is requested and leave when it settles or reverses, and that second step is a different operation, days later, carrying a different idempotency key. Hash that key and the debit would land on some other shard, quite possibly one holding nothing, and the reserve is overdraft-guarded: no shard may go negative. The user id is the one routing key both ends of a payout know, so the reserve routes by user.
Readers sum the rows
No reader cares which shard holds what. The logical balance is the sum over the rows: the backing check totals TRUST_CASH across its shards before weighing it against what users are owed, and the fee sweep drains matured REVENUE from whichever shards hold it.
A shard also inherits its parent’s currency, class, and normal side (identity checks strip the # suffix first), so everything downstream of classify already treats the rows as one account.
Turning it on is safe
The default is 1: the unsharded ledger, byte-identical to before the knob existed. And because shard 0 keeps the bare account id, an existing ledger’s balances already live on shard 0, so turning sharding on later is safe.
This is deliberately the one scale lever in a single-database design. It relieves contention on the hot rows inside that database; cross-node partitioning of the ledger is out of scope.