이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • batch 실패에서 retry 가능한 오류와 사람이 봐야 할 오류를 어떻게 구분하는가?
  • 재시도 횟수와 backoff는 어떤 기준으로 정해야 하는가?
  • 알림에는 어떤 정보가 있어야 운영자가 영향 범위와 재처리 범위를 판단할 수 있는가?
  • 재시도 후 최종 실패를 runbook으로 어떻게 연결하는가?

개요

재시도는 실패를 숨기는 장치가 아니다.

일시 오류를 흡수하고, 복구되지 않는 오류를 더 분명하게 드러내는 장치다.

batch 운영에서 retry만 늘리면 장애가 늦게 발견될 수 있다.

반대로 모든 실패를 즉시 알리면 알림 피로가 생긴다.

중요한 것은 오류를 분류하고, 재시도 정책과 알림 정책을 분리하는 것이다.

오류 분류

오류예시대응
Transient네트워크 timeout, 임시 5xx제한된 retry
ResourceDB pool 부족, lock waitbackoff, concurrency 조정
Data필수 값 누락, schema mismatchretry보다 격리와 조사
Logic변환 코드 버그배포 수정 후 재처리
Permission권한 만료, secret 오류즉시 알림

모든 오류를 같은 retry 정책으로 처리하면 복구가 늦어진다.

data 오류나 schema 오류는 반복 재시도해도 대부분 해결되지 않는다.

Retry 정책

RetryTemplate retryTemplate = RetryTemplate.builder()
    .maxAttempts(3)
    .exponentialBackoff(1000, 2.0, 30000)
    .retryOn(TransientDataAccessException.class)
    .retryOn(SocketTimeoutException.class)
    .build();

retry 대상 exception을 제한한다.

모든 exception을 retry하면 코드 버그와 데이터 오류까지 숨긴다.

backoff는 외부 시스템이나 DB가 회복할 시간을 주기 위해 필요하다.

Batch 실패 기록

CREATE TABLE batch_failure (
    failure_id bigserial PRIMARY KEY,
    job_run_id bigint NOT NULL,
    job_name varchar(100) NOT NULL,
    target_range varchar(100) NOT NULL,
    step_name varchar(100) NOT NULL,
    error_type varchar(50) NOT NULL,
    error_message text NOT NULL,
    retry_count integer NOT NULL,
    status varchar(20) NOT NULL,
    created_at timestamp NOT NULL
);

실패 기록은 알림보다 오래 남아야 한다.

운영자가 재처리할 때 Slack 메시지가 아니라 실패 table과 job run metadata를 기준으로 판단해야 한다.

알림 내용

좋은 알림은 운영자가 첫 5분 안에 영향 범위를 좁히게 한다.

[P2] settlementJob failed
jobRunId: 38122
targetDate: 2026-06-30
step: writeSettlementDaily
errorType: DATA
retryCount: 3/3
lastProcessedId: 882910
processedCount: 120000
failedKey: sellerId=991
impact: settlement_daily for 2026-06-30 not published
runbook: /runbooks/settlement-job

알림에 job name만 있으면 운영자는 로그를 뒤져야 한다.

target range, step, retry count, last processed key, impact, runbook이 필요하다.

Alert 기준

알림은 마지막 retry 이후에만 보내면 늦을 수 있다.

다음 기준을 함께 둔다.

  • SLA window를 넘을 가능성이 있으면 조기 알림
  • data 오류는 retry 전에 알림
  • permission 오류는 즉시 알림
  • transient 오류는 제한 retry 후 알림
  • 같은 오류가 반복되면 suppression보다 incident로 승격

retry와 alert는 별도 정책이다.

재처리 연결

최종 실패는 runbook으로 이어져야 한다.

runbook에는 다음이 있어야 한다.

  • 재실행 command 또는 admin action
  • target range
  • 실패 step
  • 재실행 전 확인할 source count
  • 재실행 후 검증 SQL
  • downstream 공개 여부

재시도 후 실패했는데 재처리 절차가 없다면 운영 가능한 batch가 아니다.

판단 축

  • retry 가능한 오류와 즉시 조사할 오류를 나눈다.
  • retry 횟수보다 SLA와 영향도를 함께 본다.
  • retry 대상 exception을 제한한다.
  • 알림에는 job run id와 target range를 포함한다.
  • 최종 실패는 runbook과 검증 SQL로 연결한다.

실패 모델

  • 모든 exception을 10번 retry해 장애 발견이 1시간 늦어진다.
  • data 오류를 계속 retry해 같은 row에서 매번 실패한다.
  • Slack 알림에 job 이름만 있어 운영자가 영향 범위를 모른다.
  • retry 후 성공했지만 일부 target row가 누락된 것을 검증하지 않는다.
  • 알림은 울리지만 누가 재실행할 권한이 있는지 모른다.

운영 지표

  • retry_attempt_count: retry 발생 수다.
  • retry_exhausted_count: 모든 retry 후 실패한 수다.
  • failure_by_error_type: 오류 유형별 실패 수다.
  • time_to_alert_seconds: 실패 발생부터 알림까지 시간이다.
  • time_to_recover_seconds: 최종 복구까지 시간이다.
  • rerun_success_count: 재처리 성공 수다.

개인 프로젝트 기준

개인 프로젝트에서는 이메일이나 Slack 알림이 없어도 실패 table과 로그는 남긴다.

최소한 job name, target date, error message, retry count, 재실행 방법을 남긴다.

수동으로 재실행하더라도 어떤 SQL로 성공을 확인할지 문서화한다.

기업 운영 기준

기업 운영에서는 severity와 on-call 기준이 필요하다.

정산, 매출, 규제 보고처럼 deadline이 있는 batch는 지연 시간이 곧 장애 등급이 된다.

알림 채널, 담당 owner, escalation, downstream 공개 차단 기준을 사전에 정한다.

실전 팁

  • retry 대상 exception을 명시적으로 whitelist한다.
  • data validation 실패는 retry보다 quarantine 또는 DLQ 성격으로 분리한다.
  • 알림 메시지에 jobRunId, target range, last processed key를 넣는다.
  • retry 성공 후에도 count와 sum 검증을 실행한다.
  • 장애 회고에는 retry 정책이 발견을 늦췄는지 확인한다.

위험 신호!

  • catch (Exception) 뒤에 무조건 retry한다.
  • retry count만 있고 error type 분류가 없다.
  • 최종 실패 알림이 로그 링크 하나뿐이다.
  • runbook이 없어 재실행 방법을 특정 개발자만 안다.
  • retry 성공을 데이터 품질 성공으로 간주한다.

확인 질문

확인 질문

  • retry 가능한 오류와 retry하면 안 되는 오류는 어떻게 구분하는가?
    • 네트워크 timeout 같은 일시 오류는 retry하고, schema mismatch나 필수 값 누락 같은 data 오류는 격리와 조사가 우선이다.
  • 좋은 batch 실패 알림에는 무엇이 들어가야 하는가?
    • jobRunId, job name, target range, step, error type, retry count, last processed key, impact, runbook이 들어가야 한다.
  • retry 후 성공했는데도 검증이 필요한 이유는 무엇인가?
    • task는 성공했어도 일부 row 누락, 중복 write, target count/sum 불일치가 남을 수 있기 때문이다.

참고 문서