트랜잭션 전파를 깊이 있게 학습 후 상태를 재구성 후 테스트
2024.09.04 수정
최근에 트랜잭션 전파에 대해 깊이 있게 학습하며 이전에 알림 기능을 개발할 때를 재구성 후 테스트하였고 잘못 알고 있던 점을 되짚었습니다.
2024.09.04 - [프로젝트/똑립 (UMC)] - REQUIRES_NEW / TransactionalEventListener + Async 알림 문제 복기
기존 서비스
저희 게시판 서비스는 HoneyTip(꿀팁공유해요 게시판)과 HoneyTipComment(꿀팁공유해요 댓글)이 연관관계 매핑이 되어있습니다.
HoneyTipComment는 Comment를 단일테이블 전략으로 상속받고 있습니다.
다른 댓글 서비스와 HoneyTipCommentService는 댓글 생성, 삭제시 최종적으로 CommentService 호출합니다.
새로운 요구 사항
다양한 상황 속에서, 알림을 전송해야하는 요구사항이 생겼습니다.
HoneyTipCommentService.register()
CommentService.register()
댓글 알림 AOP
댓글을 생성시에는 CommentService를 필수로 거치므로, 포인트컷을 댓글 생성부분으로 정합니다.
그 후 알림을 보내는 NotificationDispatcher를 AOP에서 호출했습니다.
NotificationDispatcher.dispatch()
다양한 알림 종류
NotificationDispatcher.dispatch()
알림 전송 담당. fcmService 메서드 호출
만약 validFCMToken에서 예외 발생시 comment 롤백됩니다.
FcmService.sendNotification()
문제 상황 발생
NotificationDispatcher.dispatch()에서 예외가 발생하던, FcmService에서 예외가 발생하던, 롤백처리 되어 comment까지 롤백되는 현상을 발견했습니다.
CommentService.register()에서 트랜잭션이 끝날 것이라 생각했었습니다.
Comment가 커밋이 되지 않은걸까요?
영속성 컨텍스트에는 존재하지만 DB에는 반영이 되지 않았습니다.
하나의 예외가 발생한다면 comment까지 롤백이 되는 것을 확인했습니다.
알림을 전송하지 못한다고 댓글이 생성되지 않으면 안되는 상황입니다.
이를 어떻게 해결할 수 있을까요?
첫번째 시도. 매 요청마다 새로운 트랜잭션 생성
매 요청마다 새로운 트랜잭션을 생성하여 실행했습니다.
REQUIRES_NEW로 생성된 트랜잭션에서 예외가 발생하면, 이 예외는 호출한 메서드로 전파됩니다. 호출한 메서드에서 이 예외를 처리하지 않고 다시 던진다면, 호출한 메서드의 트랜잭션도 롤백됨을 알았습니다.
결국에 원하는 건 CommentService의 register 메서드에서 시작한 트랜잭션이 완벽히 끝난 후에 알림을 전송하고, 생성하는 로직을 분리해야합니다.
다시 말해 트랜잭션이 끝난 후 동작해야합니다. 트랜잭션을 분리해야합니다.
두번째 시도. 트랜잭션 분리와 Listener 도입
Dispatcher, Registery 등 부가 코드 유지합니다.
Listener를 도입하여, AOP에서 Listener에 새로운 이벤트 생성합니다.
AOP에서 Listener로 전송하고, Listener는 이를 구독하여 감지하도록 변경했습니다.
트랜잭션을 분리하기 위해 CommentCreateEvent를 구독하고, 이전의 트랜잭션이 커밋된 후에 실행되도록 트랜잭션 격리 단계를 커밋 직후로 수정했습니다.
두번째 시도를하니, 알림 전송을 실패해도 Comment는 정상적으로 저장되었습니다.
두번째 문제
하지만 다른 문제가 발생했습니다.
fcm service에서 sendNotification메서드에서 예외 발생시 notificationService.register는 호출은 되지만 Notification 엔티티가 생성이 되지 않습니다.
저희는 알림 전송에 실패하게 된다면 실패여부를 저장하고 Notification 엔티티를 생성하고 싶었습니다.
Notificaation 엔티티 생성시 알림 전송이 성공한다면 성공여부를 true로, 실패했다면 false로 엔티티를 생성하는 것을 원했습니다.
메서드는 정상 호출되지만 실제 데이터는 삽입되지 않았습니다.
이것 또한 FCMService에서 발생한 예외 때문에 롤백 정책이 aop가 적용된 댓글 트랜잭션, ㅁ전체 트랜잭션에 적용되기 때문이었습니다.
그러다 매 요청마다 새로운 트랜잭션을 생성하여 실행하도록 수정해봤습니다.
이렇게하니까 모든게 해결되었습니다.
그래서 REQUIRES_NEW 설정에 대해 학습해봤습니다.
하지만 요청이 많아지면 데드락이 걸린다는 등 REQUIRES_NEW의 타입의 위험성도 알게되었습니다.
세번째 시도 비동기처리, @Async 도입
알림 전송과, 알림 엔티티 생성 작업을 독립된 트랜잭션에서 실행시키고, 서로의 상태에 영향을 주지 않기위해 비동기로 실행했습니다.
이렇게하니 최종적으로 알림 전송에 실패하더라도, 트랜잭션을 별도로 처리하여 알림 엔티티 생성에 성공했습니다.
'프로젝트 > 똑립' 카테고리의 다른 글
멀티모듈에서 AOP 사용시 주의점 (0) | 2024.11.15 |
---|---|
똑립 3. 멀티쓰레드 환경에서 동시성 제어. 분산락 적용 (0) | 2024.09.08 |
똑립 2. REQUIRES_NEW / TransactionalEventListener + Async 알림 문제 복기 (1) | 2024.09.04 |