고전적 race
"재고 read; > 0 면 감소 + 판매 확인." 두 요청이 병렬 실행. 둘 다 재고 = 1 read. 둘 다 안전하다 결정. 둘 다 감소. 재고 이제 -1, 없는 거 팔았음. 버그가 어떤 단일 line 에 없음 — read 와 write 사이 갭에 있음.
때에 따라 적절한 세 수정
- Atomic UPDATE — check 와 감소를 한 statement 에:
UPDATE products SET inventory = inventory - 1 WHERE id = ? AND inventory > 0. 0 행 update 됐으면 판매 실패. - SELECT FOR UPDATE — read 시 행 lock; 앱 코드에서 조건 로직; UPDATE; COMMIT.
- SERIALIZABLE isolation — PostgreSQL 이 충돌 감지 + retry 요청.
가장 흔한 거
Atomic UPDATE 가 보통 가장 깨끗한 답. 라운드트립 한 번, 명시 lock 없음, retry 없음. 조건 로직이 단일 SQL statement 에 fit 하기 너무 복잡할 때 SELECT FOR UPDATE.