refactor : 알림/채팅 Redis 발행 안정성 및 예외 처리 개선#155
Conversation
…P-Link-it/backend into feature/notification/message
…eature/notification/message
…nk 통합 처리, 시스템 알림과 탈퇴/미설정 유저 구분, toMap merge function 추가로 중복 키 예외 방지)
…: ChatPublishFailedException)
…eature/notification/message
There was a problem hiding this comment.
Code Review
This pull request refactors Redis message publishing for chat and notifications to improve error handling and consistency. Key changes include the introduction of custom exceptions for publishing failures, enhanced logging, and the implementation of transaction synchronization in NotificationServiceImpl to ensure notifications are sent only after a successful commit. Feedback suggests extending the transactional outbox pattern to publishReadEvent in ChatService and reconsidering the exception handling strategy in favor of a retry mechanism, as exceptions thrown after a commit may not be properly propagated to the client.
| } catch (JsonProcessingException e) { | ||
| log.error("읽음 이벤트 직렬화 실패", e); | ||
| log.error("읽음 이벤트 직렬화 실패: roomId={}, userId={}", roomId, userId, e); | ||
| throw new ChatPublishFailedException(roomId); |
| } catch (JsonProcessingException e) { | ||
| log.error("ChatPayload 직렬화 실패", e); | ||
| log.error("채팅 메시지 직렬화 실패: roomId={}, messageId={}", roomId, message.getId(), e); | ||
| throw new ChatPublishFailedException(roomId); |
There was a problem hiding this comment.
ChatPublishFailedException을 던져 STOMP ERROR 프레임을 전달하고자 하는 의도는 확인되나, afterCommit 패턴을 적용할 경우 트랜잭션 완료 후 예외가 발생하므로 클라이언트에게 에러 전달이 어려울 수 있습니다. 프로젝트 규칙에 따라, 트랜잭션 롤백과 관련된 복잡한 예외 처리나 상태 업데이트(FAILED 등) 대신, 스케줄러를 통한 재시도 메커니즘을 활용하여 처리를 단순화하는 것을 권장합니다.
References
- 트랜잭션 메소드에서 예외 발생 시 'FAILED' 상태 업데이트나 복잡한 예외 처리가 어렵다면, 이를 생략하고 스케줄러를 통한 재시도 메커니즘을 사용하는 것이 권장됩니다.
…ishFailedException)
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request improves the reliability of Redis Pub/Sub messaging by introducing custom exceptions for publish failures and implementing Transactional Outbox patterns for notifications. The reviewer noted that while the notification service correctly uses TransactionSynchronizationManager to ensure Redis messages are published only after transaction commits, the chat service's message and read event publishing logic still lack this transactional safety, risking data inconsistency if a transaction rolls back. I recommend applying the same TransactionSynchronizationManager approach to the chat message and read event publishing methods in ChatService to ensure consistency.
| try { | ||
| String json = objectMapper.writeValueAsString(payload); | ||
| String channel = CHAT_CHANNEL_PREFIX + message.getChatRoom().getId(); | ||
| String channel = CHAT_CHANNEL_PREFIX + roomId; | ||
| redisTemplate.convertAndSend(channel, json); | ||
| log.info("Redis 메시지 발행: channel={}, messageId={}", channel, message.getId()); | ||
| } catch (JsonProcessingException e) { | ||
| log.error("ChatPayload 직렬화 실패", e); | ||
| log.error("채팅 메시지 직렬화 실패: roomId={}, messageId={}", roomId, message.getId(), e); | ||
| throw new ChatPublishFailedException(roomId); | ||
| } |
There was a problem hiding this comment.
PR 설명에 언급된 'Transactional Outbox 패턴'이 채팅 메시지 발행에는 적용되지 않았습니다. 현재 구현 방식은 redisTemplate.convertAndSend가 트랜잭션 커밋 여부와 상관없이 즉시 실행되므로, 이후 트랜잭션이 롤백되더라도 Redis 메시지는 이미 발행되어 데이터 정합성이 깨질 수 있습니다. NotificationServiceImpl과 동일하게 TransactionSynchronizationManager를 사용하여 커밋 이후에 발행되도록 개선이 필요합니다. 다만, 이 경우 afterCommit 내부에서 발생하는 예외는 호출자에게 전달되지 않으므로 로그 처리가 권장됩니다.
| } catch (JsonProcessingException e) { | ||
| log.error("읽음 이벤트 직렬화 실패", e); | ||
| log.error("읽음 이벤트 직렬화 실패: roomId={}, userId={}", roomId, userId, e); | ||
| throw new ChatPublishFailedException(roomId); | ||
| } |
🔥ISSUE
📒TODO
📋REF
✅ 주요 수정 사항 상세
🎯정리