ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링부트] @DataJpaTest 멀티스레드에서 no value present
    BackEnd/SpringBoot 2023. 12. 6. 18:16

     

     

    비관적 락 테스트를 하기 위해서 멀티 스레드 코드를 테스트하였다.

     

    문제 환경

     

    0. 테스트용 메모리 디비를 사용한다.

    1. EntityManger도 @PersistenceContext로 팩토리를 통해서 tread-safe하도록 생성했다.

    2. @DataJpaTest 사용하여 테스트

     

    테스트 시나리오

     

    1. 테스트 전에 이미 더미 데이터를 넣고 시작한다.

    2. entitymanager로 flush()와 clear()를 사용하여 커밋과 1차캐시를 삭제한다.

    3. 멀티 스레드 테스트 코드를 실행 한다.

     

    1. 문제의 테스트 코드

    2. 스레드 내부 메서드

    foundUser에서 no value present가 발생한다.

     

     

    오류를 찾을수가 없어서 log를 확인했다.

    application-test.yaml에 로그를 확인 할 수 있도록 추가한다.

     

    logging:
      level:
        root: debug
        org:
          hibernate.orm.jdbc.bind: trace # 바인딩 파라미터 확인

     

    transcation의 open 확인과 로그 확인시 정상적으로 flush()와 clear()는 확인했다.

     

     

    멀티 스레드 메서드 내부 로그를 보면 이상한 점이 있다.

    아직 회원가입때 사용한 트랜잭션이 살아있고 기존 트랜잭션에 참여하는 것을 확인 된다.

    이 로그로 확인 했을때는 아직 db에 반영이되지 않았다는 것이라고 확인된다.

     

     

    DataJpaTest 사용시 @Transaction이 존재하는데 하나의 큰 트랙잭션에 다른 트랜잭션을 참여하도록 (required) 했기 때문에 적용이 되지 않았다.

     

     

    3. 해결 방안

    1. 트랜잭션을 분리해서 커밋하게 만든다.

    2. 아래와 같은 기존 트랜잭션과 상관없이 동작하도록 한다.

     

     

    4. flush는 트랜잭션을 커밋하는게 아니다.

    flush는 쿼리를 작성하는 것과 같고 -> 트랜잭션 커밋 X

    clear는 1차 캐시를 비우는 것이다.

    flush에서 로그에 쿼리가 보인다고 해서 커밋이 된것이 아니다.

    5. 멀티 스레드 외부에서 조회

    추가적으로 

    멀티스레드 외부에서는 조회가 되었는데 이유는 엔티티 매니저가 싱글턴으로 (엔티티 팩토리에서) 생성 되는데 각 스레드의 공간을 보장하기 때문이다.

    메인 스레드에서 회원가입을 진행했기 때문에 해당 스레드에서는 조회가 되었다.

    멀티 스레드에서 보장하는 것의 한편이라고 확인할 수 있다.

    entitymanger의 thread safety에 대한 검증 테스트가 된 것 같다.

     

    댓글

Designed by Tistory.