목차
상황
1. 이벤트에 당첨된 경우 휴대폰번호를 입력하고 [쿠폰받기] 버튼을 누르면 쿠폰이 즉시 발송된다.
2. 근데 가끔씩 [쿠폰받기] 버튼이 눌린채로 아무런 반응이 없는 경우가 있다.
(브라우저 자체가 멈춘다던가... 아무런 액션 없이 그냥 대기만 한다던가...)
3. 기다려도 응답이 없으니 사람들은 [쿠폰 안받기] 버튼을 누른다.
4. [쿠폰 안받기] 버튼을 누르면 당첨내역을 초기화시킨다. (다른사람에게 할당되어야하므로)
서버에 찍힌 로그는 아래와 같다.
2022-12-05 10:45:24 쿠폰받기 시작
2022-12-05 10:45:25 쿠폰안받기 시작
2022-12-05 10:45:25 쿠폰받기-쿠폰발송 성공
2022-12-05 10:45:26 쿠폰안받기 종료
2022-12-05 10:45:27 쿠폰받기 종료 << Error!!!
UnexpectedRollbackException: Transaction silently rolled back
because it has been marked as rollback-only....
일단 쿠폰이 발송되었고
DB에 휴대폰번호와 당첨내역을 보니 롤백되어 데이터가 날아간 상황이였다
ㅎㄷㄷ...... 아예 이벤트 미참여한 고객으로 나옴....
더 고려해야할 점은 위에처럼 [쿠폰안받기] 종료 이후 [쿠폰받기] 종료로 순서가 고정되어있지 않고
어떤게 먼저 수행될지 모른다는 점...
시도한 것
Jpa & Querydsl Lock 설정
//jpa
@Lock(value = LockModeType.PESSIMISTIC_WRITE)
//querydsl
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
[쿠폰받기]와 [쿠폰안받기] 에서 id(15번)로 조회할 때
비관적 락을 적용하여 다른 트랜잭션에서 읽기도 쓰지도 못하게 하였다.
select문 뒤에 for update가 붙는 것을 확인하였는데... 조회가 된다..!
PESSIMISTIC_WRITE는 분명 읽기도 쓰지도 못한다그랬는데... 조회가된다......
내가 테스트를 잘못한것일수도 있지만.. 다른분들도 몇번 테스트 해봤지만 조회가 되었고,
update문이 나갈때 똑같이 에러가 발생하며 롤백되어서 이 방법은 실패
나의 해결방법
update ~~ where ~~ 조건 설정
서버에서 데이터를 체크해서 제어하기엔 좀 어렵다고 생각되어서 Query로 해결하기로 했다!
어차피 DB에서 update문은 1번에 1개만 수행될 것이다.
[쿠폰 받기], [쿠폰 안받기] 에서 조회할 땐 아래 객체를 똑같이 조회하게된다
CASE 1 )
[쿠폰 안받기] 먼저 수행
update ~
set participate = 'N', win = 'N'
where phone is null
[쿠폰 받기] 먼저 수행
update ~
set participate = 'Y', win = 'Y', phone = '010-0000-0000'
where phone is null
participate와 win은 select 시 가져왔던 값으로 똑같이 덮어주어서 당첨 이력이 남도록 한다.
CASE 2 )
[쿠폰 받기] 먼저 수행
update ~
set participate = 'Y', win = 'Y', phone = '010-0000-0000'
where phone is null
[쿠폰 안받기] 나중 수행
update ~
set participate = 'N', win = 'N'
where phone is null
phone is null 이 아니니 해당 update문은 아예 무시됨!!
Transactional Propagation 셋팅
update문이 나가도 다른로직에서 에러가 발생해서 롤백되면 안되니까..
rollbackFor을 쓰기엔 좀.. 부담되어서 propgation을 설정해주었다
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Response SendCoupone(~~){
//참여정보 저장
saveParticipate();
//당첨정보 저장
saveWinner();
//쿠폰 발송
sendCoupon();
return response;
}
해당 설정을 해주면 매번 새로운 트랜잭션을 시작한다.
참여정보 저장 따로, 당첨정보 저장 따로, 쿠폰발송 따로따로 각개전투하며 커밋된다
앞서 말한 [쿠폰받기], [쿠폰안받기]가 동시에 들어오는 빈도수가 생각보다 좀 있어서
계속 저 에러가 발생되는 상황이였다 (차라리 죽여...)
일단은 update문과 propagation 2가지 설정하는 것으로
롤백되어서 날아가는 데이터도 잡아냈고, 에러 발생하는 빈도수도 엄청 낮추었다!
내 방법이 정답은 아니지만 이렇게 해결했다는 기록용 후기~~~ 끝!
++ 댓글 추가
좋은 의견을 남겨주셔서 나중에 다시 살펴보려한다!
'Backend' 카테고리의 다른 글
인덱스(2) : 인덱스가 데이터를 검색하는 방법 (1) | 2024.07.17 |
---|---|
인덱스(1) : 인덱스를 사용하는 이유, 인덱스의 정의, 인덱스에 저장되는 데이터 (0) | 2024.07.17 |
[SpringBoot] Jpa Connection Minimum-Idle 설정하지 말자... 에러 후기 (0) | 2022.12.04 |
[SpringBoot] Apache Poi를 이용한 엑셀다운로드는 SXSSF를 쓰자..! (0) | 2022.12.03 |
Spring Jpa SelfJoin 순환참조 방지하며 다른 엔티티와 맵핑하기 (2) | 2022.11.09 |