트레이닝을 예약 시, A가 있고, B가 있을 때

A가 3월 3일 18시 트레이닝 예약을 먼저 누르고 결제 중일 때,

B가 3월 3일 18시 트레이닝 예약을 근소한 차이로 누르면 기존 코드에서는 에러가 발생할거였다.

여기서 동시성 문제라는 걸 알았고 동시성을 어떻게 해결해야 되는지 여러 검색을 해봤다.

내가 고민했던 해결 방법은 두 가지였다.

동시성 해결 방법

  1. Synchronized 사용
  2. DB 락

1번 방법이 간단해 보였으나 Synchronized가 사용돼야 하는 메소드는 @Transactional과 함께 사용돼야 했다. 그런데 두 개를 동시에 사용하게 되면 @Transactional은 로직을 수행 후 마지막으로 commit을 하는데, 그 전에 메서드에 대한 락이 풀려버려 다른 스레드가 해당 메서드에 접근할 수 있기 때문에 내가 사용하고자 하는 방향과 맞지 않았다.

현 상황에서는 전체를 멈출 필요가 없고 예약 가능한 시간인지만 확인하는 거에 멈춰있으면 된다.

기존에는 date에서 Lock을 걸면 availableTime을 읽는 것도 자연스럽게 전 스레드의 time이 수정되고 난 후에 읽힐 줄 알았으나 그게 안 되고 예전 정보를 읽고 있더라. 그래서 date가 아닌 timeRepository에 락을 걸었다.

public interface AvailableTimeRepository extends JpaRepository<AvailableTime, Long> {
    @NotNull
    @Override
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @QueryHints({@QueryHint(name = "jakarta.persistence.lock.timeout", value = "3000")})
    Optional<AvailableTime> findById(@NotNull Long id);
    ...
}

그리고 코드를 수행해도 바뀌는 게 없는 것 같아 보던 중,

쓰기 지연이 아닌 바로 적용하기 위해 saveAndFlush가 필요하다는 글을 봤고