JetStream 메시지 처리 중 AckWait가 지났거나 ACK가 유실돼 같은 메시지가 다시 왔습니다. 원래 작업과 중복 반영은 어떻게 처리하나요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
JetStream의 AckWait는 처리 실행의 최대 시간이 아니라 그 시간 안에 ACK가 보이지 않으면 재전달할 수 있는 기준입니다. 워커를 중단하거나 DB 변경을 되돌리지 않으므로 처리 중인 작업과 재전달이 겹칠 수 있습니다.
ACK와 외부 효과
stream은 메시지를 보관하고 consumer는 전달 위치·미확인 상태·재전달을 관리합니다. DB 반영 후 ACK가 유실되면 DB 효과는 남은 채 같은 메시지가 다시 올 수 있습니다. 진행 ACK나 double ACK는 재전달 가능성을 줄여도 외부 효과의 원자적 exactly once를 만들지 않습니다.
이벤트 ID를 처리 기록의 고유 키로 삼고 처리 기록과 포인트 변경을 같은 DB transaction에 넣습니다. 이미 처리된 ID는 효과 없이 완료 처리하고 ACK합니다. AckWait는 p99 처리 시간·장애 감지 목표에 맞추고 긴 작업에는 진행 신호를 검토하되 연장 실패를 전제로 합니다.
미확인과 독성 메시지
미확인 수 상한은 메모리와 동시성을 제한합니다. 반복 실패는 최대 전달 횟수·운영 알림·수동 복구를 둡니다. 자동으로 원하는 DLQ에 간다고 가정하지 않습니다. 검증은 AckWait 초과, ACK 유실, NAK, 워커 중단·재시작을 주입해 재전달과 실제 DB 효과를 별도로 셉니다. 목표는 재전달 제거가 아니라 중복 효과 방지와 독성 메시지 격리입니다.
전달 시도와 논리 이벤트를 구분합니다
AckWait가 30초인데 워커 A의 DB 호출이 40초 걸린다고 하겠습니다. 30초 뒤 재전달받은 워커 B가 같은 이벤트를 처리할 수 있고, 나중에 A도 성공할 수 있습니다. 두 워커가 이벤트 ID를 먼저 조회하는 것만으로는 둘 다 미처리라고 볼 수 있으므로 고유 제약으로 처리 기록 생성과 실제 포인트 변경을 같은 DB 트랜잭션에서 경쟁시킵니다. 충돌한 쪽은 이전 처리 결과를 확인하고 중복 변경 없이 ACK합니다.
AckProgress는 긴 작업의 미확인 시간을 연장하는 신호이고, double ACK는 서버가 ACK를 받았는지 확인하는 추가 왕복입니다. 이것들은 재전달을 줄일 수 있지만 DB 커밋과 브로커의 ACK 상태를 원자적으로 묶지 않습니다. 연장 요청이나 확인 응답이 유실되는 경우에도 처리 ID가 같은 효과를 보호해야 합니다.
MaxAckPending은 소비자에 미확인된 전달 수를 제한합니다. 이미 실행 중인 작업을 죽이지 않으므로 애플리케이션 큐·워커 수·연결 풀은 별도로 제한합니다. BackOff 설정은 timeout 재전달 대기와 관계가 있고 명시적인 NAK의 지연은 사용하는 API·설정을 확인해야 합니다. MaxDeliver에 도달했을 때 자동으로 원하는 DLQ로 이동한다고 가정하지 않고 알림을 받아 원본 이벤트와 오류·시도 이력을 보존하는 격리 및 재처리 절차를 둡니다.
처리 ID의 보존 기간은 정상 AckWait보다 최대 재생 기간·수동 재처리 기간을 기준으로 정합니다. 오래된 메시지를 재생했는데 중복 기록이 이미 지워졌다면 같은 이벤트가 다시 효과를 만들 수 있습니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
- AckWait와 작업 수명을 구분한다.
- ACK 유실 뒤 DB 효과가 남는 순서를 설명한다.
- 이벤트 ID와 고유 제약으로 중복을 막는다.
- 미확인 상한과 독성 메시지 정책을 둔다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- AckWait 만료가 워커를 중단한다고 말한다.
- 진행 ACK가 exactly once라고 말한다.
- 최대 전달 뒤 자동 DLQ를 가정한다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.
원리부터 공부하고 싶다면
답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.