One copy, three bills
A change becomes 1 + N writes, where N is the fan-out. Readers can see two answers in between. And drifted copies need repair code.
Joins are slow, so copy?
Bad: Copy the reviewer’s name
Adds a rewrite path and stale names for no gain.
Good: Try an index first
Most slow joins lack an index. With one, the join is cheap.
Climb only as far as you must
- Generated column: exact, one row
- Same transaction or trigger: exact at commit
- Materialized view: as fresh as its refresh
- CDC or outbox: seconds behind
Save, then publish: the classic bug
A deploy kills the process between the two calls. The database says Dosa Point; search says Dosa Corner, forever. Nothing remembers to finish.
The fix puts the event in the same transaction
The rename and an outbox row commit together, or neither does. CDC relays it at least once, so consumers must be idempotent and ordered per key.
≈ 500/s
ceiling for increments on one row
Assume each increment holds the row lock about 2 ms. A one-rupee biryani offer at 3,000 orders a second queues. Sixteen striped slots: ≈ 8,000/s.
Every copy drifts eventually: a bug, a skipped code path, a failed backfill.
The plan
Schedule a drift check and keep a rebuild path. A copy you can’t rebuild is a second source of truth.
Remember
Start normalized. Copy only for a measured read, and name the sync path, drift check and rebuild for every copy.