Resolving the Reprocessing Problem

재처리 불가 문제 해결 과정

배경 및 문제

실패 시 어디까지 처리됐는지 알 수 없는 구조

기존 대량 알림 생성 구조에서는

요청 발생 = 즉시 실행 이라는 단순한 구조를 가지고 있었고,

  • 스케줄러 트리거

  • 관리자 콘솔 액션

  • 예약작업

각 트리거는 서로 인지하지 못한 채 독립적으로 실행되었고, 알림 센터 적재와 푸시 생성이 하나의 실행 흐름으로 묶여 있었습니다.

이로 인해 작업 도중 장애가 발생하면 아래와 같은 문제가 발생하였습니다.

  • 작업이 어느 지점까지 성공했는지 추적할 수 없음

  • 재시작 시 전체 재실행 또는 일부 누락/중복을 감수

  • 운영자는 로그와 기억에 의존해 복구 판단.

즉, 작업이 실패했다는 사실은 인지할 수 있지만, 무엇을 다시 해야하는지는 시스템에서 알 수 없는 상태입니다.

이 구조에서 재처리는 기술적인 문제가 아니라, 누락을 감수할 것인지, 중복 발송을 감수할 것인지의 선택 문제가 되었습니다.

고민

단순 재시도가 아닌 안전한 재시작

처음에는 실패 시 전체 작업을 다시 실행하는 방식도 고려했습니다. 하지만 대량 알림의 특성상 아래 문제가 있습니다.

  • 전체 재실행은 이미 성공한 대상에 중복 알림을 유발

  • 작업 크기가 클수록 재실행 비용과 DB 부하 증가

  • 실패 지점이 반복되면 동일한 장애를 계속 재현

결국 필요한 것은 재시도(retry)가 아니라 중단 지점 이후부터 실행할 수 있는 재시작입니다.

이를 위해서는 시스템이 아래 사항들을 트래킹하고 있어야합니다.

  • 이 작업의 현재 상태는?

  • 어디까지 처리가 됐는가?

  • 다시 시작한다면 어디부터 처리해야하는가?

해결 방법 검토

1. 전체 재실행 (X)

가장 단순한 방법이자, 작업을 처음부터 다시 실행하는 것.

하지만 이 방식은,

  • 이미 성공한 대상에 중복 알림 발생

  • 작업 규모가 클수록 재실행 비용 급증

  • 반복 실패 가능성

문제들을 가지고 있었고, 정확성과 신뢰성을 보장하지 못합니다.

2. 메시지 큐 재시도 / DLQ 활용 (X)

메시지 큐 기반 구조에서는 실패 메시지 재시도나 DLQ를 통한 재처리가 가능합니다.

하지만 이 방식은 실패 처리에는 적절하지만, 재시작 관점에서는 적절치 않았습니다.

  • 재처리 단위가 메시지 수준

  • 하나의 대량 작업이 어디까지 진행됐는지 파악이 어려움

  • 특정 작업 전체를 기준으로 이어 재시작하기 어려움

결국 재처리를 위해서는

  • 작업 단위 상태 저장

  • 진행도(cursor) 관리)

가 필요했습니다.

3. 상태 기반 재처리 모델 (O)

핵심 설계: 상태와 진행도를 데이터로 남긴다

재처리 시스템의 책임으로 만들기 위해, 각 대량 알림 생성 요청을 하나의 Job으로 정의했습니다.

각 Job은 아래 정보를 가집니다:

  • 작업 상태

  • 처리 진행도(cursor)

이 설계를 통해 작업 실패 시에도:

  • 어디까지 성공했는지 명확히 남아있고,

  • cursor 이후부터 안전하게 재시작 가능해졌습니다.

Cursor기반 재시작

대량 알림 대상은 chunk 단위로 처리되며,

  • chunk 처리 후 cursor 업데이트

  • 장애 발생 시 마지막 cursor 이후부터 재개

이 방식으로:

  • 전체 재실행 제거

  • 부분 재처리 가능

  • chunk 단위 중복 생성 최소화

가 가능하게 됐습니다.

자동 감지: 중단된 작업을 복구 대상으로 전환

재처리는 사람이 아닌 시스템이 감지하는 상태 변화여야합니다.

이를 위해:

  • PROCESSING 상태 이지만

  • 일정 시간 이상 갱신되지 않은 Job을 stuck job으로 판단

하고,

  • 시스템에서는 복구 대상으로 인지

  • 마지막 cursor 기준 재시작

  • 새로운 job이 처음 등록됬을때는 0부터 시작

  • 기존 작업 처리 방식은 수정없이 그대로 사용 가능

하도록 설계했습니다.

재처리 로직

  • RUNNING 상태의 job이 있으면 신규 job 실행 금지

  • 오래 갱신되지 않은 RUNNING job은 stuck job으로 판단

  • 복구 대상 job은 cursor 기준으로 재시작

트레이드 오프: at-least-once 보장 선택

이 구조는 exactly-once 재처리를 목표로 하지 않았습니다.

  • 중간 실패 시 동일 chunk 재처리 가능성 존재.

    • 이때는 해당 chunk는 중복 발송될 여지가 있습니다.

  • 이론적으로 중복 가능성은 완전 배제 불가.

    • 효율성과 성능을 포기한 단건 처리시에는 가능

하지만 대량 알림의 특성상,

  • 완벽한 중복 제거보다

  • 안정적인 재시작운영 안전성

이 더 중요하다고 판단하였고, 재처리 설계는 chunk 단위 at-least-once 보장이라는 트레이드 오프를 선택한 결과입니다.

Last updated