낙관적 락을 이용한 동시성 문제 해결하기

BlackHan·2024년 6월 8일
0

동시성

목록 보기
4/5
post-custom-banner

진행상황

지난 시간에 동시성 제어데드락에 대해서 알아봤다.
이번에는 프로젝트에 동시성 처리를 적용해본다.

대략 서비스를 설명하자면, 먼저 회원 가입을 하고 팀에 가입한다. 팀에 가입하기 위해서는 Join코드가 필요하고 '1111' 로 세팅했다.

팀 인원이 5명 제한이 걸려있는데, 100명을 동시에 가입시켜보려 한다.

동시성 처리 전 Code

	@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명은커녕 29명이 가입된 것을 알 수 있다. 제작한 프로젝트는 팀의 인원수를 제한하고 인원수 증설에 따른 결제를 필요로하게했지만, 이런 오류가 발생하면 치명적일 수 있다. 앞서 배운 동시성 제어가 필요해보인다.

이런 오류가 이커머스나 선착순 타임세일 같은 긴박한 프로젝트였을 경우 문제는 더 커지겠지만, 우리 프로젝트는 그저 팀에 가입하고 업무를 부여받는 식이기 때문에 충돌할 가능성이 낮다고 판단했고 낙관적 락을 사용해보기로 했다.

동시성 처리

낙관적 락 개념은 지난 시간에 학습했다.
낙관적 락은 JPA에서 지원해주기에 구현이 간단하다. 먼저 아래를 Entity에 추가한다.
팀에 가입하는 단계에 버전을 조회해야 한다.
즉, joinTeam 메소드를 수정해야 한다.

테스트 코드에서는 변한 게 없다. 테스트 코드에서 체크한 부분의 joinTeam메서드가 수정되어야 한다. 팀을 조회할 때 findById를 주석처리하고 findByIdWithOptimisticLock 를 사용했다.

findByIdWithOptimisticLock는 TeamReposiotry에서 아래처럼 간단하게 작성한다.

@Repository
public interface TeamReposiotry extends JpaRepository<TeamEntity, Long> {
    @Lock(value = LockModeType.OPTIMISTIC)
    @Query("select t " +
            "from TeamEntity t " +
            "where t.id =:teamId")
    Optional<TeamEntity> findByIdWithOptimisticLock(@Param("teamId") Long teamId);

현재는 팀을 조회하고 5명이 넘었는지 확인하는 과정에 있다.
따라서 JPQL을 위처럼 작성했다.

결과

정상적으로 5명이 가입된 것을 알 수 있다.
다음 시간에는 비관적 락을 사용해서 처리해본다.

profile
Slow-starter
post-custom-banner

0개의 댓글