Skip to content

ADR-01: 스케줄 기반 재수집 아키텍처

🇺🇸 English Version

날짜작성자리포지토리
2024-12-18@KubrickCodecollector

배경

응답 시간 문제

ADR-05에서 초기 분석 요청을 위한 큐 기반 비동기 처리를 확립함. 이는 장시간 실행 작업 문제를 해결하지만, 지연이 발생함: 이전에 분석한 저장소를 요청할 때도 큐 처리를 기다려야 함.

저장소가 미리 분석되어 최신 상태로 유지되면, 사용자는 분석 대기 없이 즉각적인 응답을 받음.

사용자 경험 영향

시나리오온디맨드만사전 수집 적용
첫 방문큐 대기 (예상됨)큐 대기 (예상됨)
재방문 (신선)캐시에서 즉시캐시에서 즉시
재방문 (오래됨)다시 큐 대기즉시 (사전 갱신됨)
인기 저장소큐 대기즉시 (사전 캐시 가능성 높음)

핵심 통찰: 대부분의 사용자 요청은 이전에 분석한 저장소에 대한 것임. 사전 수집은 대다수 요청에서 큐 대기 시간을 제거함.

부가적 이점: 데이터 신선도

응답 시간 외에도, 사전 수집은 데이터 노후화도 해결함 (의존성 업데이트, 보안 패치, 코드 리팩토링).

핵심 요구사항

  1. 자동 업데이트: 이전에 수집한 저장소를 주기적으로 재분석
  2. 리소스 효율성: 비활성 저장소의 불필요한 재수집 방지
  3. 분산 환경 안전성: 다중 인스턴스 배포에서 중복 실행 방지
  4. 우아한 성능 저하: 연쇄 효과 없이 장애 처리

결정

적응형 감쇠 로직을 갖춘 스케줄러 기반 재수집 시스템 채택

핵심 원칙:

  1. 적응형 갱신 간격: 사용자 활동 기반 감쇠 알고리즘
  2. 분산 락: 단일 실행 보장을 위한 PostgreSQL 기반 락
  3. 서비스 분리: Scheduler가 Worker와 독립적으로 실행
  4. 서킷 브레이커: 연속 실패 시 자동 중단

검토한 옵션

옵션 A: 적응형 감쇠를 갖춘 스케줄러 (선택)

작동 방식:

  • Cron 작업이 주기적으로 트리거
  • Scheduler가 분산 락을 획득하여 중복 실행 방지
  • 후보 조회: 설정된 기간 내 조회된 저장소
  • 감쇠 알고리즘 적용: 최근 활동 → 더 빈번한 갱신
  • 자격 있는 저장소를 작업 큐에 등록

감쇠 알고리즘 개념:

  • 최근 조회된 저장소는 더 자주 갱신
  • 유휴 시간이 증가하면 갱신 간격도 증가
  • 임계값을 초과하면 유휴 상태로 간주하고 제외

장점:

  • 실제 사용자 활동에 기반한 리소스 최적화
  • 활성 저장소의 데이터 신선도 유지
  • 방치된 저장소의 자동 갱신 중단
  • 연속 실패 추적을 통한 장애 격리

단점:

  • 간격 계산의 복잡한 로직
  • 사용자 조회 타임스탬프 추적 필요
  • 기준 임계값이 일부 사용 사례에서 너무 공격적일 수 있음

옵션 B: 고정 간격 갱신

  • 활동과 무관하게 모든 저장소를 N시간마다 갱신
  • 단순하지만 비활성 저장소에 리소스 낭비

옵션 C: 이벤트 기반 갱신

  • 외부 이벤트(GitHub 웹훅)로 재수집 트리거
  • 실시간이지만 웹훅 인프라 및 접근 권한 필요

구현 고려사항

서비스 아키텍처

컴포넌트스케일링 전략
Worker큐 깊이 기반 수평 확장
Scheduler단일 활성 인스턴스 (락 보호)

분리 근거:

  • Worker 스케일링이 불필요한 스케줄러를 생성하지 않음
  • Scheduler 변경이 Worker 재배포를 요구하지 않음
  • Blue-green 배포가 분산 락으로 안전 유지

Private 저장소 처리

설계 결정: 스키마 필터링이 아닌 런타임 검증

저장소 가시성은 데이터베이스에 저장하지 않음. 언제든 변경될 수 있기 때문임 (public↔private). 대신 clone 시점에 런타임으로 검증함.

이 접근법의 이유:

관심사해결책
토큰 관리Scheduler가 사용자 토큰 없이 동작
가시성 변경오래된 가시성 플래그 유지 불필요
보안동의 없이 백그라운드에서 private 코드 접근 안함
단순성추가 스키마나 동기화 로직 불필요

동작 방식:

  • Scheduler가 모든 자격 후보를 큐에 등록 (가시성 필터 없음)
  • Worker가 비인증 clone 시도
  • Private 저장소는 자연스럽게 실패
  • 연속 실패 누적 → 결국 제외

참고: 저장된 사용자 토큰으로 private 저장소 재수집이 기술적으로는 가능함. 의도적으로 제외한 이유:

  • 토큰 만료/취소 처리 복잡성
  • 사용자가 저장소 접근 권한을 잃었을 수 있음 (조직 퇴사, 권한 취소)
  • 프라이버시 우려: 명시적 사용자 행동 없이 백그라운드 접근
  • 사용자의 GitHub 쿼터에 대한 Rate Limit 소비

에러 처리 전략

서킷 브레이커 패턴:

  • Scheduler 수준: 연속 큐 등록 실패 시 배치 중단
  • 저장소 수준: 연속 분석 실패 시 자동 갱신에서 제외
  • 복구: 다음 주기에 새로 시작; 수동 재분석이 카운터 초기화

중복 방지

  • 고유 윈도우가 설정된 기간 내 중복 등록 방지
  • cron 지터 및 수동 등록 중복 처리

결과

긍정적

리소스 효율성:

  • 활성 저장소는 빈번한 업데이트
  • 비활성 저장소는 리소스 제로 소비
  • 감쇠 알고리즘이 자연스럽게 배치 크기 제한

시스템 안정성:

  • 분산 락이 단일 스케줄러 실행 보장
  • 개별 저장소 실패가 다른 저장소에 영향 없음
  • 일시적 장애는 큐 메커니즘으로 재시도

운영 단순성:

  • 모니터링할 단일 스케줄러 인스턴스
  • 실패 카운터를 통한 명확한 장애 신호

부정적

복잡도:

  • 감쇠 알고리즘이 세심한 튜닝 필요
  • 분산 락이 PostgreSQL 운영 의존성 추가
  • 추적하고 이해할 다중 실패 카운터

제한사항:

  • cron 간격에 의해 제한되는 최소 단위
  • 하드 기준이 장기 비활성 후 복귀하는 사용자를 놓칠 수 있음
  • 락 TTL이 최대 배치 처리 시간 제한

참고 자료

Open-source test coverage insights