Convex OCC Sharded Counter
Every Convex mutation is a serializable transaction over the whole database, so the read-modify-write that silently loses updates on Postgres at READ COMMITTED is simply correct here, with no BEGIN, no isolation level to pick, and no row lock to forget; a read that found nothing even conflicts with an insert into the range it searched, which is the unique constraint you never had to write. What you pay instead is contention: every writer to one hot document invalidates every other writer's read set, retries go quadratic in the writer count, and Convex eventually gives up with Write conflict: Optimistic concurrency control. This spreads increments across N shard rows selected by a hash of a caller token, never Math.random(), which returns a new value on every OCC re-execution and would move the retry to a different row. The rollup is a query, because a query never conflicts and re-runs reactively, while summing every shard inside a mutation would put all N back into one read set and undo the sharding entirely; a limit a mutation genuinely has to enforce gets a per-shard slice of the budget so the check reads only the row it is about to write. Also documents the read-set rule behind all of it, that .filter() has no index and scans the table, so a mutation written that way conflicts with every unrelated insert and works fine at 100 rows before throwing at 100k. Ships with a small model of Convex OCC and READ COMMITTED that runs the same handler under both engines, and an eight-property demo that runs under bun with no deployment. Pinned to [email protected].
npx shadcn@latest add @compronents/convex-occ-sharded-counterInstalls from ui.aryank.space. Needs a registry token and a one-time @compronents entry in components.json. The docs walk through both.
Both mutations read the same document, so both read sets cover it. The first commit invalidates the second, which aborts and re-runs from the latest timestamp. The total is never wrong, but exactly one writer commits per round, so under load the retries go quadratic and Convex eventually returns Write conflict: Optimistic concurrency control.