00. Timeout Retry Idempotency Circuit Breaker 압축 정리
6 min read
Timeout / Retry / Idempotency / Circuit Breaker
3줄 요약
Timeout은 느린 요청을 포기하는 값이 아니라 thread, connection, transaction, downstream 자원을 언제 회수할지 정하는 운영 정책이다.
Retry는 transient failure에는 도움이 되지만 idempotency, backoff, jitter, retry budget 없이 쓰면 장애를 증폭하는 traffic generator가 된다.
Circuit Breaker, Bulkhead, Fallback은 장애를 없애는 기능이 아니라 외부 의존성 장애가 전체 서비스로 번지는 것을 제한하는 보호 장치다.
핵심 정리
connect timeout은 TCP 연결 수립 제한이고 read/response timeout은 요청을 보낸 뒤 응답을 기다리는 제한이다. pool acquire timeout은 외부 API에 도달하기 전 내 애플리케이션 pool에서 대기하다 실패한 것이다.
timeout은 client, CDN/LB/proxy, application, HTTP client, DB, external API가 서로 정렬되어야 한다. 후단 dependency timeout은 caller의 전체 deadline보다 짧아야 사용자 응답 시간을 남길 수 있다.
retry 대상은 status code만으로 정하지 않는다. method, exception, idempotency key, Retry-After, downstream overload 여부를 함께 본다.
Idempotency Key는 같은 의도의 중복 요청을 하나의 결과로 수렴시키는 저장소 설계다. key, scope, request fingerprint, status, response, TTL, unique constraint가 필요하다.
Circuit Breaker가 open되는 것은 장애 해결이 아니라 caller 보호 동작이다. downstream 복구 여부와 breaker state를 별도로 관찰해야 한다.
Spring에서는 dependency별 HTTP client, timeout, pool, retry, breaker, bulkhead, fallback, metric을 분리해야 한다. 모든 외부 API가 같은 client와 같은 설정을 공유하면 장애 격리가 어렵다.
헷갈리는 지점
timeout을 길게 잡으면 안전하다고 생각하기 쉽다. 실패가 늦게 보일 뿐 thread, connection, DB lock, 외부 API pool을 오래 붙잡아 장애를 키울 수 있기 때문이다.
핵심 판단 포인트는 사용자-facing deadline, downstream timeout, retry 횟수를 곱한 최악의 점유 시간을 계산하는 것이다.
retry를 넣으면 안정성이 올라간다고 믿기 쉽다. overload 상태의 retry는 실패한 시스템에 더 많은 traffic을 보내기 때문이다.
핵심 판단 포인트는 retry success rate, retry exhausted count, downstream latency, retry budget을 함께 보는 것이다.
POST는 무조건 retry하면 안 된다고 외우기 쉽다. POST 자체는 idempotent하지 않지만 idempotency key와 중복 방지 저장소가 있으면 같은 의도의 재시도를 안전하게 만들 수 있기 때문이다.
핵심 판단 포인트는 같은 key로 같은 payload가 오면 저장된 결과를 반환하고, 다른 payload가 오면 거부하는 것이다.
fallback은 사용자 경험을 무조건 좋게 만든다고 착각하기 쉽다. 오래된 데이터나 임의 기본값을 정상처럼 보여 주면 더 큰 비즈니스 문제가 생길 수 있기 때문이다.
핵심 판단 포인트는 critical dependency에는 명확한 실패와 상태 조회를, optional dependency에는 제한이 드러나는 degrade 응답을 주는 것이다.
확인 질문
connect timeout, read timeout, pool acquire timeout은 어떻게 다른가?
connect timeout은 대상과 TCP 연결을 맺는 제한이고, read timeout은 요청 후 응답 대기 제한이며, pool acquire timeout은 내 애플리케이션의 connection pool에서 연결을 빌리지 못한 것이다.
retry가 장애를 증폭하는 대표 상황은 무엇인가?
downstream이 overload 상태인데 여러 caller와 여러 계층이 동시에 재시도해 정상 traffic보다 큰 retry traffic을 만들 때다.
Idempotency Key 저장소에 request fingerprint가 필요한 이유는 무엇인가?
같은 key로 다른 payload가 들어오는 것을 구분해 잘못된 중복 처리나 공격성 요청을 막기 위해서다.
Circuit Breaker와 Bulkhead의 차이는 무엇인가?
Circuit Breaker는 실패 중인 dependency 호출을 빠르게 차단하고, Bulkhead는 특정 dependency가 전체 thread나 connection 같은 자원을 점유하지 못하도록 격리한다.
Spring resilience 설정에서 가장 위험한 상태는 무엇인가?
HTTP client timeout과 pool을 기본값에 맡기고, retry/breaker metric 없이 모든 dependency가 같은 client와 같은 정책을 공유하는 상태다.