WORK EXPERIENCE

국면 전환 자동 종료 기능

종료 조건을 확인해 국면 전환을 자동 종료하는 스케줄러

Java, Spring, Redis

아키텍처

인스턴스 A스케줄러 실행 중인스턴스 B스케줄러 실행 중인스턴스 C스케줄러 실행 중인스턴스 D스케줄러 실행 중Redis 분산 락단일 실행락 획득 인스턴스종료 로직 수행

개요

  • 멀티 인스턴스(서버 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을 결정해야 함