비관적 락(Pessimistic Lock)
- 레코드 단위 테스트 환경 구성
- Mysql 콘솔을 세개 띄운다.
- 첫번째 콘솔은 락 정보를 조회하는 용도로 사용한다.
SELECT * FROM performance_schema.data_locks;
- 두번째 콘솔에는 레코드 단위로 비관적 락을 설정하는 용도로 사용한다.
set autocommit = false;select * from T_API where API_ID = 1000 for update;commit;rollback;
- 세번째 콘솔은 락이 걸린 레코드에 추가로 비관적 락을 설정하는 용도로 사용한다.
set autocommit = false;select * from T_API where API_ID = 1000 for update;
- 두번째와 세번째 콘셀에서 오토커밋 설정을 끈다.
set autocommit = false;
- 첫번째 콘솔의 쿼리를 실행해서 락 데이터가 존재하는지 확인한다.
SELECT * FROM performance_schema.data_locks;
- 락 데이터가 조회되는 경우 해당 트랜잭션을 찾아서 락을 제거한다.
commit;rollback;
- 비관적 락 테스트
- 일반 select 구문의 마지막에 “for update” 구문을 붙여주는 것으로 비관적 락을 생성할 수 있다.
- 두번째 콘솔에서 select … for update 쿼리를 실행하여 레코드 단위의 비관적 락을 생성한다.
- 첫번째 콘솔에서 락을 조회했을 때, 두 건의 레코드가 생성된 것을 확인한다.
- 세번째 콘솔에서 동일한 쿼리를 실행했을 때,
락이 풀릴때까지 대기하는것을 확인할 수 있다. (대기하다가 타임아웃에 걸린다) - 두번째 콘솔에서 커밋하거나 롤백한다.
- 첫번째 콘솔의 쿼리를 실행했을 때 락이 삭제된 것을 볼 수 있다.
- 세번째 콘솔의 쿼리를 실행하면, 새로운 락이 생성된 것을 확인할 수 있다.
- 백앤드 애플리케이션에서 레코드 단위의 비관적 락을 적용
- API 호출이 완료되는 시점에 커밋된다.
- 오류 발생시 롤백되도록 설정한다.
- 서비스 메소드에 try catch 구문을 사용해서 익셉션이 발생했을 익셉션을 던지도록 한다.
- Transaction 어노테이션에 rollbackFor 속성을 추가한다.
- 아래 예제에서는 BizException이 발생했을때 롤백되도록 설정되어 있다.
@Transactional(rollbackFor = BizException.class)public void insertBookmark(final int menuId) {try {final User sessionUser = ServletUtil.getSessionUser();final String remoteAddr = RemoteAddr.getRequestIpAddress();final CommonBookmarkContext context = CommonBookmarkContext.builder().userId(Objects.requireNonNull(sessionUser).getUserId()).menuId(menuId).createIp(remoteAddr).updateIp(remoteAddr).build();commonBookmarkMapper.insertBookmark(context);} catch (final Exception e) {throw new BizException(e);}}@Transactional(rollbackFor = [BizException::class])fun insertApi(param: Api) {try {return apiMapper.insertApi(param)} catch (e: Exception) {throw BizException(e)}} - 모든 경우에 커밋/롤백 작업이 실행되기 때문에 레코드 단위의 비판적 락을 적용할 수 있다.