[REFACTOR] IO Bound 작업 비동기 처리를 통한 병목 개선#59
Hidden character warning
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (37)
📝 WalkthroughWalkthroughIntroduces an outbox-based event sourcing pattern for reliable notification delivery, replaces direct repository access with service layers across calendar, chat, and notice domains, and refactors notification delivery to support batched multicast with exponential backoff retry mechanism. Changes
Sequence DiagramssequenceDiagram
actor Publisher
participant RequestEvent as Event System
participant OutboxListener as OutboxRecord<br/>Listener
participant OutboxService as OutboxService
participant RequestListener as RequestListener<br/>(Async)
participant DeliveryProcessor as Delivery<br/>Processor
participant DeviceService as DeviceService
participant Orchestrator as Orchestrator
participant FCM as FCM
participant SSE as SSE
Publisher ->> RequestEvent: publish(outboxNo, data)
RequestEvent ->> OutboxListener: BEFORE_COMMIT
OutboxListener ->> OutboxService: saveInit(outboxNo, ...)
OutboxService ->> OutboxService: persist with status=INIT
RequestEvent ->> RequestListener: AFTER_COMMIT (async)
activate RequestListener
RequestListener ->> DeliveryProcessor: processFromEvent(event)
DeliveryProcessor ->> OutboxService: find outbox
DeliveryProcessor ->> DeviceService: findByUserNo(recipientNo)
DeviceService -->> DeliveryProcessor: List<Device>
DeliveryProcessor ->> Orchestrator: deliver(notification, devices)
Orchestrator ->> Orchestrator: classify devices by channel
Orchestrator ->> SSE: send(payload, sseDevices)
SSE -->> Orchestrator: success
Orchestrator ->> FCM: sendMulticast(payload, fcmTokens)
FCM -->> Orchestrator: MulticastSendResult
Orchestrator -->> DeliveryProcessor: DeliveryResult
alt hasRetryableFailure
DeliveryProcessor ->> OutboxService: fail(outboxNo)
OutboxService ->> OutboxService: set status=INIT, nextRetryAt=now+backoff
else success
DeliveryProcessor ->> OutboxService: success(outboxNo, notificationNo)
OutboxService ->> OutboxService: set status=SUCCESS, processedAt=now
end
alt has invalid tokens
DeliveryProcessor ->> DeviceService: clearInvalidFcmTokens(invalidTokens)
DeviceService ->> DeviceService: clear fcmToken on matching devices
end
deactivate RequestListener
sequenceDiagram
participant Scheduler as RetryScheduler
participant OutboxService as OutboxService
participant DeliveryProcessor as DeliveryProcessor
participant Outbox as NotificationOutbox
participant Orchestrator as Orchestrator
participant Notification as NotificationService
loop Every 5 seconds
Scheduler ->> OutboxService: loadRetryBatch(100)
OutboxService ->> OutboxService: query INIT status, nextRetryAt <= now
OutboxService -->> Scheduler: List<NotificationOutbox>
loop for each outbox
Scheduler ->> DeliveryProcessor: processFromOutbox(outbox)
DeliveryProcessor ->> Notification: save(...)
Notification -->> DeliveryProcessor: Notification
DeliveryProcessor ->> Orchestrator: deliver(notification, devices)
Orchestrator -->> DeliveryProcessor: DeliveryResult
alt retry allowed && hasRetryableFailure
DeliveryProcessor ->> Outbox: failWithBackoff(maxRetries=5, baseBackoff=5s)
Outbox ->> Outbox: retryCount++, nextRetryAt=now+delay
else maxRetries exceeded
Outbox ->> Outbox: status=FAIL, finalizeProcess
else success
Outbox ->> Outbox: status=SUCCESS
end
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment Tip CodeRabbit can scan for known vulnerabilities in your dependencies using OSV Scanner.OSV Scanner will automatically detect and report security vulnerabilities in your project's dependencies. No additional configuration is required. |
📝 Pull Request Template
📌 제목
📢 요약
FCM 전송 분기를 탈 경우 요청 시간 대부분이 외부 IO(FCM API 호출)에 소요되어 병목이 발생함.
요청 지연을 줄이기 위해 알림 전송을 비동기로 분리했고, 이후 정합성 보장을 위해 아웃박스 패턴을 도입함.
현재는 아웃박스 기반 재처리 + 채널별 전송(SSE/FCM Multicast) 구조로 정리되어 있음.
추가로 SSE 연결 실패를 유발하던 인증 에러 응답 직렬화 문제를 수정함.
무엇을 바꿨는지
save -> deliver -> token 정리 -> outbox 상태)을 공통 처리 컴포넌트로 통합함.JwtAuthenticationFilter의ObjectMapper를 스프링 빈으로 주입받게 변경해LocalDateTime직렬화 오류를 해결함.jwt.cookie.secure=false를 명시함.왜 그렇게 했는지
NotificationOutboxDeliveryProcessor로 모아, 성공/실패 상태 전이를 한 흐름으로 보장하려는 목적.어떻게 구현되어 있는지 (아웃박스 흐름)
NotificationRequestPublisheroutboxNo를 함께 생성.NotificationOutboxRecordListener (BEFORE_COMMIT)notification_outbox에 INIT 상태 레코드 저장.NotificationRequestListener (AFTER_COMMIT + @Async)NotificationOutboxDeliveryProcessorsave -> channel 분류/전송 -> invalid token 정리 -> outbox success/fail을 단일 처리 단위로 수행.NotificationOutboxRetrySchedulerNotificationDeliveryOrchestrator🔗 연관 이슈: Resolves #이슈번호
🚀 PR 유형
✅ PR 체크리스트
📜 기타
주요 변경 포인트
NotificationRequestListener,NotificationOutboxRetrySchedulerNotificationOutboxDeliveryProcessorNotificationDeliveryOrchestratorFcmNotificationDelivery,SseNotificationDeliveryJwtAuthenticationFilter,SecurityConfigapplication-dev.yml삭제/정리
NotificationDelivery,NotificationDeliveryFactory및 관련 테스트 제거.검증
./gradlew compileJava통과.Summary by CodeRabbit
New Features
Improvements