레디스 스핀락을 적용해서 동시성 제어를 진행중입니다.

문제 상황

스핀락을 통해 동시성 제어를 시도했으나, 실패.

10대 주차 가능한 주차장에 20개의 스레드가 동시에 입차를 요청시 15~19대의 차량이 입차에 성공

문제의 원인 분석

CPU과부하로 인한 redis 성능 저하로 동시성 처리 오류 → 20개의 스레드밖에 안보내서 아닐 것 같음

해결 과정

  1. 더 작은 수의 스레드로 요청을 보내봤으나 (2대의 주차장에 5개의 스레드 동시 요청시 3대 입차) 똑같은 문제가 발생. → 락 획득 실패시 sleep 시간이 짧은가 싶어서 sleep 시간을 동적으로 더 늘려봤으나 여전히 문제가 발생했습니다.
  2. 서비스 레이어에서 바로 스핀락을 사용하게 되면 테스트 코드에 실패합니다. 그 이유는 락 해제 시점과 트랜잭션 커밋 시점의 불일치 때문입니다. 서비스 단의 enter 메서드에는 @Transactional 어노테이션이 붙어 있습니다. 때문에 스프링 AOP를 통해 enter 메서드 바깥으로 트랜잭션을 처리하는 프록시가 동작하게 됩니다. 반면 락 획득과 해제는 enter 메서드 내부에서 일어납니다. 때문에 스레드 1과 스레드 2가 경합한다면 스레드 1의 락이 해제되고 트랜잭션 커밋이 되는 사이에 스레드 2가 락을 획득하게 되는 상황이 발생합니다. 데이터베이스 상으로 락이 존재하지 않기 때문에 스레드 2는 데이터를 읽어오게 되고, 스레드 1의 변경 내용은 유실됩니다. 때문에 락 범위가 트랜잭션 범위보다 크도록 만들어 해결했습니다.

추가 문제 ->

위와같이 코드를 작성시 enter내부 메서드 enterLogic을 실행할때 Transaction이 실행되지 않고 무시되는것을 확인 할 수 있습니다. 그 이유는 **@Transactional**은 Spring의 AOP를 이용하여 동작하는데 AOP는 프록시를 이용하여 메서드를 호출하며, 빈 인스턴스를 생성하는것으로 시작합니다. 따라서 **enter**메서드에서 **enterLogic**메서드를 호출하더라도, AOP가 적용되지 않으면 **@Transactional**이 동작하지 않습니다.

따라서 enterLogic 메서드를 호출할때 별도의 트랜잭션 핸들러 클래스를 만들어 프록시 호출을 가능하게 만들었으며, **@Transactional**이 정상 작동 할 수 있게 해주어야 합니다.

Untitled

해결 여부

  1. 해결 완료