-
Notifications
You must be signed in to change notification settings - Fork 2
선착순 ‐ 동시성 문제
mjmj edited this page Aug 26, 2024
·
5 revisions
-
@Transactional
과synchronized
동시 사용 - 해결 실패 ㅠ
@Transactional
public synchronized QuizFirstComeSubmitResponseDto quizSubmit(QuizFirstComeSubmitRequest quizFirstComeSubmitRequest, User authenticatedUser) {
Long subEventId = quizFirstComeSubmitRequest.getSubEventId();
String answer = quizFirstComeSubmitRequest.getAnswer();
SubEvent subEvent = subEventRepository.findById(subEventId)
.orElseThrow(() -> new SubEventNotFoundException());
QuizFirstCome quizFirstCome = quizFirstComeRepository.findBySubEventId(subEventId)
.orElseThrow(() -> new SubEventNotFoundException());
if (dateUtil.isNotWithinSubEventPeriod(subEvent)) {
throw new SubEventNotWithinPeriodException();
}
if (!quizFirstCome.getAnswer().equals(answer)) {
return QuizFirstComeSubmitResponseDto.notCorrect();
}
EventUser eventUser = EventUser
.builder()
.user(authenticatedUser)
.subEvent(subEvent)
.chance(-1)
.build();
eventUserRepository.save(eventUser);
return quizWinnerDraw.winnerDraw(quizFirstCome, subEvent, authenticatedUser);
}
-
@Transactional
을 사용한다는 것은 AOP를 사용하여 프록시를 호출하는 것이다. - proxy가 동기화 메서드를 모두 끝낸 후 transaction이 커밋되기까지 시간 차이가 생김 이 사이에 다른 스레드가 동기화 메서드에 접근한다.
- 즉, 데이터가 commit 되기전에 다른 스레드가 이 데이터베이스를 접근한다.
- synchronized는 완벽하게 동작하지 않음
- 선착순 당첨자 로직에만
synchronized
사용 - 실패..
- QuizFirstCome이 winnerCount를 가지고 있는데 이 엔티티를 가져오는 시점은 당첨자 로직이 시작되기 전(동기화 x)이기 때문에 동시성 문제를 해결할 수 없었음.
- QuizFirstCome에서 당첨자 수를 관리하지 않고 직접 쿼리를 보내 Winners 엔티티의 당첨자 수를 직접 세줌
@Query("SELECT COUNT(w) FROM Winner w WHERE w.subEvent.id = :subEventId")
long countWinnerBySubEventId(@Param("subEventId") Long subEventId);
-
@Transactional
메서드 안에서@Transactional
이 없는 메소드를 호출하면 부모 메서드의 트랜잭션이 전파된다. - 참고
- 이 또한 @Transactional 문제에서 벗어날 수 없었음
- 낙관적락 (@Version)
- 우리가 하고 있는 작업이 쓰기가 굉장히 많은 작업이기 때문에 버전 불일치에 따른 재시도가 많이 일어날 수 있을 것이라 보았다.
- 읽기가 많은 작업이었으면 선택했지만 쓰기가 많은 작업이라 선택하지 않았다.
- lua script 사용
- 1,2,3번은 여전히 동시성 문제가 해결되지 않음.
- 추후 배치 작업 (redis에 당첨자 저장 후) 위해 lua script 사용
- redis가 싱글스레드에서 동시성을 보장해주기 때문에 문제 없이 동시성을 보장할 수 있었음