問題

我做了一個實驗,要比較「商家平板拉新訂單」的三種做法:長輪詢、短輪詢、平台用 webhook 打平板。 第一版跑出來的結果,有三處是實驗設計造成的,不是協定本身的差別——不仔細看就會寫進文章當結論。

#第一版看到的看起來像真正原因
1短輪詢只收到 4/5 張單「短輪詢會漏單」時間窗只比最後一張付款晚 1.4 秒,短輪詢間隔 2 秒——實驗先結束了,它還沒來得及問
210 秒 5 張單,兩邊請求數都是 5「長輪詢沒有省請求」時間表太忙,一直有新單,長輪詢每次都馬上回。長輪詢的優勢在閒置時才出現,而真實的商家大部分時間是閒的
3webhook 打平板失敗「平板沒公開網址所以連不到」用的是 1 號 port,fetch 本身就禁止連(bad port),請求根本沒送出去——量到的是 fetch 的限制,不是網路狀況

第 3 例的依據在 WHATWG Fetch 規格的「Port blocking」一節:HTTP(S) 請求的 port 若在 bad port 表上就直接擋下, 而 1 號(tcpmux)就在那張表裡,同表還有 21、22、25 這類常見服務的 port (Fetch Standard — bad port)。 我那次拿到的錯誤是 TypeError: bad port,跟「連不上」完全是兩回事。

第 3 例和 比錯基準 裡的「管道錯」是同一族:檢查有跑、有輸出,量的卻是別的東西。

解法

  1. 時間窗:最後一個事件之後至少留一個「最慢那一方的週期」;寫成程式檢查,太短直接丟錯

    const last = Math.max(...schedule.paidAtMs);
    if (schedule.windowMs < last + schedule.shortIntervalMs + 300) throw new Error('時間窗太短');
  2. 工作負載要像真實情況:加入閒置期(前 9 秒 5 張單、後 21 秒沒有)。修正後:長輪詢 5 次請求、短輪詢 15 次

  3. 「連不到」要真的連不到:先開一個伺服器拿系統分配的 port、再關掉,得到真正的 ECONNREFUSED;並在測試裡 assert 失敗原因,而不只是「失敗了」

為什麼這樣解

  • 對照實驗回答的是「A 和 B 差在哪」。只要條件對其中一方不利(時間不夠、負載不典型、量錯東西),差別就變成實驗設計的產物
  • 三個錯都會讓結果「看起來合理」:短輪詢漏單、長輪詢沒省多少、平板連不到——每一個都符合某種直覺,所以特別容易放行
  • 把公平前提寫成程式檢查,而不是寫在說明裡:下次改時間表的人(包括我)不會再踩

怎麼預防再次發生

寫任何對照實驗前,先列出並寫成檢查:

  • 兩邊做的是同一件事嗎? 我的每個對照實驗都有「公平前提」測試:組頁面的實驗檢查兩邊組出的畫面相同、查價的實驗檢查兩邊回傳結果相同、平板收單的實驗檢查兩邊都收齊訂單
  • 條件有沒有偏袒一方? 時間窗、負載形狀、輪詢間隔
  • 失敗的原因是我以為的那個嗎? 不只 assert「失敗」,要 assert 失敗原因(ECONNREFUSED、DEADLINE_EXCEEDED)
  • 跟機器有關的數字(延遲)只看相對關係,而且至少跑兩次確認方向一致

相關