빌드

재시도가 무는 값 — 다시 걸어 보는 쪽 말고, 받는 쪽

작성: makemind · 2026년 7월 16일

계산대가 없는 동안 끝에 이렇게 적었다.

자동 재시도가 없다. 하니스가 명시적으로 다시 붙였다. 실제 앱이라면 뒤에서 주기적으로 시도해야 하고, 그 주기와 backoff 는 여기서 안 정했다.

이 편이 그 주기를 정한다. 그리고 정하는 김에, 주기를 잘못 정하면 무슨 일이 생기는지를 잰다.

받는 쪽에서 센다

재시도 글은 대부분 거는 쪽 이야기다 — 어떻게 내 요청을 통과시킬까.

이 샘플의 서버는 반대편에 선다. 시도가 몇 번 왔는지, 그리고 그중 몇 번이 같은 200밀리초 안에 몰렸는지를 센다.

/// That second number is the one that matters. A gateway that is briefly down
/// does not care that you tried again. It cares how many of you tried again at
/// the same instant, because that is what keeps it down.

잠깐 죽은 게이트웨이는 당신이 다시 걸었다는 사실에 관심이 없다. 관심 있는 건 몇 명이 같은 순간에 다시 걸었는가다. 그게 계속 죽어 있게 만드는 것이기 때문이다.

급할 때 쓰는 방법

/// Try again immediately. This is what gets written when the retry is added in
/// a hurry, and it is the strategy that turns a short outage into a long one.
class NoBackoff extends Backoff {
  @override
  int waitBefore(int n) => n == 1 ? 0 : 20;
}
RETRY IMMEDIATELY — 64 attempts. 18 attempts inside one 200ms window. try 1~16, 20ms 간격
RETRY IMMEDIATELY — 64 attempts. 18 attempts inside one 200ms window. try 1~16, 20ms 간격
RETRY IMMEDIATELY: 4 callers, 64 attempts total, 4 accepted, peak 18
  caller 1 attempt 1: straight away -> DOWN at 12ms
  caller 1 attempt 2: after 20ms -> DOWN at 58ms
  ...
  caller 1 attempt 16: after 20ms -> ACCEPTED at 708ms

통과했다. 700밀리초 장애를 넘겼고 네 명 다 결제됐다.

그리고 게이트웨이는 그동안 64번 맞았다.

두 배씩 늘리고, 흔든다

@override
int waitBefore(int n) {
  if (n == 1) return 0;
  final base = 60 * (1 << (n - 2)); // 60, 120, 240, 480 ...
  // Full jitter: anywhere in [0, base]. Spreading matters more than being
  // punctual — nobody is waiting on an exact millisecond here.
  return _rng.nextInt(base + 1);
}
EXPONENTIAL + JITTER — 25 attempts. 13 attempts inside one 200ms window. try 1~6
EXPONENTIAL + JITTER — 25 attempts. 13 attempts inside one 200ms window. try 1~6
EXPONENTIAL + JITTER: 4 callers, 25 attempts total, 4 accepted, peak 13
  caller 1 attempt 1: straight away -> DOWN at 13ms
  caller 1 attempt 2: after 53ms  -> DOWN at 70ms
  caller 1 attempt 3: after 87ms  -> DOWN at 159ms
  caller 1 attempt 4: after 143ms -> DOWN at 305ms
  caller 1 attempt 5: after 389ms -> DOWN at 697ms
  caller 1 attempt 6: after 449ms -> ACCEPTED at 1150ms

같은 장애, 같은 인원, 같은 결과. 네 명 다 결제됐다.

시도는 64번에서 25번. 2.6배 적다.

두 배 늘리는 것과 흔드는 것은 다른 일이다

여기가 이 편이 하고 싶은 말이다.

지수 백오프는 다들 안다 — 60, 120, 240, 480. 지터는 자주 빠진다. 그런데 부르는 쪽이 하나면 지터는 있으나 마나고, 여럿이면 지터가 전부다.

이유는 단순하다. 네 명이 같은 순간에 실패했으면, 지터가 없으면 네 명이 같은 순간에 돌아온다. 60ms 뒤에 넷, 180ms 뒤에 넷, 420ms 뒤에 넷. 게이트웨이는 방금 자기를 넘어뜨린 그 봉우리를 계속 맞는다.

위 로그를 보면 대기가 53, 87, 143, 389, 449로 어느 것도 60·120·240·480이 아니다. 흔들린 값이다.

검증이 그걸 확인한다.

# Jitter must actually produce different waits, otherwise the four callers
# come back together and the whole point is lost.
WAITS=$(grep -o "after [0-9]*ms ->" captures/run.log | sort -u | wc -l | tr -d ' ')
[ "$WAITS" -ge 2 ] || { echo "   every wait was identical — jitter is not doing anything"; exit 1; }

이 콘텐츠는 개발자 이상이 필요합니다

로그인 후 플랜을 업그레이드하면 계속 읽을 수 있습니다.

플랜 보기
Twitter