題目與適用場景
某個冪等讀 API 的 p99 延遲很高,p50 正常,慢請求主要來自偶發排隊或單機抖動。請設計 Request Hedging:先送主請求,經過延遲仍未完成時向另一副本送出複本,取先回應的結果並取消另一個。回答必須說明額外負載與故障放大邊界。
面試官考察重點
是否理解目標
Hedging 針對尾延遲,不會讓每個請求都更快。它適合可安全重複的讀操作,不能直接複製有副作用的寫請求。
是否能控制代價
如果所有請求同時複製,後端負載可能接近翻倍。應使用延遲觸發、最大嘗試次數、節流與取消,並用原始請求指標評估效果。
是否能處理相關故障
把主請求與副本送到同一台過載機器沒有收益。跨實例、可用區或故障域路由,以及佇列深度與錯誤率保護,決定方案是否安全。
回答前要釐清的問題
- API 是否冪等,結果是否允許多個並行讀取?
- p50、p95、p99 與 p99.9 基線及 SLO 是什麼?
- 慢請求是隨機單機抖動,還是所有副本都在排隊?
- 副本如何選擇,是否跨實例、可用區或版本?
- 取消請求能否真正釋放下游執行緒、連線與計算資源?
- 哪種錯誤率、佇列深度或 hedge fire rate 會自動關閉?
30 秒回答框架
「我只對冪等讀啟用 hedging。先送主請求;按請求類別用動態 p95 或受控分位數作延遲閾值,超時才向不同故障域的健康副本送一次 hedge。任一請求成功就取消另一個,整條鏈路共用 deadline。最大嘗試次數、節流令牌、錯誤率與佇列深度保護後端;監控 p99、觸發率、額外請求量、取消成功率與原始延遲,異常時關閉。」
分步深入解答
定義請求狀態機
狀態依序為 primarysent、hedgewaiting、hedgesent、winnerselected 與 deadline_exceeded。主請求先送出;閾值內回應就結束,超過閾值才建立副本。任一成功回應成為 winner,其他請求收到取消信號。
選擇閾值與請求範圍
依方法、租戶、請求大小或 prompt 長度分桶維護延遲直方圖,使用 p95 一類的動態閾值並設定上下限與冷啟動回退值。不能只看全站平均,也不能讓所有請求立即複製。
路由到獨立副本
副本應避開主請求所在實例、可用區或故障域,優先選健康且佇列較短的節點。若所有候選都高負載,hedging 只會增加擁塞,應等待、降級或快速失敗。
處理取消與 deadline
客戶端與代理都要傳遞取消令牌;下游必須在取消後停止工作並釋放連線、執行緒、GPU 或快取。主請求與 hedge 共用總 deadline,不能因複製而延長使用者等待時間。
保護下游負載
設定 maxAttempts、最小 hedge delay、並行預算與按服務的令牌桶。gRPC 設定把最大嘗試次數限制為 5,並提供 retry throttling;實作仍要結合自身容量與錯誤語意。
處理錯誤與非冪等操作
只有可重試且冪等的錯誤才允許繼續送 hedge。驗證失敗、參數錯誤等確定性錯誤應立即回傳;寫操作要用冪等鍵,或改用單次請求與補償流程。
偽程式碼
~~~text send(primary) timer = hedgeThreshold(request_class) if primary unfinished at timer and budget_allows(): send(hedge, differentfailuredomain) winner = firstsuccessbefore_deadline() cancel(allotherattempts) record(primarylatency, hedgefired, winner, cancel_result) ~~~
複雜度、監控與回滾
單次請求最壞嘗試數由 maxAttempts 限制,額外請求量取決於觸發率與取消延遲。持續監控 p50/p95/p99、hedge fire rate、額外 QPS、下游佇列、錯誤率、取消成功率與原始主請求延遲;出現擁塞或錯誤惡化時按開關或令牌預算關閉。
| 控制項 | 作用 | 失控風險 |
|---|---|---|
| hedge delay | 只複製慢請求 | 太短會放大 QPS |
| maxAttempts | 限制並行副本 | 太高會形成請求風暴 |
| cancellation | 釋放輸家資源 | 失效會持續消耗下游 |
| throttle budget | 擁塞時收緊策略 | 缺少指標會誤判容量 |
高品質示範回答
「我先確認這是冪等讀,並用按請求類別分桶的 p95 延遲作為初始 hedge 閾值。主請求送出後,只有超過閾值且下游健康、預算允許時,才把一次副本路由到不同實例或可用區。兩次請求共用總 deadline;先成功者回傳,代理立即傳播取消並記錄是否真正釋放資源。用最大嘗試數、令牌桶、佇列深度與錯誤率保護後端,確定性錯誤不再複製。上線前做小流量對照,比較 p99、觸發率、額外 QPS、取消延遲與原始主請求延遲;出現擁塞或 hedge storm 就自動關閉。」
常見錯誤
每個請求都立即複製
這會把 hedging 變成無條件複製,常態負載與成本都會增加,也無法證明尾延遲來自偶發抖動。
複製有副作用的寫請求
兩個請求可能建立兩筆資料或扣款兩次。除非已有冪等鍵、去重與交易語意,否則不應使用 hedging。
把兩個請求送到同一故障域
同一實例、機架或可用區的共同故障會讓副本同時變慢,還會向過載位置加壓。
只看使用者看到的 p99
hedging 可能掩蓋真實主請求變慢,使擴容信號延遲。應同時記錄未 hedge 的主請求延遲與佇列深度。
忽略取消實作
停止等待回應不等於下游停止計算。必須驗證取消傳播、資源釋放與取消延遲。
沒有停止開關
高錯誤率、佇列堆積或 hedge fire rate 異常時,系統需要按服務、租戶或區域快速關閉策略。
追問與應對
Hedging 與 retry 有什麼差異?
Retry 通常等待失敗後再送;hedging 在等待期間按延遲閾值並行送出副本,用來繞過偶發慢請求。兩者都需要冪等、deadline 與節流。
閾值為什麼常用 p95?
它讓大多數正常請求不觸發複製,同時覆蓋尾部一小部分請求。實際閾值應依請求類別、預算與實驗結果動態調整。
如果所有副本都擁塞怎麼辦?
停止送 hedge,使用限流、排隊、降級或快速失敗。hedging 無法修復容量不足,繼續複製會造成更嚴重擁塞。
如何驗證取消真的生效?
在下游記錄取消事件、工作單元完成時間、連線占用與資源釋放延遲,並以故障注入確認 winner 產生後 loser 會停止。
串流回應如何處理?
可把首位元組或首 token 延遲作為觸發條件,但已開始輸出後複製可能造成重複資料。需要定義串流合併、取消與客戶端可見性。
gRPC 設定有哪些關鍵限制?
maxAttempts、hedgingDelay 與非致命狀態碼決定發送時機;gRPC 將最大嘗試次數限制為 5,並提供 retry throttling 與 server pushback。業務仍要校驗自己的容量預算。