Skip to content

[SRLT-151] 알림 발행 유실 방지를 위한 Outbox 구조 도입#93

Merged
SeongHo5356 merged 20 commits into
developfrom
SRLT-151-알림-outbox
May 9, 2026

Hidden character warning

The head ref may contain hidden characters: "SRLT-151-\uc54c\ub9bc-outbox"
Merged

[SRLT-151] 알림 발행 유실 방지를 위한 Outbox 구조 도입#93
SeongHo5356 merged 20 commits into
developfrom
SRLT-151-알림-outbox

Conversation

@SeongHo5356
Copy link
Copy Markdown
Member

🚀 Why - 해결하려는 문제가 무엇인가요?

  • 기존 알림 구조는 Notification을 DB에 저장한 뒤, 트랜잭션 커밋 이후 Redis Pub/Sub로 실시간 알림을 발행하는 방식이었습니다.
  • 이 구조에서는 DB 커밋 이후 Redis publish 전에 애플리케이션이 종료되거나 장애가 발생하면, 알림 row는 남아도 실시간 발행 작업은 유실될 수 있었습니다.
  • 알림 발행을 재처리 가능한 작업으로 남겨, 일시 장애나 프로세스 중단 이후에도 Redis/SSE 발행을 복구할 수 있는 기반이 필요했습니다.

✅ What - 무엇이 변경됐나요?

  • NotificationOutbox 엔티티를 추가했습니다.
  • 알림 생성 시 NotificationNotificationOutbox를 같은 트랜잭션에 저장하도록 변경했습니다.
  • 알림 발행 이벤트가 notification id 대신 outbox id를 전달하도록 변경했습니다.
  • outbox 발행 전용 서비스와 스케줄러를 추가했습니다.
  • outbox 상태를 PENDING, PROCESSING, SENT, FAILED, DISCARDED로 관리하도록 했습니다.
  • Redis publish 실패 시 outbox를 FAILED로 남기고, 설정된 지연 시간 이후 재처리할 수 있도록 했습니다.
  • test 환경에서는 outbox worker를 비활성화하고, dev/stage/prod 설정에는 worker 실행 설정을 추가했습니다.

🛠️ How - 어떻게 해결했나요?

  • NotificationService.notifyMember(...)에서 알림을 저장한 뒤 즉시 NotificationOutbox를 함께 생성합니다.
  • 트랜잭션 커밋 이후 NotificationCreatedEventHandler가 outbox id를 받아 NotificationOutboxPublishService에 발행을 위임합니다.
  • NotificationOutboxPublishService는 outbox를 먼저 PROCESSING 상태로 claim한 뒤 Redis publish를 수행합니다.
  • publish 성공 시 outbox를 SENT로 변경합니다.
  • Redis 장애 등 일시적 실패가 발생하면 outbox를 FAILED로 변경하고 nextRetryAt을 설정해 재처리 대상으로 남깁니다.
  • 알림 row가 없는 등 재시도해도 복구할 수 없는 경우에는 DISCARDED로 분리합니다.
  • NotificationOutboxPublishScheduler는 주기적으로 PENDING, 재시도 가능한 FAILED, 오래된 PROCESSING outbox를 조회해 다시 발행합니다.
  • 여러 워커가 같은 outbox를 동시에 처리할 가능성은 @Version 기반 낙관적 락과 claim 단계로 방어합니다.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

Warning

Rate limit exceeded

@SeongHo5356 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 49 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a5693331-ba5b-4885-b48b-1a5583e1fb9d

📥 Commits

Reviewing files that changed from the base of the PR and between dbc6380 and 6fc755e.

📒 Files selected for processing (48)
  • config
  • src/main/java/starlight/StarlightApplication.java
  • src/main/java/starlight/adapter/notification/NotificationSendAdapter.java
  • src/main/java/starlight/adapter/notification/persistence/NotificationJpa.java
  • src/main/java/starlight/adapter/notification/persistence/NotificationOutboxJpa.java
  • src/main/java/starlight/adapter/notification/persistence/NotificationOutboxRepository.java
  • src/main/java/starlight/adapter/notification/persistence/NotificationRepository.java
  • src/main/java/starlight/adapter/notification/redis/NotificationRedisSubscriberConfig.java
  • src/main/java/starlight/adapter/notification/redis/RedisNotificationPublisher.java
  • src/main/java/starlight/adapter/notification/redis/RedisNotificationSubscriber.java
  • src/main/java/starlight/adapter/notification/sse/NotificationSseRegistry.java
  • src/main/java/starlight/adapter/notification/webapi/NotificationController.java
  • src/main/java/starlight/adapter/notification/webapi/dto/request/NotificationTestRequest.java
  • src/main/java/starlight/adapter/notification/webapi/dto/response/NotificationResponse.java
  • src/main/java/starlight/adapter/notification/webapi/swagger/NotificationApiDoc.java
  • src/main/java/starlight/application/aireport/AiReportService.java
  • src/main/java/starlight/application/aireport/required/AiReportNotificationPort.java
  • src/main/java/starlight/application/notification/NotificationCreatedEventHandler.java
  • src/main/java/starlight/application/notification/NotificationOutboxPublishScheduler.java
  • src/main/java/starlight/application/notification/NotificationOutboxPublishService.java
  • src/main/java/starlight/application/notification/NotificationService.java
  • src/main/java/starlight/application/notification/event/NotificationCreatedEvent.java
  • src/main/java/starlight/application/notification/provided/NotificationUseCase.java
  • src/main/java/starlight/application/notification/provided/dto/input/NotificationSendInput.java
  • src/main/java/starlight/application/notification/provided/dto/result/NotificationResult.java
  • src/main/java/starlight/application/notification/required/NotificationCommandPort.java
  • src/main/java/starlight/application/notification/required/NotificationOutboxCommandPort.java
  • src/main/java/starlight/application/notification/required/NotificationOutboxQueryPort.java
  • src/main/java/starlight/application/notification/required/NotificationPublishPort.java
  • src/main/java/starlight/application/notification/required/NotificationQueryPort.java
  • src/main/java/starlight/application/notification/required/NotificationRealtimePort.java
  • src/main/java/starlight/application/notification/required/dto/NotificationPublishMessage.java
  • src/main/java/starlight/bootstrap/AsyncConfig.java
  • src/main/java/starlight/domain/notification/entity/Notification.java
  • src/main/java/starlight/domain/notification/entity/NotificationOutbox.java
  • src/main/java/starlight/domain/notification/enumerate/NotificationOutboxStatus.java
  • src/main/java/starlight/domain/notification/exception/NotificationErrorType.java
  • src/main/java/starlight/domain/notification/exception/NotificationException.java
  • src/main/java/starlight/domain/notification/exception/NotificationOutboxException.java
  • src/main/resources/templates/pdf-ai-report-ready.html
  • src/test/java/starlight/adapter/notification/NotificationSendAdapterTest.java
  • src/test/java/starlight/adapter/notification/redis/RedisNotificationSubscriberTest.java
  • src/test/java/starlight/application/aireport/AiReportServiceIntegrationTest.java
  • src/test/java/starlight/application/aireport/AiReportServiceUnitTest.java
  • src/test/java/starlight/application/expertReport/ExpertReportServiceUnitTest.java
  • src/test/java/starlight/application/notification/NotificationOutboxPublishServiceUnitTest.java
  • src/test/java/starlight/application/notification/NotificationServiceUnitTest.java
  • src/test/java/starlight/application/order/OrderPaymentServiceUnitTest.java

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'tools'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch SRLT-151-알림-outbox

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 24, 2026

Test Results

262 tests   262 ✅  11s ⏱️
 51 suites    0 💤
 51 files      0 ❌

Results for commit 6fc755e.

♻️ This comment has been updated with latest results.

@SeongHo5356 SeongHo5356 requested a review from 2ghrms May 7, 2026 13:56
Copy link
Copy Markdown
Member

@2ghrms 2ghrms left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨서용~~

@SeongHo5356 SeongHo5356 merged commit 6af9bdd into develop May 9, 2026
4 checks passed
@SeongHo5356 SeongHo5356 self-assigned this May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants