最後の在庫を二人が予約した
한국어 원문으로 표시합니다.
목표
재고 차감·예약 id·취소를 원자적으로 묶고 초과 판매를 막습니다.
왜 중요한가
재고가 하나 남았는데 두 요청이 동시에 조회해 모두 예약에 성공했다. 이후 같은 취소 메시지가 두 번 도착하자 재고가 원래보다 늘었다. 예약의 중복 제거와 취소의 중복 제거는 서로 다른 상태 전이이며, 남은 수량 검사도 차감과 같은 트랜잭션에 있어야 한다.
단계
/root/work/idem-stock-reservation-lab/service.py에서 quantity(value)는 bool 제외 양의 int만 반환하고 그 외 ValueError입니다.
처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.
mkdir -p /root/work/idem-stock-reservation-lab
test -e /root/work/idem-stock-reservation-lab/service.py || cp /opt/fixtures/ten_labs/idem-stock-reservation-lab/service.py /root/work/idem-stock-reservation-lab/service.py
cd /root/work/idem-stock-reservation-lab
-
/root/work/idem-stock-reservation-lab/service.py에서 init_db(path)는 stock(sku TEXT PRIMARY KEY,available INTEGER NOT NULL)와 reservations(id TEXT PRIMARY KEY,sku TEXT NOT NULL,qty INTEGER NOT NULL,cancelled INTEGER NOT NULL DEFAULT 0)를 멱등 생성합니다. -
/root/work/idem-stock-reservation-lab/service.py에서 add_stock(path,sku,amount)는 bool 제외 0 이상 int를 검사하고 새 상품만 INSERT합니다. 중복 상품은 IntegrityError입니다. -
/root/work/idem-stock-reservation-lab/service.py에서 available(path,sku)는 저장된 수량을 반환하고 상품이 없으면 KeyError입니다. -
/root/work/idem-stock-reservation-lab/service.py에서 reserve(path,rid,sku,qty,fault=lambda:None)는 수량을 검증합니다. 기존 예약 id는 같은 상품/수량이면 False, 다르면 ValueError입니다. 새 예약은 상품 존재와 재고를 확인해 차감→fault→예약 삽입 후 True입니다. 부족·없는 상품은 ValueError입니다. -
/root/work/idem-stock-reservation-lab/service.py에서 cancel(path,rid)는 없는 예약·이미 취소된 예약이면 False입니다. active 예약이면 같은 트랜잭션에서 cancelled=1과 재고 반환을 수행해 True입니다. -
/root/work/idem-stock-reservation-lab/service.py에서 reservation(path,rid)는 (sku,qty,cancelled) 튜플 또는 None을 반환합니다. -
/root/work/idem-stock-reservation-lab/service.py에서 compete(path,sku)는 reserve를 서로 다른 id first/second, 수량 1로 두 스레드에서 실행합니다. ValueError만 False로 바꾼 입력 순서 결과 리스트를 반환합니다.
참고
- 인터넷과 패키지 설치 없이 기존 lab-dev 환경에서 수행합니다.
- 각 단계는 45초 채점 예산 안에서 실행됩니다. 실제 sleep이나 네트워크 호출을 추가하지 마세요.
- 채점은 제출 모듈을 새로 불러오고 독립 입력과 임시 DB로 검사합니다. 예상값을 상수로 반환하는 대신 계약을 구현하세요.
- FastAPI 공식 문서 · pytest 공식 문서 · Python sqlite3
- 한계: 결제 승인과 재고 예약을 하나의 분산 트랜잭션으로 묶는 실습은 아니다. 예약 만료와 결제 실패의 보상은 별도 흐름이다. 조회 시점의 재고 숫자를 믿지 않고 쓰는 트랜잭션 안에서 확인하는 원리를 작은 SQLite DB로 배운다.
수량을 정수 계약으로 고정한다
/root/work/idem-stock-reservation-lab/service.py에서 quantity(value)는 bool 제외 양의 int만 반환하고 그 외 ValueError입니다.
처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.
mkdir -p /root/work/idem-stock-reservation-lab
test -e /root/work/idem-stock-reservation-lab/service.py || cp /opt/fixtures/ten_labs/idem-stock-reservation-lab/service.py /root/work/idem-stock-reservation-lab/service.py
cd /root/work/idem-stock-reservation-lab
음수 예약이 재고를 늘리지 못하게 합니다.
저장 후 bash /opt/lab/checks/idem-stock-reservation-lab/01-contract.sh로 확인하세요.
재고와 예약을 나누어 저장한다
/root/work/idem-stock-reservation-lab/service.py에서 init_db(path)는 stock(sku TEXT PRIMARY KEY,available INTEGER NOT NULL)와 reservations(id TEXT PRIMARY KEY,sku TEXT NOT NULL,qty INTEGER NOT NULL,cancelled INTEGER NOT NULL DEFAULT 0)를 멱등 생성합니다.
취소된 예약도 남겨야 같은 취소와 재예약을 구별할 수 있습니다.
저장 후 bash /opt/lab/checks/idem-stock-reservation-lab/02-contract.sh로 확인하세요.
상품 초기화를 재고 변경과 구별한다
/root/work/idem-stock-reservation-lab/service.py에서 add_stock(path,sku,amount)는 bool 제외 0 이상 int를 검사하고 새 상품만 INSERT합니다. 중복 상품은 IntegrityError입니다.
같은 초기화 명령이 사용 중인 재고를 덮지 않게 합니다.
저장 후 bash /opt/lab/checks/idem-stock-reservation-lab/03-contract.sh로 확인하세요.
남은 수량을 조회한다
/root/work/idem-stock-reservation-lab/service.py에서 available(path,sku)는 저장된 수량을 반환하고 상품이 없으면 KeyError입니다.
없는 상품을 품절과 구별해 잘못된 상품 id를 숨기지 않습니다.
저장 후 bash /opt/lab/checks/idem-stock-reservation-lab/04-contract.sh로 확인하세요.
예약과 재고 차감을 묶는다
/root/work/idem-stock-reservation-lab/service.py에서 reserve(path,rid,sku,qty,fault=lambda:None)는 수량을 검증합니다. 기존 예약 id는 같은 상품/수량이면 False, 다르면 ValueError입니다. 새 예약은 상품 존재와 재고를 확인해 차감→fault→예약 삽입 후 True입니다. 부족·없는 상품은 ValueError입니다.
검사와 차감을 같은 트랜잭션에 두고 장애 때 모두 롤백합니다.
저장 후 bash /opt/lab/checks/idem-stock-reservation-lab/05-contract.sh로 확인하세요.
취소도 한 번만 반영한다
/root/work/idem-stock-reservation-lab/service.py에서 cancel(path,rid)는 없는 예약·이미 취소된 예약이면 False입니다. active 예약이면 같은 트랜잭션에서 cancelled=1과 재고 반환을 수행해 True입니다.
취소 메시지 재전송으로 재고가 계속 증가하지 않아야 합니다.
저장 후 bash /opt/lab/checks/idem-stock-reservation-lab/06-contract.sh로 확인하세요.
예약 상태를 확인한다
/root/work/idem-stock-reservation-lab/service.py에서 reservation(path,rid)는 (sku,qty,cancelled) 튜플 또는 None을 반환합니다.
반환값뿐 아니라 취소 상태가 DB에 남았는지 읽습니다.
저장 후 bash /opt/lab/checks/idem-stock-reservation-lab/07-contract.sh로 확인하세요.
마지막 재고 경쟁을 재현한다
/root/work/idem-stock-reservation-lab/service.py에서 compete(path,sku)는 reserve를 서로 다른 id first/second, 수량 1로 두 스레드에서 실행합니다. ValueError만 False로 바꾼 입력 순서 결과 리스트를 반환합니다.
재고 1과 재고 2인 두 상황을 비교해야 무조건 하나만 성공시키는 구현도 거를 수 있습니다.
저장 후 bash /opt/lab/checks/idem-stock-reservation-lab/08-contract.sh로 확인하세요.