You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Burst 부하 구간에서 AbortPolicy 이후 드러난 rejected 건을 유실로 두지 않기 위한 저장 및 재처리 구조 구성.
문제 상황
AbortPolicy 적용 이후 user-log request path는 보호됐지만 admin-log-feature executor 포화 시 enqueue reject가 지속적으로 발생
reject 건은 metric으로만 확인 가능했고, 어떤 이벤트가 실패했는지 추적하거나 재처리할 수 있는 저장 구조 부재
결과적으로 admin dispatch 실패가 최종 feature 반영 유실로 바로 이어지는 구조 유지
해결 과정
단계
내용
의도
실패건 영속화
admin dispatch 대상을 outbox row로 저장
reject와 HTTP 실패를 복구 가능한 상태로 전환
상태 관리
READY / PROCESSING / ACKED / RETRY / DEAD 정의
재처리 흐름과 종료 상태 명확화
재전달 분리
scheduler가 READY, RETRY를 batch claim 후 재전달
request path와 retry path 분리
결과 반영
admin HTTP 결과에 따라 ACKED, RETRY, DEAD 갱신
성공/실패 후속 처리 일관화
계측 보강
dispatch, outbox lifecycle metric 추가
저장, 재시도, 종료 상태 관측 가능화
구분
내용
목적
실패건 저장
user_log_admin_dispatch_outbox 도입
reject 및 dispatch 실패건 영속화
상태 전이
READY / PROCESSING / ACKED / RETRY / DEAD 구성
재처리 대상 구분
재전달 경로
scheduler 기반 batch dispatch
request path와 retry path 분리
결과 판별
admin HTTP 결과 객체 반환
ACK / RETRY 결정 기준 명확화
계측 보강
dispatch / outbox metric 추가
enqueue, 저장, 재시도, 종료 상태 관측
설정 추가
batch size, retry delay, max attempts 외부화
운영 환경별 조정 여지 확보
구조도
flowchart LR
A["UserLogService"] --> B["Kafka publish"]
A --> C["Outbox 저장"]
D["UserLogAdminDispatchScheduler"] --> E["READY / RETRY 조회"]
E --> F["AdminLogFeatureDispatchService"]
F --> G["AdminLogFeaturesClient"]
G --> H{"dispatch 결과"}
H -->|success| I["ACKED"]
H -->|failure| J["RETRY / DEAD"]
style C fill:#eef8ef,stroke:#5a9c67
style D fill:#eef4ff,stroke:#5b80b7
style I fill:#eef8ef,stroke:#5a9c67
style J fill:#fff6ea,stroke:#c58f48
The reason will be displayed to describe this comment to others. Learn more.
Code Review
이 PR은 Admin 로그 발송에 아웃박스 패턴을 도입하여 시스템의 안정성과 재시도 메커니즘을 강화했습니다. 주요 변경 사항으로 아웃박스 엔티티 및 테이블 생성, FOR UPDATE SKIP LOCKED를 활용한 배치 스케줄러 구현, 그리고 기존 UserLogService의 직접 발송 로직을 큐잉 방식으로 전환하는 작업이 포함되었습니다. 리뷰 결과, 아웃박스 테이블의 무한 증식을 방지하기 위한 데이터 정리(Cleanup) 로직 추가, 예외 발생 시 디버깅을 위한 로그 보강, 그리고 배치 조회 시 DB 라운드트립을 줄이기 위한 성능 최적화가 필요합니다.
The reason will be displayed to describe this comment to others. Learn more.
아웃박스 테이블(user_log_admin_dispatch_outbox)에 처리 완료(ACKED)되거나 재시도 횟수를 초과한(DEAD) 데이터에 대한 삭제 또는 아카이빙 로직이 부재합니다. 데이터가 무한히 쌓일 경우 스케줄러의 조회 성능이 점진적으로 저하될 수 있으므로, 주기적인 정리(Cleanup) 로직 도입이 필요합니다.
The reason will be displayed to describe this comment to others. Learn more.
현재 claimReadyBatch 로직은 ID 조회 후 다시 전체 엔티티를 조회(findAllById)하고 상태를 변경하여 저장하는 3단계 DB 라운드트립이 발생합니다. 성능 최적화를 위해 네이티브 쿼리에서 엔티티 리스트를 직접 반환받거나, 벌크 업데이트 쿼리를 사용하는 방안을 권장합니다.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝작업 내용
Burst 부하 구간에서
AbortPolicy이후 드러난rejected건을 유실로 두지 않기 위한 저장 및 재처리 구조 구성.문제 상황
AbortPolicy적용 이후user-logrequest path는 보호됐지만admin-log-featureexecutor 포화 시 enqueue reject가 지속적으로 발생해결 과정
READY / PROCESSING / ACKED / RETRY / DEAD정의READY,RETRY를 batch claim 후 재전달ACKED,RETRY,DEAD갱신user_log_admin_dispatch_outbox도입READY / PROCESSING / ACKED / RETRY / DEAD구성구조도
flowchart LR A["UserLogService"] --> B["Kafka publish"] A --> C["Outbox 저장"] D["UserLogAdminDispatchScheduler"] --> E["READY / RETRY 조회"] E --> F["AdminLogFeatureDispatchService"] F --> G["AdminLogFeaturesClient"] G --> H{"dispatch 결과"} H -->|success| I["ACKED"] H -->|failure| J["RETRY / DEAD"] style C fill:#eef8ef,stroke:#5a9c67 style D fill:#eef4ff,stroke:#5b80b7 style I fill:#eef8ef,stroke:#5a9c67 style J fill:#fff6ea,stroke:#c58f48👀변경 사항
구현 항목
src/main/java/site/holliverse/customer/persistence/entity/UserLogDispatchStatus.javasrc/main/java/site/holliverse/customer/persistence/entity/UserLogAdminDispatchOutbox.javasrc/main/java/site/holliverse/customer/persistence/repository/UserLogAdminDispatchOutboxRepository.javaFOR UPDATE SKIP LOCKED조회src/main/java/site/holliverse/customer/application/usecase/log/UserLogAdminDispatchOutboxService.javasrc/main/java/site/holliverse/customer/application/usecase/log/UserLogAdminDispatchOutboxStateService.javasrc/main/java/site/holliverse/customer/application/usecase/log/UserLogAdminDispatchScheduler.javasrc/main/java/site/holliverse/customer/application/usecase/log/AdminLogFeatureDispatchService.javasrc/main/java/site/holliverse/customer/application/usecase/log/UserLogService.javasrc/main/java/site/holliverse/customer/integration/external/AdminLogFeaturesClient.javasrc/main/java/site/holliverse/shared/monitoring/CustomerMetrics.javasrc/main/java/site/holliverse/shared/config/runtime/CustomerRuntimeInfraConfiguration.javasrc/main/resources/application-customer.ymlsrc/main/resources/db/migration/V33__create_log_dispatch_outbox.sql🎫 Jira Ticket
#️⃣관련 이슈