예약, 일반, 공통 구역을 나누고 매시간 스케쥴링을 통해 각 구역별로 적절하게 배치되도록 구현했습니다.
기존 예약, 일반, 공통으로 나뉘었던 주차장 내 구획을 없애고 park_booking_by_hour 테이블을 추가로 만들어 Available컬럼 값을 통해 해당 시간에 예약이나 입차가 가능한지 체크하는 방식으로 수정했습니다.
public <T> T runOnLock(Long key, Supplier<T> task) {
while (true) {
if (!lock(key)) {
try {
log.info("락 획득 실패");
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CustomException(ErrorType.FAILED_TO_ACQUIRE_LOCK);
}
} else {
log.info("락 획득 성공, lock number : {}", key);
break;
}
}
try {
return task.get();
} finally {
// Lock 해제
unlock(key);
}
}
public CarInResponseDto enter(CarNumRequestDto requestDto, Admin admin) {
return redisLockRepository.runOnLock(
requestDto.getParkId(),
() -> enterLogic(requestDto, admin)
);
}
위 코드 enter(requestDto, admin)에서 enterLogic은 @Transactional 어노테이션이 붙어 있으나, Transactional은 적용되지 않는다.
enter
메서드 들어올 때 mgtService
프록시 빈으로 들어오고, 레디스 락을 잡고 enterLogic
수행하는 부분은 프록시 빈으로 들어오는게 아니라, 이미 프록시 빈으로 들어와 있던 mgtService
의 메서드를 호출한 것 → enterLogic
에 있는 모든 AOP가 무시된다.private final MgtService mgtServiceSelf;
public CarInResponseDto enter(CarNumRequestDto requestDto, Admin admin) {
return redisLockRepository.runOnLock(
requestDto.getParkId(),
() -> mgtServiceSelf.enterLogic(requestDto, admin)
);
}