Files
clean-architecture-backend-…/docs/messaging/operations.md
T
DongHyeonka d646c2f12f feat(messaging): 브로커 중립 메시징 플랫폼 24개 leaf 추가
messaging-superpowers-package 설계서/계획서 기반 구현.
registry를 19 → 43 leaf로 확장하고 src/messaging 아래 24개 leaf를 등록.

- core-api: M1 publish/consume + M2 batch·delayed·pause-resume
- policy/transport-spi: 재시도 결정, DLQ orchestration, admission control, lifecycle
- kafka·rabbit(Stable): contiguous commit, confirm/return 상관, 배치, 보안 설정
- pulsar·nats(Experimental): 기본 비활성, live 인증 없음을 코드로 기록
- outbox/inbox/claim-check: 트랜잭션 결합, lease, 무결성 검증
- admin: plan → approve → execute를 타입으로 강제
- 문서 9종, infra compose 7종, JMH 벤치마크 3종

검증: 아키텍처 게이트 3종 통과, 24개 leaf 전부 check 통과,
messaging 테스트 604개 통과/0 실패.

미완: 계획서가 요구한 실 브로커 IT 40개 중 7개만 작성.
Rabbit 13 / Outbox 6 / Inbox 4 / NATS·Pulsar·Share 5 / testkit 2 /
starter·admin 3, 그리고 TLS·ACL 2개가 남음.
2026-08-14 14:55:38 +09:00

4.0 KiB

운영 Runbook

배포 전 체크

./gradlew verifyCleanArchitectureDependencies --console=plain
./gradlew verifyRuntimeModuleMembership --console=plain
./gradlew verifyOneTypePerFile --console=plain

destination profile은 startup에서 검증된다. 아래는 부팅 실패다.

  • ordered destination + reorder 가능 retry
  • ordering=KEY + key resolver 없음
  • payload 상한 > 8,388,608 bytes
  • DLQ 자기 참조 / retry 자기 참조
  • retry·DLQ 그래프 cycle
  • 미등록 retry·DLQ destination
  • M1 destination + manual settlement
  • AT_LEAST_ONCE + confirmation NONE
  • production profile + topology auto-create
  • broker topology가 manifest와 불일치

증상별 대응

publish가 AMBIGUOUS로 쏟아진다

broker confirm 경로 문제다. 실패가 아니다.

  1. PublishEvidence.transmissionMAY_HAVE_BEEN_TRANSMITTED인지 확인
  2. Kafka: delivery.timeout.ms, ISR 상태, leader election 확인
  3. Rabbit: confirm timeout, channel 상태 확인
  4. Outbox를 쓰고 있다면 status='AMBIGUOUS' row가 같은 messageId로 재시도 중이다. 정상이다.
  5. consumer 쪽 Inbox가 중복을 흡수하는지 확인

AMBIGUOUS를 실패로 취급해 새 messageId로 재발행하지 말 것. 중복이 복구 불가능해진다.

DLQ가 비어 있는데 메시지가 사라졌다

DLQ publish 실패 시 source는 settlement되지 않는다. 메시지는 source에 남아 재전달된다.

  1. msg.failure-codeDEAD_LETTER_*인 로그 확인
  2. DLQ destination이 실제로 존재하는지 (topology validation)
  3. DLQ credential에 publish 권한이 있는지

consumer lag이 한 partition에서만 증가한다

ContiguousPartitionOffsetTracker가 gap에서 멈춘 것이다. 설계된 동작이다.

commit은 연속 완료 offset까지만 전진한다. offset 11이 아직 실행 중이면 10과 12가 끝나도 watermark는 10에 머문다. 12를 commit하면 consumer가 죽었을 때 11을 잃는다.

  1. 해당 partition의 in-flight를 확인
  2. 느린 handler를 찾는다 (handlerTimeout 초과 여부)
  3. 필요하면 PAUSE_PARTITION retry가 걸려 있는지 확인

재시도 폭풍

RetryPolicy.jitter=false인지 확인한다. jitter 없이는 같은 초에 실패한 모든 consumer가 같은 초에 재시도한다.

shutdown이 오래 걸린다

GracefulShutdownCoordinator가 in-flight를 기다리는 중이다.

  • inFlight()가 0이 되면 즉시 종료
  • drain deadline(기본 30초) 초과 시 남은 작업을 unsettled로 포기한다 → broker가 재전달
  • draining 시작 후 새 retry attempt는 만들지 않는다

Destructive 작업

전부 DestructiveOperationGuard를 통과해야 한다.

조건 요구
admin credential application runtime은 보유하지 않음
AdminApproval 유효기간 내
dry-run 항상 허용

Replay

기본: 격리된 consumer group (replay-<requestId>)
기존 group 대상: 승인 티켓 필수

기존 production group으로 replay하는 것은 "다시 읽기"가 아니라 live consumer를 되감는 것이다. 그 사이의 모든 것이 재처리된다.

Redrive

dry-run으로 후보 수 확인
→ 승인 획득
→ batch 100건 이하로 실행
→ republish CONFIRMED 인 것만 DLQ에서 settlement

redriveId로 재구동 루프를 추적한다. 같은 메시지가 반복해서 redrive되면 근본 원인이 해결되지 않은 것이다.

Offset reset

KafkaOffsetResetExecutor는 승인 predicate를 생성자 인자로 받는다. 승인 소스 없이 조립된 runtime은 물리적으로 reset을 수행할 수 없다.

Topology

production topology는 IaC가 만들고 애플리케이션은 검증만 한다.

TopologyValidationRuntime은 모든 불일치를 한 번에 보고하고 startup을 실패시킨다. partition 수가 다르면 destination이 광고하는 ordering 보장이 달라지고, min.insync.replicas가 없으면 acks=all의 의미가 달라진다.