글 / 시스템 연계

지수 백오프와 지터

재시도 간격은 두 배씩 증가시키되 상한을 두고, 무작위성을 더하여 여러 클라이언트의 재시도가 겹치지 않게 한다.

작성일 · 읽는 시간 2분

즉시 재시도의 문제

서버가 일시적으로 중단되면 그 시점에 실패한 클라이언트가 모두 즉시 재시도한다. 복구 직후의 서버에 요청이 동시에 집중되어 다시 장애가 발생한다. 따라서 재시도에는 간격을 두어야 하고, 각 재시도는 서로 다른 시점에 도착해야 한다.

지수 증가와 상한

지수 백오프(exponential backoff)는 실패할 때마다 대기 시간을 두 배로 증가시키는 방법이다. 대기 시간이 무한히 증가하지 않도록 상한(cap)을 둔다. 첫 간격이 0.5초, 상한이 30초이면 0.5, 1, 2, 4, 8, 16, 30초로 증가한다.

두 배씩 증가하다 상한 30초에 이르는 재시도 간격과 지터 범위

지터를 이용한 분산

간격만 증가시키면 같은 시점에 실패한 클라이언트는 다음에도 같은 시점에 재시도한다. 지터(jitter)는 대기 시간에 무작위성을 더하는 방법이다. 그중 full jitter는 0부터 앞에서 구한 간격까지의 범위에서 값을 균등하게 추출한다.

지터가 없을 때와 full jitter를 적용했을 때의 재시도 시점 비교

pythonretry.py
import random
import time

BASE, CAP, MAX_ATTEMPTS = 0.5, 30.0, 8


def backoff(attempt: int) -> float:
    # 0과 '두 배씩 늘린 간격' 사이에서 고른다 (full jitter)
    return random.uniform(0, min(CAP, BASE * 2 ** attempt))


def call_with_retry(send):
    for attempt in range(MAX_ATTEMPTS):
        try:
            return send()
        except (ConnectionError, TimeoutError):
            if attempt == MAX_ATTEMPTS - 1:
                raise
            time.sleep(backoff(attempt))

재시도 대상 오류

오류 재시도
연결 실패, 시간 초과 함
과부하, 요청 제한(HTTP 503, 429) 함
잘못된 요청, 권한 없음(HTTP 400, 403) 안 함

재전송해도 결과가 같은 오류를 재시도하면 부하만 증가한다. 횟수에도 상한을 두고, 상한을 초과하면 오류를 그대로 상위로 전파한다. 주문 생성과 같이 상태를 변경하는 요청은 두 번 처리되어도 결과가 같도록 만든 뒤에만 재시도한다.

오류 종류와 횟수 상한에 따른 재시도 판단 흐름