테마
본사 레거시 유통은 거래처마다 신용 한도를 두고, 입고되지 않은 발주 금액의 합이 한도를 넘으면 발주 등록을 막는다. MD 네 명이 같은 거래처의 발주를 동시에 등록하는 일이 잦다. 지난달 한도 3,000만 원인 거래처 P007의 미입고 발주 합계가 3,412만 원이 되어 거래처가 출고를 멈췄다. 조민우는 "동시에 읽어서 생긴 일"이라며 발주 등록 프로시저를 SERIALIZABLE로 바꿨다. 이번 달에도 같은 거래처가 3,310만 원이 되었다. 에러 로그에 ORA-08177은 한 번도 없다.
CF12 발주: 734행 · 10블록 · CF12_PK (ORD_NO) · PTNR_NO · ORD_PRGS_CD(10 발주완료, 11 입고중, 12 입고완료) · 이 문제에서는 발주 금액 ORD_AMT와 인덱스 IX_CF12_PTNR (PTNR_NO, ORD_PRGS_CD)를 더했다CF12_PTNR 거래처 한도(이 문제를 위해 더한 테이블): PTNR_NO PK · CRDT_LMT 신용 한도 · 거래처 30곳ORD_PRGS_CD를 12로 바꾼다. 발주 금액을 고치는 「발주 수정」 화면도 있다.PROCEDURE REG_PO(p_ptnr VARCHAR2, p_no VARCHAR2, p_amt NUMBER) IS
v_lmt NUMBER;
v_open NUMBER;
BEGIN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- 지난달 한도 초과 뒤 추가
SELECT CRDT_LMT INTO v_lmt FROM CF12_PTNR WHERE PTNR_NO = p_ptnr;
SELECT NVL(SUM(ORD_AMT), 0) INTO v_open
FROM CF12
WHERE PTNR_NO = p_ptnr AND ORD_PRGS_CD IN ('10', '11');
IF v_open + p_amt > v_lmt THEN
RAISE_APPLICATION_ERROR(-20041, '거래처 한도를 넘습니다');
END IF;
INSERT INTO CF12 (ORD_NO, ORD_DT, PTNR_NO, ORD_PRGS_CD, ORD_AMT)
VALUES (p_no, TO_CHAR(SYSDATE, 'YYYYMMDD'), p_ptnr, '10', p_amt);
COMMIT;
END;
[시간표 — 거래처 P007 · 한도 30,000,000 · 미입고 26,200,000]
시각 MD 1 (SID 52) · 발주 3,800,000 MD 2 (SID 87) · 발주 3,100,000
14:05:11.300 SET TRANSACTION … SERIALIZABLE
14:05:11.302 SET TRANSACTION … SERIALIZABLE
14:05:11.303 SUM → 26,200,000 · + 3,800,000 = 30,000,000 ≤ 한도
14:05:11.304 SUM → 26,200,000 · + 3,100,000 = 29,300,000 ≤ 한도
14:05:11.305 INSERT 발주 2023121407 · COMMIT
14:05:11.306 INSERT 발주 2023121408 · COMMIT
→ 미입고 33,100,000 (한도 초과) · 기다림 0 · 에러 0
[V$LOCK — 14:05:11.305 (두 세션이 모두 INSERT 한 순간)]
SID TYPE 대상 LMODE REQUEST BLOCK
52 TM CF12 3 0 0
52 TX 52 의 트랜잭션 6 0 0 ← 새 행 2023121407 하나
87 TM CF12 3 0 0
87 TX 87 의 트랜잭션 6 0 0 ← 새 행 2023121408 하나
(두 세션이 같은 행을 고치지 않는다 — 서로 기다릴 일도 ORA-08177 이 날 일도 없다)
[한도 초과가 된 등록] 지난달 34,120,000 (READ COMMITTED 일 때) · 이번 달 33,100,000 (SERIALIZABLE 로 바꾼 뒤)
V$LOCK을 근거로 두 등록이 모두 한도 검사를 통과한 이유와, SERIALIZABLE이 이를 막지 못한 이유를 설명하시오.