본문 바로가기

개발이야기

JPA 업데이트 동시성 문제 (feat. @DynamicUpdate)

반응형

JPA 업데이트 동시성 문제 (feat. @DynamicUpdate)  

최근에 담당하는 서비스에서 이상한 케이스가 발생해서 조금 자세히 확인을 해봤습니다.
비지니스 플로우는 아래와 같습니다.

1. Insert into table_A ...
2. update table_A set terms_agreed_at = now()
3. update table_A set token_bound_at = now()

1~3 번 까지 순차적으로 발생 해야 하는데 실제 DB에 쌓인 데이터는
1번(인서트)와 3번(업데이트)만 처리되고
2번이 누락되어 terms_agreed_at 칼럼이 null 로 남아 있는 경우가 종종 발생했네요.

서버 로그를 확인해보면 ..
2번 API 호출이 정상적으로 실행 되었음에도 실제 데이터는 누락 되는 경우가 발생했는데
2번과 3번이 거의 동시에 업데이트 처리 된것이 문제 였네요.

일반적으로 update 쿼리 실행시 row 락이 걸려서 2번과 3번이 동시에 실행 되는 일은 없겠지만
JPA를 사용하는 경우에는 2번과 3번이 동시에 1번 상태를 읽은 후 2번과 3번이 순차적으로 실행되지만
3번 업데이트 실행시에 2번 업데이트의 갱신 내용을 1번 상태로 덮어써 버리는 문제가 발생 합니다.

JPA 디폴트 설정으로 ...
업데이트 쿼리 호출시 변경된 칼럼에 대해서만 업데이트 하지 않고
모든 칼럼을 업데이트 하는 쿼리 구문이 생성 되는데 이게 문제의 핵심이네요

해결책으로는
Entity Class 의 상단에 하이버네이트가 제공하는 @DynamicUpdate 어노테이션을 선언하기만 하면 됩니다.
그러면 아래와 같이 변경이 발생한 칼럼에 대해서만 업데이트를 하게 됩니다.

동시성 업데이트 문제가 아니더라도 ...
불필요한 물리 DB의 부하를 줄이기 위해서라도 @DynamicUpdate 사용 여부를 다시 한번 확인 해서
모든 Entity에 선언을 해주면 좋을것 같습니다.

( 분산 환경에서 동시성 문제를 해결하려면 @Version 을 사용한 Optimistic lock을 사용하세요. 
이글은 단지 @DynamicUpdate를 소개하는 것일뿐 ..  )

반응형