제3자 도구가 갑자기 요청을 제한하면, 재시도가 왜 오히려 눈사태를 키우는가

← 전체 목차 · 이전: 다중 도구를 병렬 호출할 때, 대화 상태는 먼저 갱신하나 나중에 갱신하나 · 다음: 도구가 200을 반환해도, 그 안의 값이 틀릴 수 있다면 무엇을 검증해야 하는가

"타임아웃 → 재시도 → 실패 → 몇 초 대기 → 다시 시도"는 방어가 아니라 공격이 될 수 있다. 실패한 작업들이 마침 레이트 리밋 회복 구간에 동시에 재시도를 걸면, 막 회복 중이던 인터페이스를 순간적으로 다시 가득 채워 버린다. 방어가 다음 라운드 장애의 진원지로 뒤바뀌는 것이다.

레이트 리밋 눈사태의 다섯 가지 실패 시나리오

레이트 리밋 눈사태는 "도구가 에러를 반환하면 곧 장애"라는 단순한 그림이 아니라, 레이트 리밋이 트리거된 이후의 연쇄 반응으로 인해 Agent 서비스 전체가 불가용해지는 현상이다.

시나리오내용
순간 고동시성이 레이트 리밋을 트리거실패한 작업들이 동시에 재시도해 재시도 폭풍을 형성, 하류 도구를 다시 무너뜨림
등급별 제한의 파급일부 인터페이스만 제한됐는데 전체 작업 체인이 실패
다중 Agent 공유 쿼터 경쟁저우선 작업이 고우선 작업의 몫을 잠식
제한 식별자 부재시스템 장애를 레이트 리밋으로 오인해 반복 재시도
제한 해제 후 트래픽 돌발회복 직후 재차 레이트 리밋 트리거

토큰 버킷 쿼터 관리 + 서킷 브레이커 등급별 강등

agent_tool_circuit 테이블로 도구별 상태를 관리한다.

tool_id          도구 식별자
biz_type         업무 유형
token_bucket     토큰 버킷 (쿼터)
circuit_status   서킷 상태
degrade_level    강등 등급
priority_weight  우선순위 가중치
error_count      오류 카운트

실행 링크:

1. 도구 호출 전 → 토큰 버킷에서 쿼터를 먼저 취득, 취득해야만 호출 허용
2. 취득 실패 → 대기열에 진입, priority_weight 순으로 정렬
3. 서킷 브레이커가 오류율을 집계 → 임계값 도달 시 자동으로 서킷 상태 전환
4. 등급별로 강등 전략을 실행
제한 정도강등 동작
경도 제한자동으로 대체 도구로 전환
중도 제한캐시된 결과를 반환
중차단비핵심 작업을 직접 거부

서킷 브레이커 상태는 5단계로 흐른다.

정상 → 경계 → 차단 → 강등 → 반개방 회복

동시성 안전: 매 상태 변경은 "현재 오류율 + 시간 윈도우"를 기준으로 해야 하며, 데이터베이스 행 잠금으로 상태 유일성을 보장한다. 동시 상태 변경 시 영향 행 수가 0이면 상태 요동을 회피한 것으로 본다.

제한 식별자가 없을 때 — 오류 코드 클러스터링 + 응답 시간 적합 모델

도구 쪽에서 명확한 레이트 리밋 표시를 주지 않는 경우가 실제로는 흔하다. 단일 오류 코드에 의존하지 않고, 오류 코드 클러스터링 + 응답 시간 적합 모델로 제한 여부를 간접 추론한다.

프로덕션 실전 디테일

문제처리
데이터 일관성쿼터 관리와 도구 호출을 같은 스케줄 링크 안에서 처리
장기 저장서킷 기록은 장기 보존해 제한 전략을 지속 개선; 완료 후 7일 초과한 장애 기록은 이력 테이블로 이관, 단 이관 기간 동안 장애가 재발하지 않음을 보장
모니터링 지표제한 트리거율, 재시도 성공률, 서킷 지속 시간, 강등 작업 비중, 쿼터 사용률
자체 장애 시 강등토큰 버킷 서비스 자체가 불가용하면 "단일 머신 제한 + 핵심 작업 우선"으로 강등, 사후 트래픽 복기와 쿼터 교정 병행

핵심 통찰

  1. 재시도는 방어이자 동시에 공격이 될 수 있다 — 실패 작업이 레이트 리밋 회복 구간에 동시에 재시도하면, 막 회복 중인 인터페이스를 순간적으로 다시 가득 채운다. 재시도 방안의 좋고 나쁨을 가르는 기준은 "재시도가 있는가"가 아니라 "그 재시도가 다음 장애의 진원이 되는가"이다.
  2. 레이트 리밋 눈사태를 정의부터 바로잡아야 답이 맞는다 — 이를 "도구 에러"라는 점상 장애로 이해하면 해법은 catch 하나에 머문다. "제한 트리거 이후의 연쇄 반응이 서비스 전체를 불가용하게 만드는 것"이라고 범위를 다시 잡아야 방어가 체계가 된다.
  3. 제한에 등급이 있으면 강등에도 등급이 있어야 한다 — 조회 인터페이스는 살아있고 쓰기 인터페이스만 막히는 상황에서 "도구 전체가 불가용"이라는 이진 가정은 틀렸다. 경도-전환, 중도-캐시, 중차단-거부 세 단계가 이진 가정보다 훨씬 촘촘하다.
  4. 공유 쿼터 풀은 본질적으로 제한 문제가 아니라 스케줄링 문제다 — 다중 업무 라인이 하나의 쿼터를 공유할 때 저우선 작업이 고우선의 몫을 잠식하는 것은 "제한이 부실해서"가 아니라 "우선순위가 없어서"다. 그래서 방안에는 priority_weight와 "취득 실패 시 우선순위 대기열 진입"이 반드시 있어야 한다.
  5. 제한 상태는 약속이 아니라 추론으로 얻어야 할 때가 있다 — 도구 쪽이 명시적 제한 표시를 주지 않는 것은 일상이다. 오류 코드 클러스터링 + 응답 시간 적합 모델은 단일 신호가 아니라 통계적 특징에서 상태를 복원하는 방식이다.
  6. 서킷 브레이커의 본질은 동시성 유일성을 반드시 보장해야 하는 상태 머신이다 — 상태 전이 자체는 어렵지 않다. 어려운 것은 동시 접근에서의 상태 요동이다. "현재 오류율+시간 윈도우 기준 변경, 행 잠금으로 유일성, 영향 행 수 0으로 요동 회피"까지 말해야 진짜 구현 경험이다.
  7. 레이트 리밋 관리 컴포넌트 자체도 무너질 수 있다 — 토큰 버킷 서비스가 불가용할 때 "단일 머신 제한 + 핵심 작업 우선"으로 강등하고 사후 복기·교정을 한다. 의존이 건강할 때만 성립하는 안정성 방안은 안정성 방안이 아니다.

엔지니어링 실전 Tips

  • "영향 행 수 0"을 동시성 판정 근거로 쓸 것: 서킷 브레이커 상태 변경은 행 잠금 + 영향 행 수로 변경 권한을 획득했는지 판단하는 것이 상태 요동을 막는 가장 직접적인 구현이다.
  • 강등 동작을 미리 번호로 매길 것: degrade_level 필드로 경/중/중차단 세 등급 각각의 확정 동작(대체 도구/캐시/거부)을 미리 정해 두어야 장애 시 자동화가 가능하다.
  • 쿼터를 못 얻은 요청은 우선순위 대기열로 보낼 것, 무한 재시도로 보내지 말 것: 전자는 스케줄링이고 후자는 재시도 폭풍의 씨앗이다.
  • 이관은 "재발 없음"을 전제로 할 것: 7일 지난 정상 장애 기록만 이관하고, 이관하는 동안 장애가 재발하지 않는지 반드시 확인해야 한다.
  • 쿼터 관리와 도구 호출을 같은 스케줄 링크에 둘 것: 두 동작이 분리되면 "쿼터를 취득한 것"과 "호출을 발기하는 것" 사이에 틈이 생겨 쿼터가 다른 요청에 먼저 소진될 수 있다.
  • 모니터링 다섯 지표를 세트로 볼 것: 제한 트리거율, 재시도 성공률, 서킷 지속 시간, 강등 작업 비중, 쿼터 사용률 — 하나만 보면 "재시도 성공률은 높은데 쿼터 사용률이 이미 가득 찬" 붕괴 직전 상태를 놓친다.
  • 면접·설계 검토 답변 순서: 범위와 실패 시나리오 정의 → 핵심 방안(토큰 버킷+서킷 등급 강등) → 프로덕션 디테일(상태 추론, 이관, 모니터링, 자체 장애 강등) 순으로 답하면 빠뜨림이 없다.