Tail Latency 극복: Go 마이크로서비스에서 Request Hedging 가이드
Source: Dev.to
분산 시스템에서는 종종 “Long Tail(긴 꼬리)”에 대해 이야기합니다.
예를 들어 95 %의 요청이 100 ms 이하로 완료되지만, 마지막 1 %(P99 지연)는 2 초 이상 걸릴 수 있습니다. 하나의 사용자 행동이 열 개의 서로 다른 서비스 호출을 트리거하는 마이크로서비스 아키텍처에서는, 단 하나의 느린 종속성이 전체 사용자 경험을 병목 현상으로 만들 수 있습니다.
표준 재시도는 여기서 도움이 되지 않습니다. “긴 꼬리” 요청은 아직 실패한 것이 아니라 느린 것이기 때문입니다. 2 초 타임아웃이 발생해 재시도가 일어나기를 기다리는 것은 시간을 낭비하는 것입니다. 긴 꼬리를 이기려면 Request Hedging(요청 헤징)(또는 speculative retries, 추측 재시도) 가 필요합니다.
Request Hedging이란?
개념은 간단하지만 강력합니다: 요청이 평소보다 오래 걸리고 있다면(예: P95 지연보다 오래) 요청을 중단하지 않습니다. 대신 동일한 두 번째 요청을 병렬로 시작합니다. 먼저 끝난 요청의 결과를 사용하고, 다른 요청은 취소합니다.
이 추측적 접근 방식은 두 개의 동일한 요청이 동시에 긴 꼬리를 만나게 될 확률이 매우 낮기 때문에 P99 지연을 크게 감소시킵니다.
수동 헤징의 복잡성
Go에서 헤징을 직접 구현하는 것은 고루틴 관리의 악몽과 같습니다:
- 타이머가 포함된
select블록이 필요합니다. - 두 개(또는 그 이상)의 고루틴을 조정해야 합니다.
- 하나가 성공하면 나머지는 즉시 취소해 자원을 절약해야 합니다.
- 두 요청이 정확히 같은 밀리초에 성공하는 경우와 같은 레이스 컨디션을 처리해야 합니다.
대부분의 개발자는 하나의 헤징된 호출을 처리하기 위해 수백 줄의 부서지기 쉬운 보일러플레이트 코드를 작성하게 됩니다.
Resile 방식: DoHedged
Resile은 요청 헤징을 단일 함수 호출만으로 간단하게 만들어 줍니다. 고루틴 수명 주기, 컨텍스트 취소, 레이스 컨디션을 자동으로 처리합니다.
import "github.com/cinar/resile"
data, err := resile.DoHedged(
ctx,
func(ctx context.Context) (*User, error) {
return apiClient.GetUser(ctx, userID)
},
resile.WithMaxAttempts(3),
resile.WithHedgingDelay(100 * time.Millisecond),
)
내부에서 무슨 일이 일어나나요?
- Resile은 첫 번째 요청을 시작합니다.
- 설정된
HedgingDelay(예: 100 ms)만큼 대기합니다. - 첫 번째 요청이 아직 끝나지 않았다면 두 번째 요청을 시작합니다.
- 하나가 성공적으로 결과를 반환하면 Resile은 다른 요청의 컨텍스트를 취소하고 데이터를 반환합니다.
적절한 Hedging Delay 선택하기
헤징의 “마법”은 지연 시간에 있습니다.
- 너무 짧게: 불필요하게 트래픽이 두 배가 되어 하위 서비스에 추가 부하를 줍니다.
- 너무 길게: 지연 감소 효과가 거의 없습니다.
팁: HedgingDelay를 P95 또는 P99 지연으로 설정하세요. 이렇게 하면 가장 느린 1‑5 %의 요청만 헤징하게 되어, 최소한의 추가 부하로 큰 지연 감소를 얻을 수 있습니다.
관측성: “추측” 승리 추적하기
Resile의 OpenTelemetry 통합(telemetry/resileotel)을 사용한다면, 분산 트레이스에서 이러한 승리를 확인할 수 있습니다. 각 헤징 시도는 서브‑스팬으로 기록되며, 헤징된 요청이 승리하면 첫 번째 스팬은 취소되고 두 번째 스팬이 성공하여, 헤징이 사용자를 2‑초 대기에서 구해냈다는 명확한 증거를 제공합니다.
결론
요청 헤징은 과거에 대규모 인프라 팀을 가진 기업만이 사용할 수 있는 기술이었습니다. Resile을 사용하면 모든 Go 개발자가 더 빠르고 회복력 있는 마이크로서비스를 구축할 수 있는 도구가 됩니다.
“대기하고 재시도”에서 “헤징하고 승리”로 전환함으로써, 긴 꼬리 지연을 경쟁력으로 바꿀 수 있습니다.
GitHub에서 Resile에 스타를 눌러 주세요: