LabHub
배우기 러닝패스 코스

Idempotency — Two Clicks, One Charge

Deleting an idempotency key ends its guarantee

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

완료·진행 중 기록을 구분하고 보관 기간과 배치 정리를 구현합니다.

왜 중요한가

테이블이 커지자 오래된 멱등 키를 모두 삭제했다. 아직 결제 중인 요청의 기록도 사라졌고 재시도가 새 요청으로 들어왔다. 완료 응답의 보관 기간과 진행 중 작업의 소유권은 같은 만료 정책이 아니다. 정리는 단순 DELETE가 아니라 보장 범위를 바꾸는 상태 전이다.

단계

  1. /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
  1. /root/work/idem-retention-lab/service.py에서 reserve(path,key,digest,expires)는 없는 키를 pending,response=NULL로 넣고 True입니다. 기존 키는 만료와 관계없이 바꾸지 않고 False입니다.

  2. /root/work/idem-retention-lab/service.py에서 finish(path,key,digest,response)는 일치하는 key·fingerprint이며 pending인 행만 response를 JSON으로 저장하고 done으로 바꿔 True, 나머지는 False입니다.

  3. /root/work/idem-retention-lab/service.py에서 fetch(path,key,now)는 done이고 expires>now인 행의 response를 JSON 파싱해 반환합니다. 그 외 None입니다.

  4. /root/work/idem-retention-lab/service.py에서 expired(path,now,limit=10)는 bool 제외 int 1~100인 limit를 검사합니다. done이고 expires<=now인 id를 id 오름차순으로 최대 limit개 반환합니다.

  5. /root/work/idem-retention-lab/service.py에서 purge(path,now,limit=10)는 expired와 같은 limit 검증·조건·정렬로 후보를 고른 뒤 한 트랜잭션에서 삭제하고 삭제한 id 리스트를 반환합니다. pending은 지우지 않습니다.

  6. /root/work/idem-retention-lab/service.py에서 counts(path)는 {pending:개수, done:개수}입니다. 해당 상태가 없어도 키와 0이 있어야 합니다.

  7. /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를 전제로 합니다.

참고

완료 여부와 만료를 분리한다

/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로 확인하세요.