init: 클린 아키텍처 백엔드
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Runbook — background job dead letter
|
||||
category: INTERNAL
|
||||
error_codes: [JOB_DEAD_LETTER]
|
||||
severity: P1
|
||||
owner: oncall
|
||||
last_updated: 2026-06-13
|
||||
status: stub
|
||||
---
|
||||
|
||||
# Runbook: background job dead letter (`runbook://job/dead-letter`)
|
||||
|
||||
## 1. Trigger
|
||||
|
||||
이 runbook은 다음 alert에서 발동됩니다.
|
||||
|
||||
- alert name: `job_dead_letter`
|
||||
- alert payload 필수 field: `error.code=JOB_DEAD_LETTER`, `job_name`, `correlation_id`, `runbook_link`
|
||||
- 임계: `job.dlq.total` > 0 for 5m (p1) — retry 소진 후 DLQ 진입은 자동 회복이 없으므로 점검 대상
|
||||
|
||||
## 2. First Response (5분 이내)
|
||||
|
||||
### Step 1 — 확인
|
||||
1. ERROR log에서 `JOB_DEAD_LETTER` 라인 확인: `job_name`, 최종 실패 원인 예외, `correlation_id` 추출
|
||||
2. `job.retry.total{outcome=EXHAUSTED}` 추이로 DLQ 유입 규모 파악
|
||||
3. DLQ 적재 위치(향후 retry carrier 확정 시 DB 테이블/큐) 확인 — 현재 skeleton은 vocabulary 단계
|
||||
|
||||
### Step 2 — 임시 격리
|
||||
- DLQ는 max attempts(3) 소진의 최종 상태 — 자동 재시도 없음, 수동 개입 필수
|
||||
- 비즈니스 크리티컬 job이면 §4의 수동 처분(재처리 또는 폐기)을 우선 수행
|
||||
|
||||
## 3. Diagnosis
|
||||
|
||||
- log query: `{service="app"} | error.code="JOB_DEAD_LETTER" | stats count by job_name`
|
||||
- metric panel: `job.dlq.total{job_name}`, `job.retry.total{job_name, outcome}`
|
||||
- 최종 실패 원인 분류:
|
||||
- poison input(직렬화/계약 위반) → 입력 결함, 재처리해도 실패 — 수정 후 재처리 또는 폐기
|
||||
- 외부 의존성 장기 outage 중 attempts 소진 → 의존성 회복 후 재처리로 해결 가능
|
||||
- non-transient error(권한/도메인/스키마)인데 retry된 경우 → 분류기 보강 필요(WAF-REL05-C3: 즉시 DLQ가 정답)
|
||||
|
||||
## 4. Mitigation (수동 처분 — 둘 중 하나)
|
||||
|
||||
- **재처리 (기본)**: 원인 해소 후 해당 job을 다시 enqueue. 소비자는 멱등(idempotencyKey dedupe) 의무가 있으므로 중복 처리 안전
|
||||
- **폐기 (영구)**: 작업이 더 이상 유효하지 않으면 DLQ에서 제거. ⚠ 비즈니스 오너 승인 후에만 수행하고 incident 기록에 남김
|
||||
- 장기: poison input 재발 방지(입력 계약 테스트 보강), non-transient error는 retry 없이 즉시 DLQ로 분류
|
||||
|
||||
## 5. Escalation
|
||||
|
||||
- 처분 판단(재처리 vs 폐기)이 불가하면 해당 job의 비즈니스 오너에게 escalate
|
||||
- DLQ 누적이 특정 `job_name`에 집중되면 해당 job 코드 오너에게 page
|
||||
|
||||
## 6. Recovery / Verification
|
||||
|
||||
- 회복 확인: `job.dlq.total` 증가 멈춤, 재처리분의 소비자 dedupe 동작 확인
|
||||
- post-incident: DLQ 원인 분류 기록, 같은 원인의 재발 방지 테스트 추가
|
||||
|
||||
## 7. Related
|
||||
|
||||
- error-codes.yaml rows: `JOB_DEAD_LETTER` (INTERNAL, 500, retryable=false)
|
||||
- metrics.yaml: `job.dlq.total{job_name}`, `job.retry.total{job_name, outcome=DLQ}`
|
||||
- 코드: `app-bootstrap` `async/BackgroundJobMetrics`(retry/DLQ vocabulary 기록 seam — D2/D4)
|
||||
- 관련 runbook: [[job-executor-rejected]], [[job-timeout]], [[outbox-dead-letter]]
|
||||
- 관련 branch: [[feature-background-job-async-contract]] (D4 retry/DLQ vocabulary SSOT — outbox/outbound가 consume)
|
||||
|
||||
---
|
||||
|
||||
> **Stub 상태 안내**: 이 runbook은 skeleton 단계의 stub. retry carrier(Spring Retry / Resilience4j / 자체) 확정 후 DLQ 저장소·재처리 절차 보강 필요.
|
||||
Reference in New Issue
Block a user