오프라인 선점 잠금 방식은 누군가 수정 화면을 보고 있을 때 수정 화면 자체를 실행하지 못하게 막는다.
엄격하게 데이터 충돌을 막기 위해 누군가 수정 화면을 보고 있을 때 수정 화면 자체를 실행하지 못하게 하는 것이 오프라인 선점 잠금 방식이다.
한 트랜잭션 범위에서만 적용되는 선점 잠금 방식이나 나중에 버전 충돌을 확인하는 비선점 잠금 방식으로는 구현할 수 없다.
만약 이런 상황에서 사용자 A가 수정 요청을 수행하지 않는다면 잠금이 해제되지 않으므로 잠금 유효 시간을 가져야 한다.
잠금 유효 시간이 지나면 잠금을 해제해 다른 사용자가 잠금을 다시 구할 수 있도록 해야 한다.
하지만 유효 시간이 지난 뒤 얼마 되지 않아 수정 요청을 수행한다면 실패하게 된다.
이를 방지하기 위해 일정 주기로 유효 시간을 증가시키는 방식이 필요하다.
- 잠금 선점 시도
- 잠금 확인
- 잠금 해제
- 잠금 유효시간 연장
public interface LockManager {
LockId tryLock(String type, String id) throws LockException; // 잠금 선점 시도
void checkLock(LockId lockId) throws LockException; // 잠금 확인
void releaseLock(LockId lockId) throws LockException; // 잠금 해제
void extendLockExpiration(LockId lockId, long inc) throws LockException; // 락 유효 시간 연장
}
잠금을 선점한 이후에 실행하는 기능은 다음과 같은 상황을 고려해 잠금이 유효한지 확인해야 한다.
- 잠금의 유효 시간이 지났으면 이마 다른 사용자가 잠금을 선점한다.
- 잠금을 선점하지 않은 사용자가 기능을 실행했다면 기능 실행을 막아야한다.
잠금 정보를 저장하기 위한 테이블 생성 쿼리
create table locks (
`type` varchar(255),
id varchar(255),
lockid varchar(255),
expiration_time datetime,
primary key (`type`, id)
) character set utf8;
create unique index locks_idx ON locks (lockid);
확실히 선점 잠금과 비선점 잠금에 비해 확실하게 충돌을 방지할 수 있을 것 같다고 생각한다.
하지만 사용자 입장에서는 좋은 경험이 되지는 않을 것 같다.
오프라인 선점 방식은 데이터의 일관성이 아주 아주 중요한 상황에서 사용하면 좋을 것 같다.