Files
document-haness/.agents/skills/rewriting-technical-prose-naturally/examples/23625-rdb-task-queue.md
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

74 lines
4.6 KiB
Markdown

<!-- 출처: https://techblog.woowahan.com/23625/ · 우아한형제들 기술블로그 · 참고용 원문 사본 -->
<!-- 이 페이지는 자바스크립트로 본문을 그려서 전문을 받지 못했다. 아래는 받아낸 부분이다.
인용부호가 붙은 문장만 원문 그대로이고, 나머지는 원문을 줄인 것이다. 문장 표본으로 쓰지 말고
글의 골격(요구사항 목록 → 표 설계 → 트레이드오프)을 보는 데만 쓴다. -->
# 장시간 비동기 작업, Kafka 대신 RDB 기반 Task Queue로 해결하기
박민규 · Backend · 2025년 11월 25일
## 문제 상황
전자계약서 시스템에서 대용량 엑셀 파일 생성 작업은 Kafka를 통해 비동기로 처리되고 있었습니다. 초기에는 대부분의 작업이 10분 이내에 완료되었지만, 신규 엑셀 타입 추가로 인해 각 행마다 여러 외부 API를 호출해야 하면서 처리 시간이 30분 이상으로 증가했습니다.
심각한 문제는 사용자가 "같은 엑셀 파일을 여러 번 받았다"는 문의였습니다. 조사 결과, Kafka의 5분 타임아웃(`max.poll.interval.ms`)을 초과하면서 리밸런싱이 발생하여 Worker가 비동기 처리한 뒤 즉시 ACK하도록 변경했지만, 이는 작업 유실 위험을 초래했습니다.
## 근본 원인
"5분 동안 `poll()`이 호출되지 않으면 Consumer가 죽은 것으로 판단하고" 리밸런싱이 일어납니다. 장시간 작업 중 리밸런싱이 발생하면서 동일 메시지가 다른 Consumer에게 재할당되었습니다. 타임아웃을 증가시키는 임시 해결책은 실제 Worker 장애 감지를 지연시키는 부작용을 초래했습니다.
## 기존 Kafka 방식의 한계
팀은 엑셀 생성에 Kafka가 정말 필요한지 검토했습니다. Kafka는 대량 트래픽과 다중 consumer group에 유리하지만, 실제로는 트래픽이 일정하고 생산자/소비자가 모두 내부 서비스였습니다. 장시간 작업 특성상 타임아웃 문제가 지속적으로 발생했고, 타임아웃을 늘리면 실제 장애 감지가 1시간 이상 지연되는 딜레마가 생겼습니다.
## RDB 기반 Task Queue 아키텍처로 전환
새로운 구조는 다음과 같은 요구사항을 반영했습니다:
- **시간 제한 없음**: 1~2시간 걸리는 작업도 안정적 완료
- **배포 영향 없음**: 서버 배포 시 즉시 중단되어야 함
- **작업 유실 방지**: 서버 다운 시에도 작업 재처리
- **자동 재시도**: 일시적 오류 시 최대 3회 재시도
- **병렬 처리**: 여러 Worker의 분산 처리
- **중복 방지**: 동일 작업의 중복 처리 차단
### 핵심 구조
**테이블 설계**:
```sql
CREATE TABLE excel_download_request (
id BIGINT PRIMARY KEY,
status VARCHAR(20), -- PENDING, IN_PROGRESS, DONE, FAILED
last_heartbeat_at DATETIME, -- Worker 생존 신호 마지막 수신 시각
retry_count INT DEFAULT 0, -- 재시도 횟수 (최대 3회)
created_at DATETIME,
updated_at DATETIME
);
```
**작업 선점 및 처리**: Worker는 3초마다 PENDING 작업을 조회하고 Redis 분산 락으로 선점합니다. 동시에 최대 2개 작업을 병렬 처리합니다(10대 서버 × 2 = 20개의 시스템 처리 용량).
**Heartbeat 메커니즘**: 작업 중인 Worker는 1분마다 마지막 활동 시각을 갱신합니다. 2분 이상 갱신이 없으면 Fallback 스케줄러가 작업을 PENDING으로 되돌려 다른 Worker가 복구하도록 합니다. 복구 스케줄러는 ShedLock 으로 한 인스턴스만 수행합니다.
**재시도 처리**: 실패한 작업은 retryCount를 증가시킨 뒤 PENDING으로 되돌립니다. 3회 이상 실패하면 최종적으로 FAILED 상태로 처리됩니다.
## 개선 효과
**강점**:
- Kafka 메시지 플로우 제거로 디버깅 용이
- 단일 데이터 소스(RDB)로 상태 관리 단순화
- 메시지 재발행/유실 문제 원천 차단
- Worker 수평 확장이 단순함
- 단순 쿼리로 실시간 모니터링 가능
**트레이드오프**:
- 지속적인 폴링 쿼리로 DB 부하 증가(커버링 인덱스로 최적화, 분당 20~30회 정도)
- 폴링 주기(3초)만큼 처리 시작 지연 발생(엑셀 다운로드에서는 허용 가능)
## 핵심 인사이트
"작업 트랜잭션 특성에 따라 메시징 시스템 선택이 달라져야 한다"는 결론에 도달했습니다. 짧고 빠른 작업은 Kafka 같은 이벤트 스트리밍에 적합하지만, 수십 분 소요되는 복잡한 작업은 상태 관리와 재시도가 용이한 RDB 기반 Task Queue가 더 안정적입니다.