국면 전환 자동 종료 기능
종료 조건을 확인해 국면 전환을 자동 종료하는 스케줄러
아키텍처
개요
- 멀티 인스턴스(서버 4대) 환경에서 10초마다 종료 조건을 확인하는 국면 전환 종료 스케줄러
문제
- 운영 중 국면 전환이 자동 종료되지 않아 WebSocket 로그를 확인하니 종료 상태 알림에 Y와 N이 동시에 도착
- 서버 4대가 10초마다 국면 전환 종료 스케줄러를 동시에 실행하며 Race Condition 발생
해결 전략
- Redis 분산 락으로 인스턴스 1대만 종료 로직을 수행하도록 보장
- 커스텀 어노테이션과 AOP로 락 획득과 해제를 모듈화하고 finally에서 해제
기술 선택 이유
-
스케줄러 전용 인스턴스 1대 지정 - 제외
- 가장 단순하지만, 그 인스턴스가 내려가면 종료 스케줄러가 통째로 멈춤
-
Redis 분산 락 - 채택
- 서버 4대가 공유하는 실행 제어 지점 필요
- 이미 운영 중인 Redis라 별도 인프라를 추가하지 않고 DB 락 없이 스케줄러 실행만 제어
- 프로세스가 종료돼도 락이 남지 않도록 TTL 10초를 함께 설정해 자동 만료
-
AOP - 채택
- 반복되는 락 획득과 해제를 모듈화해 신규 스케줄러에도 어노테이션으로 동일 정책 적용
검증
- 동일 Redis를 공유하는 API 인스턴스 4개가 종료 가능한 국면 전환 1건을 두고 국면 전환 종료 스케줄러를 동시 실행
동시 실행 결과
| 지표 | 분산 락 미적용 | Redis 분산 락 적용 |
|---|---|---|
| 총 스케줄러 실행 요청 | 4회 | 4회 |
| 종료 로직 진입 | 4회 | 1회 |
| 국면 종료 로그 | 4건 | 1건 |
| 종료 상태 알림 | 4건 | 1건 |
- 락을 얻지 못한 3개 요청은 Redis 단계에서 종료. 종료 로직과 알림은 락을 얻은 1개 인스턴스만 수행
회고
- 종료 로직이 10초를 넘겨 새 인스턴스가 락을 획득한 경우, 이전 인스턴스가 새 락까지 해제할 가능성이 있음
- TTL은 스케줄러 주기와 같은 10초로 설정. 종료 로직이 단순해 10초 안에 끝난다고 판단했을 뿐, GC나 네트워크 지연으로 늦어질 가능성은 고려하지 못함
- 다시 진행한다면 락을 획득한 인스턴스만 해제하도록 보완하고 종료 로직 수행 시간의 p95와 p99, 최댓값을 측정해 TTL을 결정해야 함