Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.backendsystemdesignlab.notification.outbox;

import com.backendsystemdesignlab.notification.notification.domain.DeliveryStatus;
import com.backendsystemdesignlab.notification.notification.domain.Notification;
import com.backendsystemdesignlab.notification.notification.domain.NotificationDelivery;
import com.backendsystemdesignlab.notification.notification.repository.NotificationDeliveryRepository;
import com.backendsystemdesignlab.notification.notification.repository.NotificationRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class OutboxFailureService {

private static final int MAX_PUBLISH_ATTEMPTS = 5;

private final OutboxEventRepository outboxEventRepository;
private final NotificationRepository notificationRepository;
private final NotificationDeliveryRepository deliveryRepository;

@Transactional
public boolean recordFailure(Long outboxEventId, String error) {

OutboxEvent event = outboxEventRepository.findById(outboxEventId)
.orElseThrow(() -> new IllegalArgumentException("Outbox event not found: " + outboxEventId));

if (event.getStatus() != OutboxStatus.PENDING) {
return false;
}

event.recordFailure(error);

if (event.getAttemptCount() < MAX_PUBLISH_ATTEMPTS) {
return false;
}

event.markFailed();

Notification notification = notificationRepository.findByIdForUpdate(event.getNotificationId())
.orElseThrow(() -> new IllegalArgumentException("알림을 찾을 수 없습니다."));

NotificationDelivery delivery = deliveryRepository.findById(event.getDeliveryId())
.orElseThrow(() -> new IllegalArgumentException("전송 정보를 찾을 수 없습니다."));

if (delivery.getStatus() == DeliveryStatus.PENDING) {
delivery.markFailed();
}

updateNotificationStatus(notification);

return true;
}

private void updateNotificationStatus(Notification notification) {
deliveryRepository.flush();

long total = deliveryRepository.countByNotificationId(notification.getId());
long sent = deliveryRepository.countByNotificationIdAndStatus(notification.getId(), DeliveryStatus.SENT);
long failed = deliveryRepository.countByNotificationIdAndStatus(notification.getId(), DeliveryStatus.FAILED);

// 아직 처리 중인 Delivery 존재
if (sent + failed < total) {
return;
}

if (failed > 0) {
notification.fail();
} else {
notification.complete();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class OutboxPublisher {
private final OutboxTransactionService transactionService;
private final RabbitTemplate rabbitTemplate;
private final NotificationTransactionService notificationTransactionService;
private final OutboxFailureService failureService;

// DB Connection 없음 (RabbitMQ가 DB를 잡고 있지 않게)
@Scheduled(
Expand Down Expand Up @@ -95,14 +96,9 @@ private String routingKey(OutboxEvent event) {

private void handleFailure(OutboxEvent event, String reason) {

boolean finalFailure = transactionService.recordPublishFailure(event.getId(), reason);
boolean finalFailure = failureService.recordFailure(event.getId(), reason);

if (finalFailure) {
notificationTransactionService.recordPublishFinalFailure(
event.getNotificationId(),
event.getDeliveryId()
);

log.error("Outbox 마지막 시도 실패. outboxId={}, reason={}", event.getId(), reason);
} else {
log.warn("Outbox publish 실패. outboxId={}, reason={}", event.getId(), reason);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
public class OutboxTransactionService {

private final OutboxEventRepository outboxEventRepository;
private static final int MAX_PUBLISH_ATTEMPTS = 5;

@Transactional(readOnly = true)
public List<OutboxEvent> findPendingEvents() {
Expand All @@ -28,23 +27,4 @@ public void markPublished(Long outboxEventId) {
}
event.markPublished();
}

@Transactional
public boolean recordPublishFailure(Long outboxEventId, String error) {
OutboxEvent event = outboxEventRepository.findById(outboxEventId)
.orElseThrow(() -> new IllegalArgumentException("Outbox event not found: " + outboxEventId));

if (event.getStatus() != OutboxStatus.PENDING) {
return false;
}

event.recordFailure(error);

if (event.getAttemptCount() >= MAX_PUBLISH_ATTEMPTS) {
event.markFailed();
return true;
}

return false;
}
}