"Realtime"
- 持久連線推送:WebSocket、SSE、long-polling 的承諾差異
server 推 client 的持久連線機制對消費者承諾什麼:重連誰負責、訊息會不會漏、單向還是雙向;選型看消費者形狀
- webhook 對外承諾:投遞保證不是預設、consumer 負責一半
webhook 是盡力而為的事件推送不是可靠佇列:投遞保證逐 vendor 讀、可靠性責任分一半給 consumer
- Firestore realtime listener 扇出與成本:snapshot 訂閱、re-read 計費與連線規模
Firestore 的 snapshot listener 提供即時同步、但訂閱的扇出、查詢結果變動的 re-read 計費與連線數會在規模下變成成本與效能瓶頸;本文展開 listener 的推送模型、訂閱範圍設計、五個 realtime 成本踩坑,以及即時需求超過 listener 該換推送架構的邊界
- 11.C55 WHATWG SSE spec:內建自動重連與 Last-Event-ID 補送
SSE 把重連與斷點續傳的協商鉤子寫進協議:自動重連、Last-Event-ID 補送、retry 欄位;補送實際保證仍看 server replay
- 11.C56 RFC 6455:WebSocket 是雙向 transport、不內建投遞保證
WebSocket 給雙向管線、不給保證:協議層對投遞保證、ack、重連、斷點續傳全部沉默、留給應用層
- 11.C57 Slack Socket Mode:WebSocket 上自建 ack、retry 與多連線熱備
協議不給保證、vendor 在應用層自建整套可靠性:envelope ack、未 ack 就 retry、多連線熱備、斷線預警
- 11.C58 RFC 6202:long-polling 的機制代價與 fallback 定位
long-polling hold 住請求到有事件才回:header 開銷、三段網路延遲、每 client 佔一條連線是機制決定的、不是實作品質
- 11.C59 Socket.IO:先 long-polling 再 upgrade WebSocket 的 transport negotiation
WebSocket 不保證能建立(proxy/防火牆會擋)、所以先用 long-polling 建連再 upgrade:fallback 的價值是相容性下限、不是效能
- 11.C60 Stripe webhooks:at-least-once 加簽章、明文要求 consumer 冪等與不依賴順序
webhook 對外承諾的教科書樣本:三天重試、重複投遞、no-ordering、簽章驗證的責任在同一頁明文轉移給 consumer
- 11.C61 GitHub webhooks:不自動重試的反向承諾
at-least-once 不是所有 vendor 都給:GitHub 明文只試一次、失敗靠 consumer 自建排程補投;逼你讀 vendor 明文而非假設
- 11.C62 Slack Events API:3 秒 ack 上限加固定三次重試
「慢等於失敗」寫死成 3 秒硬上限、逼 consumer 走立即 2xx 加背景處理;retry header 讓 consumer 辨識重投
- 11.C63 Shopify webhooks:ordering 不保證、指定 header 去重、投遞不保證
跨 vendor 佐證 ordering-not-guaranteed 加冪等 header 是通則;甚至連投遞本身都不保證、需 reconciliation 兜底