Question presented to candidate: "Two API servers receive the same 'add 5 units of SKU G-700' message at the same moment. Today the code does SELECT to see if the row exists, then INSERT or UPDATE. Why does that break under concurrency, and how would you write it with INSERT ... ON CONFLICT so it is atomic? What exactly does PostgreSQL do when the two statements collide?"
What a strong answer should cover:
- Why check-then-act races: both sessions see "no row" in their own snapshot, both INSERT, the loser gets a unique violation (or, without a constraint, a duplicate row)
INSERT ... ON CONFLICT (key) DO UPDATE SET col = excluded.col(orDO NOTHING): one statement, decided at the unique index, so exactly one outcome per key- The conflict target must match a unique index or constraint;
excludedis the row that was proposed; reference the old value with the table name (inventory.qty + excluded.qty) - What happens on a collision in PostgreSQL READ COMMITTED: the second session waits for the first to commit, then updates the committed row (or inserts if the first rolled back); under REPEATABLE READ it fails with a serialization error instead
- Limits: one statement cannot touch the same key twice (deduplicate the batch first),
MERGEis not a race-free replacement in PostgreSQL, and the syntax differs per engine (MySQLON DUPLICATE KEY UPDATE, SQL ServerMERGEplus locking hints)
Clarifying questions expected:
- Is there already a unique constraint on the business key (here
sku), or does it have to be added first? - On conflict, should the new value replace the old one, add to it, or be ignored?
- Which isolation level does the application run at?
Code / implementation expected: Yes — an upsert with ON CONFLICT ... DO UPDATE and excluded, and an explanation (ideally a timeline) of two sessions colliding.
Schema used by the examples: