冪等キーを消すと保証も終わる
한국어 원문으로 표시합니다.
목표
완료·진행 중 기록을 구분하고 보관 기간과 배치 정리를 구현합니다.
왜 중요한가
테이블이 커지자 오래된 멱등 키를 모두 삭제했다. 아직 결제 중인 요청의 기록도 사라졌고 재시도가 새 요청으로 들어왔다. 완료 응답의 보관 기간과 진행 중 작업의 소유권은 같은 만료 정책이 아니다. 정리는 단순 DELETE가 아니라 보장 범위를 바꾸는 상태 전이다.
단계
/root/work/idem-retention-lab/service.py에서 init_db(path)는 keys(id TEXT PRIMARY KEY,fingerprint TEXT NOT NULL,status TEXT NOT NULL,response TEXT,expires REAL NOT NULL)를 멱등 생성합니다.
처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.
mkdir -p /root/work/idem-retention-lab
test -e /root/work/idem-retention-lab/service.py || cp /opt/fixtures/ten_labs/idem-retention-lab/service.py /root/work/idem-retention-lab/service.py
cd /root/work/idem-retention-lab
-
/root/work/idem-retention-lab/service.py에서 reserve(path,key,digest,expires)는 없는 키를 pending,response=NULL로 넣고 True입니다. 기존 키는 만료와 관계없이 바꾸지 않고 False입니다. -
/root/work/idem-retention-lab/service.py에서 finish(path,key,digest,response)는 일치하는 key·fingerprint이며 pending인 행만 response를 JSON으로 저장하고 done으로 바꿔 True, 나머지는 False입니다. -
/root/work/idem-retention-lab/service.py에서 fetch(path,key,now)는 done이고 expires>now인 행의 response를 JSON 파싱해 반환합니다. 그 외 None입니다. -
/root/work/idem-retention-lab/service.py에서 expired(path,now,limit=10)는 bool 제외 int 1~100인 limit를 검사합니다. done이고 expires<=now인 id를 id 오름차순으로 최대 limit개 반환합니다. -
/root/work/idem-retention-lab/service.py에서 purge(path,now,limit=10)는 expired와 같은 limit 검증·조건·정렬로 후보를 고른 뒤 한 트랜잭션에서 삭제하고 삭제한 id 리스트를 반환합니다. pending은 지우지 않습니다. -
/root/work/idem-retention-lab/service.py에서 counts(path)는 {pending:개수, done:개수}입니다. 해당 상태가 없어도 키와 0이 있어야 합니다. -
/root/work/idem-retention-lab/service.py에서 retention_cycle(path,key)는 expires=10,digest='v1'로 예약하고 {receipt:1}로 완료합니다. now=10에서 purge 후 같은 키를 digest='v2',expires=20으로 새 예약해 그 결과 bool을 반환합니다. 함수는 빈 DB를 전제로 합니다.
참고
- 인터넷과 패키지 설치 없이 기존 lab-dev 환경에서 수행합니다.
- 각 단계는 45초 채점 예산 안에서 실행됩니다. 실제 sleep이나 네트워크 호출을 추가하지 마세요.
- 채점은 제출 모듈을 새로 불러오고 독립 입력과 임시 DB로 검사합니다. 예상값을 상수로 반환하는 대신 계약을 구현하세요.
- FastAPI 공식 문서 · pytest 공식 문서 · Python sqlite3
- 한계: 무한 기간 exactly-once 효과를 보장하지 않는다. 외부 시스템은 오래된 요청을 재전송할 수 있으므로 보관 기간은 제공자의 재시도 정책과 맞춰야 한다. abandoned pending을 복구하는 임대·보상 정책은 별도 실습의 책임이며, 이 정리 함수에서 추측해 지우지 않는다.
완료 여부와 만료를 분리한다
/root/work/idem-retention-lab/service.py에서 init_db(path)는 keys(id TEXT PRIMARY KEY,fingerprint TEXT NOT NULL,status TEXT NOT NULL,response TEXT,expires REAL NOT NULL)를 멱등 생성합니다.
처음 한 번 준비하세요. 기존 파일은 덮어쓰지 않습니다.
mkdir -p /root/work/idem-retention-lab
test -e /root/work/idem-retention-lab/service.py || cp /opt/fixtures/ten_labs/idem-retention-lab/service.py /root/work/idem-retention-lab/service.py
cd /root/work/idem-retention-lab
만료 시각만 보고 업무가 끝났다고 판단하지 않습니다.
저장 후 bash /opt/lab/checks/idem-retention-lab/01-contract.sh로 확인하세요.
진행 중 키를 예약한다
/root/work/idem-retention-lab/service.py에서 reserve(path,key,digest,expires)는 없는 키를 pending,response=NULL로 넣고 True입니다. 기존 키는 만료와 관계없이 바꾸지 않고 False입니다.
기록 삭제와 키 재사용은 분리해서 명시적으로 제어합니다.
저장 후 bash /opt/lab/checks/idem-retention-lab/02-contract.sh로 확인하세요.
현재 요청만 완료한다
/root/work/idem-retention-lab/service.py에서 finish(path,key,digest,response)는 일치하는 key·fingerprint이며 pending인 행만 response를 JSON으로 저장하고 done으로 바꿔 True, 나머지는 False입니다.
다른 본문의 작업자가 완료 응답을 덮지 못하게 지문까지 비교합니다.
저장 후 bash /opt/lab/checks/idem-retention-lab/03-contract.sh로 확인하세요.
유효한 완료 응답만 재생한다
/root/work/idem-retention-lab/service.py에서 fetch(path,key,now)는 done이고 expires>now인 행의 response를 JSON 파싱해 반환합니다. 그 외 None입니다.
now==expires 경계에서 더 이상 재생하지 않는 계약을 고정합니다.
저장 후 bash /opt/lab/checks/idem-retention-lab/04-contract.sh로 확인하세요.
정리 후보를 제한한다
/root/work/idem-retention-lab/service.py에서 expired(path,now,limit=10)는 bool 제외 int 1~100인 limit를 검사합니다. done이고 expires<=now인 id를 id 오름차순으로 최대 limit개 반환합니다.
전체 테이블을 한 번에 지우면 잠금 시간이 길어지고 진행 중 행을 섞기 쉽습니다.
저장 후 bash /opt/lab/checks/idem-retention-lab/05-contract.sh로 확인하세요.
선택과 삭제를 같은 트랜잭션에 둔다
/root/work/idem-retention-lab/service.py에서 purge(path,now,limit=10)는 expired와 같은 limit 검증·조건·정렬로 후보를 고른 뒤 한 트랜잭션에서 삭제하고 삭제한 id 리스트를 반환합니다. pending은 지우지 않습니다.
후보 조회와 삭제 사이에 상태가 바뀌는 틈을 만들지 않습니다.
저장 후 bash /opt/lab/checks/idem-retention-lab/06-contract.sh로 확인하세요.
상태별 개수를 실제로 센다
/root/work/idem-retention-lab/service.py에서 counts(path)는 {pending:개수, done:개수}입니다. 해당 상태가 없어도 키와 0이 있어야 합니다.
정리 뒤 미완료 행이 사라지지 않았는지 관측할 수 있어야 합니다.
저장 후 bash /opt/lab/checks/idem-retention-lab/07-contract.sh로 확인하세요.
삭제 뒤 보장 범위가 끝남을 확인한다
/root/work/idem-retention-lab/service.py에서 retention_cycle(path,key)는 expires=10,digest='v1'로 예약하고 {receipt:1}로 완료합니다. now=10에서 purge 후 같은 키를 digest='v2',expires=20으로 새 예약해 그 결과 bool을 반환합니다. 함수는 빈 DB를 전제로 합니다.
정리 후 키가 새 요청이 된다는 한계를 숨기지 않고 직접 재현합니다.
저장 후 bash /opt/lab/checks/idem-retention-lab/08-contract.sh로 확인하세요.