본문 바로가기
C.W.K.
Stream
Lesson 09 of 10 · published

앱 코드의 경쟁 상태

~12 min · transactions, concurrency

Level 0스키마 새싹
0 XP0/86 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

확인과 변경 사이의 틈

재고를 읽어 0보다 크면 줄이는 코드를 두 요청이 동시에 실행한다고 해보자. 둘 다 재고 1을 읽고 성공을 결정하면 최종 값이 -1이 될 수 있어. 버그는 어느 한 줄이 아니라 읽기와 쓰기 사이에 있어.

조건과 변경을 한 문장에

UPDATE products SET inventory = inventory - 1 WHERE id = ? AND inventory > 0는 재고 확인과 차감을 원자적으로 처리해. 갱신 행이 0개면 판매하지 않고, 1개면 성공이야.

복잡한 경우의 두 선택

조건이 SQL 한 문장에 들어가지 않으면 SELECT FOR UPDATE로 잠근 뒤 계산하거나 SERIALIZABLE에서 재시도해. 가능하면 UPDATE ... WHERE id = ? AND condition처럼 조건을 WHERE에 밀어 넣는 방식이 가장 단순해.

Code

재고를 원자적으로 갱신하기·sql
UPDATE products
SET inventory = inventory - 1
WHERE id = :product_id AND inventory > 0
RETURNING inventory;
-- RETURNING 이 행 반환: 판매 성공.
-- 행 반환 없음: 매진, UPDATE 가 아무것도 매치 안 함.
복잡한 판단에는 SELECT FOR UPDATE 쓰기·sql
BEGIN;
SELECT inventory, max_per_customer, customer_purchase_count(?, ?)
FROM   products
WHERE  id = ?
FOR UPDATE;
-- ... 앱에서 복잡한 비즈니스 로직 ...
UPDATE products SET inventory = inventory - 1 WHERE id = ?;
COMMIT;
SERIALIZABLE로 충돌 판단을 맡기기·python
def safe_purchase(c, product_id, customer_id):
    for attempt in range(5):
        try:
            with c.transaction(isolation_level="serializable"):
                inv = c.execute("SELECT inventory FROM products WHERE id = %s", (product_id,)).fetchone()[0]
                if inv > 0:
                    c.execute("UPDATE products SET inventory = inventory - 1 WHERE id = %s", (product_id,))
            return True
        except psycopg.errors.SerializationFailure:
            continue
    return False

External links

Exercise

코드에서 경쟁 조건이 생길 수 있는 읽기 후 쓰기 패턴을 찾아 검사 조건을 WHERE에 넣은 원자적 UPDATE로 바꿔. 클라이언트 2개에서 동시에 실행해 결과가 안전한지 확인해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.