230406 TIL #52 LockAcquisition Exception / 비관적Lock

김춘복·2023년 4월 6일
0

TIL : Today I Learned

목록 보기
52/571

230406 Today I Learned

실전프로젝트 6일차. 오늘은 redis를 하루종일 적용해보느라 고생했다. 이 내용은 내일 올리도록 하고 오늘은 어제 정리못한 jmeter 테스트 당시 발생했던 LockAcquisition Exception 처리를 올리도록 하겠다.


LockAcquisition Exception

  @Transactional
  public Long makeReservations(ReservationRequestDto dto, User user, Long ticketInfoId) {
    TicketInfo ticketInfo = ticketInfoRepository.findById(ticketInfoId).orElseThrow(
        () -> new IllegalArgumentException("공연회차 정보가 없습니다.")
    );
    if (!ticketInfo.isAvailable()) {
      throw new IllegalArgumentException("현재 예매가 불가능한 공연입니다.");
    }
    if (ticketInfo.getTotalSeats() >= ticketInfo.getReservedSeats() + dto.getCount()) {
      ticketInfo.reserveSeats(dto.getCount());

      // 만약 ReservedSeats와 TotalSeats가 같아지면 isAvailable을 false로 변경
      if (ticketInfo.getTotalSeats() == ticketInfo.getReservedSeats()) {
        ticketInfo.setAvailable(false);
      }
      ticketInfoRepository.save(ticketInfo);
      Reservation reservation = new Reservation(dto, user, ticketInfo);
      reservationRepository.saveAndFlush(reservation);
      return reservation.getId();
    }
    throw new IllegalArgumentException("남은 자리가 없습니다");
  }
  • 문제 : jmeter로 위의 예매하기 Post 요청에 서버 부하 테스트를 하던 중 H2일때는 10000명까지 잘 버티던 서버가 RDS MySQL 방식으로 바꾸니 50명 부터 LockAcquisition Exception이 발생했다.

  • 시도
  1. Isolation Level을 Read Uncommitted, Read Committed, Repeatable Read로 조정해봤으나 큰 차이 x

  2. .saveAndFlush를 .save로 바꿔봤으나 실패

  3. RDS 스펙 업그레이드
    db.t2.micro 프리티어 가장 기본에서 db.t4g.micro로 업그레이드 해봤으나 차이 x.
    Post말고 Get으로 단순 db조회로 요청을 바꾸니 10000명까지 문제가 없어 RDS 문제는 아니었다.

  4. TicketInfo에서 다른 트랜잭션끼리 동일한 자원을 수정하는 과정에서 발생한 문제라고 생각해서 로직 삭제 후 테스트 해보니 10000명까지 문제가 안나서 이걸 문제의 원인으로 잡았다.

그래서 일단 Optimistic Lock으로 TicketInfo에

@Version
private Long version;

을 만들어 시도해봤으나 실패. 여기서 Optimistic Lock이 잘 적용되었는지는 모르겠다.
추후 한번 더 시도해볼 예정.


  • 해결 : TicketInfo를 pessimistic lock으로 바꿔서 해결!!
    위의 version은 그대로 두고 TicketInfoRepository에서 아래와 같이 설정해 service에서 ticketInfo에 접근할 때 해당 메서드를 사용해서 Lock 적용.
public interface TicketInfoRepository extends JpaRepository<TicketInfo, Long> {

  @Lock(LockModeType.PESSIMISTIC_WRITE)
  @Query("SELECT t FROM TicketInfo t WHERE t.id = :id")
  Optional<TicketInfo> findByIdWithLock(@Param("id") Long id);
}

이렇게 하니 LockAcquisition Exception이 발생하지 않고 아래와 같은 결과를 얻었다.


  • 알게된 점 : LockAcquisition Exception이 발생했을땐 Lock을 제대로 걸어줘야 한다.
    그리고 비관적Lock은 지나치게 반응속도가 늦어 보완할 방법을 찾아야 한다.
profile
Backend Dev / Data Engineer

0개의 댓글