8장 중복·재시도·동시성을 통제한다
분산 시스템에서 “정확히 한 번”은 어렵다. 대신 적어도 한 번 전달될 수 있다고 가정하고 행동을 멱등하게 만든다. BriefFlow는 행동 유형, 브리프 ID, 영수증 ID를 멱등성 키로 사용한다.
const key = `${action.type}:${brief.id}:${receipt.id}`;
큐 테이블에 키의 고유 제약을 두고, 외부 API가 멱등성 키를 지원하면 같은 값을 전달한다. 지원하지 않으면 전송 전후 상태를 저장하고 조회 가능한 외부 ID를 남긴다.
재시도는 모든 오류에 적용하지 않는다. 타임아웃, 429, 일시적 5xx는 지수 백오프와 jitter를 사용한다. 잘못된 이메일, 권한 없음, 스키마 오류는 재시도해도 낫지 않으므로 보류함으로 보낸다. 최대 횟수와 총 시간을 제한한다. 예제의 src/retry-policy.mjs는 이 분류와 상한 있는 지연 계산까지만 구현하며, 실제 큐 소비와 예약은 운영 확장 범위다.
const decision = classifyRetry({ status: response.status });
if (decision === 'retry') {
job.availableAt = new Date(Date.now() + retryDelayMs(job.attemptCount));
} else {
job.status = 'hold';
}
두 운영자가 동시에 승인할 수 있다. 저장 시 버전 또는 updatedAt을 비교해 먼저 읽은 낡은 화면의 승인을 거절한다. 잠금 실패 문구에는 최신 변경을 다시 보라는 행동을 제공한다.