具代表性的面試主題

後端面試:如何設計安全的 Request Hedging?

後端困難
OfferCC 編輯團隊發佈 更新

題幹

某個冪等讀 API 的 p99 延遲很高,請設計 Request Hedging。說明觸發閾值、候選副本、取消競速請求、負載保護、監控與何時關閉策略。

題目與適用場景

某個冪等讀 API 的 p99 延遲很高,p50 正常,慢請求主要來自偶發排隊或單機抖動。請設計 Request Hedging:先送主請求,經過延遲仍未完成時向另一副本送出複本,取先回應的結果並取消另一個。回答必須說明額外負載與故障放大邊界。

面試官考察重點

是否理解目標

Hedging 針對尾延遲,不會讓每個請求都更快。它適合可安全重複的讀操作,不能直接複製有副作用的寫請求。

是否能控制代價

如果所有請求同時複製,後端負載可能接近翻倍。應使用延遲觸發、最大嘗試次數、節流與取消,並用原始請求指標評估效果。

是否能處理相關故障

把主請求與副本送到同一台過載機器沒有收益。跨實例、可用區或故障域路由,以及佇列深度與錯誤率保護,決定方案是否安全。

回答前要釐清的問題

  • API 是否冪等,結果是否允許多個並行讀取?
  • p50、p95、p99 與 p99.9 基線及 SLO 是什麼?
  • 慢請求是隨機單機抖動,還是所有副本都在排隊?
  • 副本如何選擇,是否跨實例、可用區或版本?
  • 取消請求能否真正釋放下游執行緒、連線與計算資源?
  • 哪種錯誤率、佇列深度或 hedge fire rate 會自動關閉?

30 秒回答框架

「我只對冪等讀啟用 hedging。先送主請求;按請求類別用動態 p95 或受控分位數作延遲閾值,超時才向不同故障域的健康副本送一次 hedge。任一請求成功就取消另一個,整條鏈路共用 deadline。最大嘗試次數、節流令牌、錯誤率與佇列深度保護後端;監控 p99、觸發率、額外請求量、取消成功率與原始延遲,異常時關閉。」

分步深入解答

定義請求狀態機

狀態依序為 primary_sent、hedge_waiting、hedge_sent、winner_selected 與 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。業務仍要校驗自己的容量預算。

公開來源

同類題目