← 이슈 기록 목록

Kafka 발행 실패로 DB에 유령 레코드가 남는 문제

KafkaSpringTransactionFrontend

01이슈 및 현상

마이그레이션 등록 시 DB에는 WAITING 레코드가 저장됐는데 Kafka 발행만 실패하면, 처리 데몬은 그 작업을 인지하지 못하는 불일치 상태가 됐습니다. 사용자는 에러만 받았지만 같은 기간을 재등록하면 이번엔 중복이라며 막혔습니다.

02원인 파악

@Transactional이 붙은 insert() 메서드가 정상 반환된 뒤, 컨트롤러에서 Kafka를 호출하는 구조였습니다. @Transactional은 메서드 반환 시점에 커밋이 확정되므로, 그 다음 단계인 Kafka 발행이 실패해도 이미 커밋된 DB를 롤백할 수 없었습니다.

03해결 방안

Kafka 발행을 insert()에서 분리해 별도 메서드(sendKafka)로 빼고, 실패 시 {kafkaRetry: true, id}를 응답으로 내려 프론트엔드가 1초 간격·최대 10회 재시도를 직접 구동하도록 설계했습니다. 최종 실패 시 DELETE로 레코드를 CANCELLED 처리하고, 페이지 이탈 시엔 beforeunload에서 navigator.sendBeacon으로 취소 요청을 보내 좀비 요청을 막았습니다.

04결과 및 성과

커밋 확정 이후 발생하는 발행 실패에도 DB-메시징 정합성이 깨지지 않게 됐습니다. 재시도 진행 상황이 사용자에게 실시간으로 노출되고, 페이지를 벗어나도 처리되지 않을 레코드가 남지 않도록 자동 취소까지 확보했습니다.