멱등성(Idempotent)을 갖춘 멀티 프로바이더 작업 API 구축하기
요약
멀티 프로바이더 환경에서 API 타임아웃이나 재시도로 인해 발생하는 중복 작업 문제를 방지하기 위한 멱등성(Idempotency) 설계 방법을 다룹니다. Idempotency-Key 활용과 데이터베이스의 Compare-and-Swap 패턴을 통해 시스템의 안정성을 확보하는 구체적인 가이드를 제공합니다.
핵심 포인트
- Idempotency-Key를 사용하여 동일한 의도에 대한 중복 작업을 방지해야 함
- 작업(Job)과 시도(Attempt)를 분리하여 상태 전이를 관리해야 함
- Compare-and-Swap(CAS) 패턴을 통해 데이터베이스 수준에서 경합 상태를 해결함
- UI 디바운스에 의존하지 말고 API와 DB 계층에서 근본적인 해결책을 마련해야 함
사용자가 “패치 생성(Generate patch)”을 클릭했을 때 요청이 타임아웃되고 UI에서 “다른 프로바이더 시도(Try Another Provider)”를 제안한다고 가정해 봅시다. 만약 그 버튼이 두 번째 작업을 생성한다면, 첫 번째 프로바이더가 나중에 작업을 완료할 수도 있습니다. 결과적으로 하나의 의도(intention)에 두 개의 패치가 생성되고, 어쩌면 두 개의 부작용(side effects)이 발생할 수 있습니다.
7월 25일의 공식 타임라인은 좋은 장애 사례(failure fixture)입니다. OpenAI는 초기 장애가 09:17:49 UTC에 시작되어 10:02:52에 완화 모니터링(mitigation monitoring) 단계에 진입했으며, 11:08:36에 해결되었다고 밝혔습니다. 다음 장애는 11:35:24에 시작되었습니다. 조사 과정에서 해당 장애는 오류 급증 및 완화 조치 진행 중인 것으로 식별되었으며, 공식 전체 상태는 부분적 시스템 저하(Partial System Degradation)였습니다. 기록상으로는 두 번째 장애의 근본 원인(root cause), 전 세계적 범위, 정확한 영향 범위, 또는 최종 복구에 대한 결론을 뒷받침할 수 없습니다.
해결책은 버튼 디바운스(button debounce)가 아니라 API와 데이터베이스에 있어야 합니다.
하나의 의도, 여러 번의 시도
실행되지 않은 이 TypeScript 인터페이스는 지속 가능한 작업(durable job)과 프로바이더 시도(provider attempts)를 분리합니다:
type JobState = 'accepted' | 'running' | 'needs_review' | 'completed' | 'cancelled';
type AttemptState = 'starting' | 'unknown' | 'failed' | 'succeeded' | 'superseded';
...
POST /jobs는 Idempotency-Key(멱등성 키)를 요구합니다. (owner_id, idempotency_key)에 유니크 제약 조건(unique constraint)을 설정하고, 키와 입력 다이제스트(input digest)가 모두 일치할 경우 기존 작업을 반환하십시오. 다른 입력과 함께 재사용되는 것은 거부해야 합니다.
POST /jobs/:id/attempts는 새로운 작업이 아니라 권한이 부여된 상태 전이(state transition)입니다. 검토자(reviewer)가 대체 사유(supersession reason)를 기록하지 않는 한, 이전 시도가 starting 또는 unknown 상태인 동안에는 거부해야 합니다. 워커(worker)는 외부로 보이는 커밋을 수행하기 전에 비교 및 교체(compare-and-swap)를 통해 작업 버전(job version)을 점유합니다.
UPDATE jobs
SET winning_attempt_id = :attempt, state = 'completed', version = version + 1
WHERE id = :job
...
업데이트된 행(rows)이 0개라는 것은 다른 시도(attempt)가 이미 성공했거나 상태가 변경되었음을 의미합니다. 정책이 허용한다면 제한된 진단(diagnostics)을 위해 실패한 출력값을 유지하되, 이를 절대 적용해서는 안 됩니다.
계층 간 실패 체크 (Cross-layer failure checks)
다음은 실행된 결과가 아닌 제안된 테스트 항목들입니다:
POST /jobs를 더블 클릭했을 때 하나의 작업 식별자(job identifier)가 반환되는지 확인합니다.- 입력값을 변경하여 동일한 키를 재사용했을 때 충돌(conflict)이 발생하는지 확인합니다.
- 프로바이더(provider) 제출 후 타임아웃(timeout)을 주입했을 때,
failed가 아닌unknown상태가 되는지 확인합니다. unknown상태일 때 폴백(fallback)을 요청하면review-required가 반환되는지 확인합니다.- 두 번의 시도를 역순으로 완료했을 때, 하나의 승자(winner)만 결정되는지 확인합니다.
- 브라우저를 새로고침했을 때, 메모리가 아닌 서버 상태로부터 제어권(controls)을 도출하는지 확인합니다.
권한 부여(Authorization)는 조사(inspection), 재시도(retry), 취소(cancellation), 그리고 출력 가져오기(output fetch) 시 작업 소유권을 반드시 확인해야 합니다. 영속성(Persistence)은 지연된 콜백(callbacks)을 조정(reconcile)할 수 있을 만큼 충분한 기간 동안 시도 ID(attempt IDs)를 유지해야 합니다. 롤백(Rollback)은 원장(ledger)을 삭제하지 않고 새로운 폴백 시도를 비활성화하는 것을 의미합니다.
프로바이더의 다양성은 의미론적 위험(semantic risk)을 초래합니다. 서로 다른 모델들은 의도(intent), 도구 사용(tool use), 또는 패치 동작(patch behavior)에서 차이가 있음에도 불구하고 동일한 TypeScript 형태(shape)를 충족할 수 있습니다. 자동 전환을 허용하기 전에 프로바이더별 수락 픽스처(acceptance fixtures)를 실행하십시오. 스키마 검증(schema validation)만으로는 불충분합니다.
수직 슬라이스 평가 경로 (A vertical-slice evaluation path)
해외의 MonkeyCode Try Online service는 현재 "Start free"라고 표시되어 있습니다. 해당 프로젝트의 README에는 빌드, 테스트, 프리뷰와 통합된 모델을 갖춘 관리형 서버 측 클라우드 환경(managed server-side cloud environments)에 대해 설명되어 있습니다. 이는 '무료로 시작 가능'하다고 정확히 설명될 수 있는, 일회성 수직 슬라이스(vertical-slice) 평가의 후보가 됩니다. 모델/서버 할당량(quotas), 리전(regions), 가동 시간(uptime) 또는 SLA 세부 사항은 변경될 수 있으므로 콘솔에서 확인해야 합니다.
공식 MonkeyCode 소스 저장소는 AGPL-3.0 라이선스를 사용합니다. 검토된 메인 커밋 18baaf54937a65a7d47f1f9d83dd808777aa6cea의 README에 따르면, 개발 환경(development environments), 모델(models), 작업(tasks) 및 요구 사항(requirements)에 대한 내장된 관리 기능을 제공합니다. 저는 셀프 호스팅(self-hosting) 및 탈출 옵션으로서 소스 코드를 조사한 다음, 권한이 낮은 하나의 작업(low-authority task)을 호스팅된 흐름(hosted flow)과 비교해 볼 것입니다. 오픈 소스라고 해서 프로바이더(provider) 의존성이 사라지는 것은 아니며, 저는 호스팅된 MonkeyCode의 신뢰성을 테스트하지 않았습니다.
풀스택(full-stack) 팀을 위한 관련 권장 사항은 모든 계층에서 작업 식별성(task identity)과 지속성(persistence)을 평가하는 것입니다. 단순히 인터페이스가 여러 모델을 제공한다는 이유만으로, 하나의 사용자 동작을 서로 관련 없는 작업들로 변환하는 폴백 경로(fallback path)를 채택하지 마십시오.
공개 사항: 저는 MonkeyCode 사용자로서 저의 개인적인 경험을 공유하는 것이며, 해당 프로젝트와 관련이 없습니다.
AI 지원 공개: 이 기사는 AI의 지원을 받아 초안이 작성되었으며, 인용된 1차 자료를 바탕으로 검토되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기