지난번에는 낙관적 락을 이용해서 문제 해결을 해보았다.
이번에는 비관적 락을 이용해보자
비관적 락의 단점에 대해서는 해당 링크에서 확인했었는데, 우리 프로젝트는 팀에 가입하고 업무를 부여받는 식이기 때문에 충돌할 가능성이 낮다고 판단했고 낙관적 락을 사용했었다.
해당 Test코드는 낙관적 락을 적용하기 전과 같다. 100명을 가입신청 한다.
@Test
@DisplayName("5명 제한 팀에 100명이 동시에 가입 신청")
void joinTeamTest()throws InterruptedException{
//given
final int JOIN_USER = 100;
User user1 = createUser();
createTeam(user1.getId());
TeamJoinDto joinDto = TeamJoinDto.builder()
.joinCode("1111")
.build();
CountDownLatch countDownLatch = new CountDownLatch(JOIN_USER);
//101명을 한번에 넣어주기 위함
ExecutorService executorService = Executors.newFixedThreadPool(101);
//when
for (int i = 0; i < JOIN_USER; i++) {
User user2 = createUser();
executorService.submit(()->{
try{
teamService.joinTeam(user2.getId(),joinDto,1L);
} finally {
countDownLatch.countDown();
}
});
}
// 모든 작업이 완료될 때까지 대기
countDownLatch.await();
// then
// 5명이어야한다.
List<MemberEntity> members = memberRepository.findAll();
System.out.println(members.size());
assertThat(members.size()).isEqualTo(5);
}
5명 정원인데, 30명이 가입되었다.
JPA의 @Lock을 이용했다. TeamReposiotry에서 @Lock(LockModeType.PESSIMISTIC_WRITE)
을 통해 쿼리에 비관적 잠금을 적용한다. 이렇게 되면 해당 엔티티에 대해 다른 트랜잭션이 쓰기 잠금을 걸 수 없다.
@Repository
public interface TeamReposiotry extends JpaRepository<TeamEntity, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select t " +
"from TeamEntity t " +
"where t.id =:teamId")
Optional<TeamEntity> findByIdWithPessimisticLock(@Param("teamId") Long teamId);
}
현재는 팀을 조회하고 5명이 넘었는지 확인하는 과정에 있다.
따라서 JPQL을 위처럼 작성했다.
이제 joinTeam 메서드에서 팀을 조회할 때 findByIdWithPessimisticLock
를 사용한다. 팀을 조회할 때 기존의 findById를 주석처리
다섯명이 정상적으로 가입되었다.다음 시간에는 분산락을 알아보고, 적용해보는 시간을 가지려 한다. 그리고 여태 적용해봤던 락들이 실제 실행시간을 비교해보겠다.