Exercise 2: Two Transactions, Two Different Rows — Possible Solution ==================================================================== THE TEST ------------------------------ store = MVCCStore(detect_conflicts=True) setup = store.begin() store.write(setup, 'row_a', 1); store.write(setup, 'row_b', 1) store.commit(setup) t1 = store.begin(); store.read(t1, 'row_a') t2 = store.begin(); store.read(t2, 'row_b') store.write(t1, 'row_a', 100) store.write(t2, 'row_b', 200) outcome = {} try: store.commit(t1); outcome['t1'] = 'committed' except WriteConflict: outcome['t1'] = 'conflict' try: store.commit(t2); outcome['t2'] = 'committed' except WriteConflict: outcome['t2'] = 'conflict' RESULT ------------------------------ two transactions writing to two DIFFERENT rows: {'t1': 'committed', 't2': 'committed'} Both transactions commit successfully. Neither one is rejected, despite starting from the same snapshot and committing in overlapping succession -- the exact shape that produced a real conflict in Finding 5, except this time each transaction's own write targets a different row. WHY THE CHECK IS PER-ROW, NOT PER-TRANSACTION ------------------------------ The conflict-detection loop checks each row individually: for row_id in self.txn_pending_writes[txn_id]: current = self._current_committed_version(row_id) if current is not None and current.created_ts > snapshot: raise WriteConflict(...) t1's own txn_pending_writes only contains 'row_a'. When t1 commits, the loop asks exactly one question: has 'row_a' specifically been committed by someone else since t1's snapshot? At the point t1 commits, nobody else has touched 'row_a' at all -- t2 only ever wrote to 'row_b' -- so the answer is no, and t1 commits cleanly. The identical reasoning applies to t2's own commit, checking only 'row_b'. WHY THIS IS THE CORRECT SCOPE FOR THE CHECK ------------------------------ Finding 5's own lost-update problem exists because two transactions computed new values for the SAME row from the SAME stale snapshot, and one silently overwrote the other's work. That specific danger requires both the row AND the staleness to line up -- two transactions racing on completely unrelated rows were never at risk of losing each other's work in the first place, since neither transaction's own write could possibly be overwritten by the other (they're not writing to the same place). A conflict check scoped at the whole-transaction or whole-database level, rather than per-row, would reject this pair of genuinely-independent updates for no real reason -- correct in principle, but far too conservative in practice. WHY THIS WORKS AS AN ANSWER ------------------------------ Directly contrasting this exercise's own outcome (no conflict) against Finding 5's own outcome (a real, detected conflict) -- with the ONLY difference between the two tests being which row each transaction targets -- isolates row identity as the specific variable the conflict check actually depends on, rather than merely asserting that the check is "per-row" without demonstrating what that means in practice.