Uber, 문맥 인식 재시도 차단으로 폭풍 막아 950만 건 요청 억제
- Uber가 공유 인프라에 문맥 인식(context-aware) 재시도 차단 장치를 넣어 950만 건의 불필요한 요청을 막음
- 재시도 횟수 R이 모든 홉에서 같으면 깊이 ɗ 노드의 요청량이 R^ɗ × Ƞ로 늘어나, 서비스 D 장애 시 홉마다 1회 재시도만 설정해도 최하위 노드가 8배 트래픽을 받음
- 재시도 예산 B를 두면 (1+B)^ɗ × Ƞ로 줄어들어, 10% 예산에서 최하위 노드 부하가 1.33배로 억제됨
- 오류 소유권(error ownership) 개념으로 오류가 발생한 C에서 D 구간에서만 재시도하고 A와 B는 재시도를 막아 전체 요청량을 1.1배로 묶음
- 하위 서비스 오류 코드를 상위로 번역해 재시도 판단에 쓰는 방식은 팬인/팬아웃 규모와 잦은 호출 흐름 변경 때문에 Uber에서는 확장되지 않아 공유 인프라에 해법을 둠
Hacker News opinions
재시도 전부 허용하는 짓 했다가 폭풍 맞아봤고, 그 다음엔 최상위 서비스에서만 재시도하게 했더니 실패마다 작업을 통째로 다시 하더라. 적당한 중간 지점이 뭐냐 이거지.
지수 백오프 하나 붙여두면 됨. 에러 내는 쪽이 재시도 가능 시각을 같이 돌려주게 하는 게 제일 단순하고, 우선순위 낮은 요청은 더 오래 기다리게 하면 됨.
핵심은 두 개임. 재시도하는 쪽에 초당 요청 EMA 기준으로 20%까지만 재시도로 허용하는 예산을 두고, 재시도 포기하고 위로 올려보내는 에러는 절대 재시도 안 하기로 합의하는 거. 이 둘만으로 폭풍 규모가 묶임.
우린 서비스 클라이언트를 중앙화해서 회로 차단기처럼 썼음. 에러율이 한계 넘으면 요청을 파일에 쌓아뒀다가 30초마다 하나씩 보냈는데, 안 그러면 죽은 서비스에 30초 타임아웃을 두 번씩 물고 UX가 박살났음.
재시도 데드라인도 좋은 선택지임. 요청 전체를 500ms로 잡고 남은 시간 예산을 HTTP 헤더로 내려보내서 미들웨어가 강제하게 하면 됨.
로드 셰딩이 글에 안 보이는 게 좀 걸림. 호출자 지수 백오프랑 합치면 시작점으로 충분한데. 429나 503이 바로 그 신호일 수도 있고.
Uber 로드 셰더도 괜찮음. 초당 요청이 늘 때 지연이 확 튀는 지점을 동시성 한계로 잡는 방식인데, Netflix concurrency-limits나 failsafe-go도 비슷하게 함.
토큰 버킷이면 다 된다는 건 좀 아닌 게, A에서 B, C, D로 이어지는 체인에서 D가 죽으면 버킷 레이트 안에서도 A, B, C가 전부 재시도해서 증폭이 남음.
A에서 B, C, D로 갈 때 D가 죽으면 C만 D를 재시도하고 A와 B는 체인 전체를 재시도하지 않게 만드는 게 영리함. 에러 예산도 SRE와 SLO에서 온 개념 같고.
예전 동료가 A, B, C, D 각각에 10번 재시도에 sleep 넣어놨는데 코드가 버그투성이여서 D가 1만 번 재시도 끝에 포기했음. 1초 안에 끝날 일이 30분짜리가 됐더라.
구글은 잠시만요, 새로고침하지 마세요 벽을 띄우는데 나는 Ctrl+R을 광클함. 그게 인간 판별 테스트인가 싶기도 하고.