效能工程

協同遺漏陷阱(coordinated omission)

想像你這樣替一個服務計時:送出一個請求、等回覆、記錄花了多久、再送下一個——而你打算每 10 毫秒送一個請求。現在假設伺服器整整凍結了一秒。公平的報告會說:那個在途中的請求很慢,「而且」那一秒內本該送出的大約 100 個請求「也」被延遲了,因為它們卡著等輪到自己。但你的迴圈根本沒送出它們——它忙著等那個凍住的請求。於是你的資料記了一個慢樣本,卻無聲地遺漏了另外一百個。這種無聲的遺漏,與慢本身完全相關,就是協同遺漏。

精確地說:每當你的量測框架恰好在系統停滯時暫停了自己的取樣,使得最糟行為的那些時刻被取樣不足,協同遺漏就發生了。閉迴路的負載產生器(送出、等回應、再送下一個)在結構上就會這樣——如果一個回應遲了,下一次送出也自動跟著遲,而那個積壓從不以獨立的慢樣本出現。結果是一條看起來遠比現實健康的延遲分布,而它對尾端的毒害最深:你的 p99 與 p999(第 99 與第 99.9 百分位)會樂觀好幾倍,因為那些「定義」尾端的罕見壞事件,正是你的框架沒能記錄的。修法是依「預定」的時程量測,而非實際的時程:如果一個請求本應在時刻 T 開始卻直到 T+900 毫秒才得以開始,就把它的延遲算成從 T 起算的完整延遲,並替那些本該在停滯期間發出的請求補回缺漏的樣本(HdrHistogram 與 wrk2 之類的工具正是這麼做)。

它之所以重要,是因為尾端延遲才是使用者真正感受到、也是服務水準協議(SLA)據以撰寫的東西,而協同遺漏是「基準回報漂亮的 p99 但正式環境滿是逾時」最常見的原因。誠實的說法:這不是罕見的邊角案例——幾乎每個天真的「送出並對來回計時」迴圈都中招。對策是固定速率(開迴路,不論先前回應是否返回都按時程送出),並把完整的排隊延遲歸給每個被停滯擋住的請求。

計畫:每 10 毫秒 1 個請求。伺服器在 0 -> 1000 毫秒凍結。 天真閉迴路:只記到「一個」1000 毫秒樣本,p99 看起來約 2 毫秒。 修正(開迴路):本該 10 毫秒發的請求等了 990 毫秒、20 毫秒的等了 980 毫秒…約 100 個巨大樣本;真實 p99 約 990 毫秒。

同一次停滯,兩種說法:閉迴路框架藏起約 100 個被延遲的請求;修正遺漏後揭露出差了約 500 倍的尾端。

協同遺漏是由 Gil Tene(HdrHistogram 作者)命名並倡議的;經驗法則是:任何在發出下一個請求前先等回應的基準,幾乎肯定低報了它的尾端延遲。

又稱
coordinated omissionthe stalled-requests-vanish problem協同遺漏被吞掉的延遲