1. 문제: 발행된 이벤트는 무조건 처리된다.
동시성 이슈를 해결하기 위해 낙관적 잠금을 구현하고 있었다.
충돌을 방지하기 위해 낙관적 잠금을 걸게 되면 애플리케이션 단에서 버저닝 필드를 검증한다. 그 후 버저닝 필드가 일치하지 않으면 트랜잭션 충돌로 판단하여 아래와 같이 낙관적 잠금 예외를 발생시키게 된다. 이 예외는 트랜잭션이 커밋되는 시점에 발생하는데, 그렇다면 예외가 발생해 트랜잭션이 롤백된다면 트랜잭션 내부에서 이전에 발행된 이벤트는 어떻게 처리될까?
총대마켓의 기존 코드에서는 트랜잭션이 롤백되어도 이미 발행된 이벤트는 실행되었다. 즉, 사용자가 참여하려고 할 때 공동구매가 마감되어 참여하지 못했는데(동시성 이슈로 인한 트랜잭션 롤백), "에버님이 공동구매에 참여했어요!"라는 알림이 전송(이벤트 실행)되는 꼴이다.
이 문제를 해결하기 위해, 예외가 발생하여 호출 메서드의 트랜잭션이 롤백되는 경우 발행된 이벤트가 실행되지 않도록 하자.
2. 기존 코드 상황: @EventListener로 발행 즉시 처리
기존 코드에 따르면 이벤트는 @EventListener를 통해 발행 즉시 처리된다. 즉 트랜잭션의 상태와 무관하게 아래에서 정의된 로직이 실행된다.
@EventListener
@Async
public void handleParticipateEvent(ParticipateEvent event) {
// ...
}
3. 해결 방법: @TransactionalEventListener로 트랜잭션 상태에 따라 처리
@TransactionalEventListener를 적용하고 phase를 default인 AFTER_COMMIT으로 설정하여, 트랜잭션이 커밋되어야만 이벤트가 처리되도록 한다. 덕분에 이벤트가 발행된 후 트랜잭션이 롤백되면, 트랜잭션이 커밋되지 않은 상황이므로 이벤트가 발행되어도 처리되지 않는다.
@TransactionalEventListener(phase = AFTER_COMMIT)
@Async
public void handleParticipateEvent(ParticipateEvent event) {
// ...
}
이것이 해결 방법의 전부이다. 하지만 @TransactionalEventListener에 대해 더 알아보고 싶어 학습테스트를 구현해 학습해 보았다. 관련 글은 아래를 참고하면 된다.
↓
↓
↓
↓
2024.12.23 - [⛳️ 공동구매 서비스 총대마켓] - 👀 @TransactionalEventListener: 학습테스트로 동작 방식 확인해보기
'⛳️ 공동구매 서비스 총대마켓' 카테고리의 다른 글
QueryDSL을 도입하면 무엇이 좋을까 (from JPQL) (0) | 2025.01.06 |
---|---|
👀 @TransactionalEventListener: 학습테스트로 동작 방식 확인해보기 (0) | 2024.12.23 |
동시성 이슈 해결 과정: 트랜잭션 격리 수준, 낙관적 잠금, 비관적 잠금 (2) | 2024.12.18 |
FCM 푸시알림 트러블슈팅: 비동기 처리로 동시성 이슈 방지하기 (0) | 2024.11.25 |
FCM 푸시알림 도입기 (with 안드로이드 코틀린 & 백엔드 자바) (0) | 2024.11.20 |