- [트러블 슈팅] 다중 인스턴스일 때 동시성 문제 해결2026년 03월 08일 09시 41분 06초에 업로드 된 글입니다.작성자: do_hyuk728x90반응형
기존 코드
// 동일 JVM 내부에서 같은 Flow ARN에 대한 describe→start/stop 경쟁을 차단 private static final ConcurrentHashMap<String, ReentrantLock> FLOW_LOCKS = new ConcurrentHashMap<>(); private static ReentrantLock lockForArn(String arn) { return FLOW_LOCKS.computeIfAbsent(arn, k -> new ReentrantLock()); } public String updateStartStatus(String arn) { ReentrantLock lock = lockForArn(arn); lock.lock(); try { channel = aws.describe(arn); status = channel.status(); if (status == IDLE) { // AWS channel 실행 요청(1~2분 소요) // IDLE -> STARTING -> RUNNING 상태 변경 순서 channel.start() } throw new Exception(); } finally { lock.unlock(); } return null; }ReentrantLock을 사용한 이유
해당 로직은 스케줄러를 통해 여러 스레드에서 접근하는 메서드인데 동시에 같은 arn으로 접근할 경우
[첫 번째 요청] aws 상태 = IDLE if 접근 후 채널 시작 요청 [두 번째 요청] aws 상태 = IDLE if 접근 후 채널 시작 요청(ERROR 발생) -> 이미 starting 상태라서위와 같은 동시성 문제를 방지하고자 해당 Lock을 사용 ReentrantLock은 JVM 내부에서만 동작하는 프로세스 락이기 때문에 여러 스레드가 접근하는 것을 제어할 수 있다.
문제 상황
테스트 서버는 단일 인스턴스이기 때문에 ReentrantLock을 사용할 수 있었음 하지만 개발/운영 서버는 다중 인스턴스 상황인 것을 알게 되어 해당 Lock 방식은 사용 못하게 됨
다중 인스턴스 환경일 경우 Lock 사용방법
보통은 Redis를 통해 분산 락 방식을 사용하는데 기존에 Redis를 쓰고 있었다면 고려해 볼 만한 상황이지만
이 문제를 해결하기 위해 새로 추가해야 했기 때문에 생각조차 하지 않음
해결
조건부 해결 방법이지만 다중 인스턴스지만 DB를 한 개만 사용하기 때문에 가능한 방법이다.
// 해당 arn을 기준으로 DB에서 상태값을 IDLE -> STARTING 으로 변경해주는 쿼리 추가 int count = service.updateChannelState(arn, STARTING); if (count == 0) { // 다른 서버가 이미 선점함 -> 스킵 log.info("이미 시작중임 ㅇㅇ") return arn; } // count == 1 인 서버만 도달 channel.start()이미 DB의 상태값이 STARTING일 경우 변경된 개수가 0개 이기 때문에 count가 0이 되어 start 요청을 보내지 않게 한다.
728x90반응형'Spring' 카테고리의 다른 글
Spring Cache 사용기록 (0) 2026.02.22 [트러블 슈팅] JPA UPDATE 벌크 연산 후 조회 안됨 (0) 2026.02.01 [트러블 슈팅] 재배포 없이 스케줄링 날짜 변경하기 (0) 2026.01.31 [트러블 슈팅] JWT 유효기간보다 빨리 만료되는 문제 해결 (0) 2026.01.11 Chained Transaction Manager deprecated (0) 2026.01.04 댓글