1. Optimistic Locking in JPA
[1] 동시성 제어를 위한 노력
- 엔터프라이즈 수준에서 DB의 동시성 제어를 관리하는 것이 중요하다. 효과적으로 오류 방지가 가능한 multiple transaction을 핸들링할 수 있어야 한다.
- 대표적인 예시 중, read 와 update 사이의 일관성을 보장해야 한다는 것이 상당히 중요하다.
[2] Optmistic Lock In JPA
- JPA에서 낙관적 락을 사용하기 위해서는 엔티티에 버전을 명시하는 @Version 어노테이션을 추가해야 한다.
- Lock Mode에는 OPTIMISTIC과 OPTIMISTIC_FORCE_INCREMENT 두 종류가 있다.
- OPTIMISTIC: 버전 속성을 포함하는 모든 엔터티에 대해 낙관적 읽기 잠금을 얻는다.
- READ는 OPTIMISTIC과 같은 용도로 사용하지만 OPTIMISTIC을 선호한다.
- OPTIMISTIC 으로 요청할 경우, persistence provider에서 dirty read와 non-repeatable read를 보호해준다.
- OPTIMISTIC_FORCE_INCREMENT는 OPTIMISTIC과 같지만 추가적으로 버전을 증가시킨다.
- WRITE와 동의어지만 OPTIMISTIC_FORCE_INCREMENT을 사용해야 좋다.
- OPTIMISTIC_INCREMENT을 사용할 경우 persistence provider가 OPTIMISTIC_INCREMENT 기능을 아는 것이 좋기 때문이다 (persistence provider는 스키마 생성을 위한 EntityManagerFactory를 생성하는 persistence class 이므로 엔티티매니저에서 커밋 또는 플러시 시점을 연기하는지에 대해 지정될 수 있는 장점이 있다.
- OPTIMISTIC: 버전 속성을 포함하는 모든 엔터티에 대해 낙관적 읽기 잠금을 얻는다.
[3] Optimistic Locking
(1) EntityManager
eneityManager.find(Student.class, studentId, LockModeType.OPTMISTIC);
(2) Query
Query query = entityManager.createQuery("from Student where id = :id");
query.setParameter("id", studentId);
query.setLockMode(LockModeType.OPTIMISTIC_INCREMENT);
query.getResultList()
[4] OptimisticLockException
Persistence Provider가 엔티티의 Optimistic Lock 충돌을 감지하면 OptimisticLockException을 발생시킨다. 이 예외는 충돌하는 엔티티에 대한 참조가 포함되어 있다.(원인 예외에 관한 정보를 제공해주는 것으로 이해했다.) 하지만 PersistenceProvider가 모든 상황에 대한 엔티티 참조를 포함하지 않으므로 OptimisticLockException에 관한 예외 핸들링 로직을 추가하는 것이 좋다.
[5] Conclusion
- OPTIMISTIC LOCK은 엔티티에 포함된 버전 속성을 이용해 엔티티의 동시성 문제를 해결할 수 있다. 그러므로 수정과 삭제로 인한 데이터 덮어쓰기 또는 삭제에 관한 문제를 보장할 수 있다.
- 대조적으로 PESSIMISTIC LOCK은 DB LEVEL에서 엔티티 잠금을 거는 것이 아니다. 그러므로 DB 교착상태에 취약하지 않다.
2. Pessimistic Lock In JPA
[1] 동시성 제어를 위한 노력
- 동시성을 제어하기 위해서는 적절한 트랜잭션 격리 레벨 또는 데이터 잠금 설정에 대해 고민해볼 수 있다. 트랜잭션 격리 레벨은 데이터베이스 커넥션에 관해 정의된다. 하지만 격리 레벨은 커넥션이 생성되면 한번만 설정할 수 있다. 그리고 커넥션 내에 모든 명령문에 영향을 미친다. 트랜잭션이 가지는 한계를 비관적 락을 통해 해결할 수 있다. 비관적 락은 데이터의 배타적 접근을 세분화하여 회득할 수 있는 데이터베이스 메커니즘이다.
- 우리가 획득할 수 있는 락의 종류에는 Shared Lock 과 Exlusive Lock이 있다. Shared Lock은 다른 사람이 락을 획득 했을 때 데이터를 조회할 수 있지만 쓸 수는 없다. 데이터를 수정하거나 삭제하기 위해서는 배타적 잠금이 필요하다.
[2] Lock Modes
@Lock(LockModeType.PESSIMISTIC_READ)
public Optional<Customer> findById(Long customerId);
- JPA 명세는 3가지 비관적 락을 정의하고 있다.
- PESSIMISTIC_READ : Shared Lock을 획득해 다른 사용자의 수정, 삭제를 막는다.
- PESSIMISTIC_WRITE : Exclusive Lock을 획득해 다른 사용자의 조회, 수정, 삭제를 막는다.
- PESSIMISTIC_FORCE_INCREMENT : PESSIMISTIC_WRITE 처럼 동작하고 추가적으로 Entity에 version에 관한 속성이 있을 경우, 추가한다.
- 이 모두는 LockModeType 클래스의 정적 멤버이며 트랜잭션이 DB 잠금을 얻을 수 있도록 하며 커밋, 롤백이 될 때까지 유지되는 락이다. 그리고 한 번에 하나의 잠금만 획득할 수 있으며 불가능 할 경우, PersistenceException이 발생한다.
- Pessimistic Lock에 관한 예외는 아래와 같다.
- PessimisticLockException : 락을 획득하거가 Shared Lock을 Exclusive Lock을 변환하는데 실패하는 경우, 트랜잭션 수준(transaction-level)의 롤백이 발생한다.
- LockTimeoutException : 잠금을 획득하거나 Shared Lock을 Exclusive Lock으로 변환하는데 시간을 초과하는 경우 명령문 수준(statement-level)의 롤백이 발생한다.
- PersitenceException : 영속성 문제가 발생하는 경우 예외를 반환한다. PersistenceException의 하위 예외인 NoResultExceptoin, NoUniqueResultException, LockTimeoutException, QueryTimeoutException를 제외한 예외는 활성 트랜잭션을 롤백하도록 표시한다.
[3] Lock Scope
@Lock(LockModeType.PESSIMISTIC_READ)
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "3000")})
public Optional<Customer> findById(Long customerId);
- Lock Scope parameter는 락을 획득한 엔티티의 연관관계의 잠금을 어떻게 다룰지에 관해 정의하는 파라미터이다.
- PersimisticLockScope.NORMAL : LockScope의 기본 값이며, 해당 엔티티에만 잠금을 획득한다. 만약 조인 상속을 사용할 경우, 조상도 잠금 대상에 포함된다.
- PersimisticLockScope.EXTENDED : NORMAL의 기능 뿐만 아니라 조인 테이블에서 관련 엔티티를 함께 블록할 수 있다. (e.g. @ElementCollection, @OneToOne, @OneToMany, @JoinTable)
[4] Lock Timeout
- TimeOut 설정을 통해 잠금을 얻기 위한 대기 시간을 설정할 수 있다. 대기 시간의 단위는 ms이다.
- TimeOut 설정을 지원하지 않는 DatabasDriver가 존재할 수 있음을 명심해야 한다.
References
'spring > summary' 카테고리의 다른 글
ShedLock 으로 하나의 스케줄링만 실행하기 (0) | 2025.01.16 |
---|---|
Spring REST Docs + Swagger UI 를 통한 API 문서화 (1) | 2024.12.07 |
MessageSourceAutoConfiguration 코드 검토하기 (0) | 2024.11.28 |
[kurly tech blog] Redisson, Spring AOP 기반 분산락 적용 방법 summary (0) | 2024.11.26 |
Spring Batch 기반 회원 삭제 배치 삽질 기록 (0) | 2024.09.03 |