先分清事件時間與處理時間
事件時間是交易、設備讀值或使用行為實際發生的時間;處理時間則是資料被平台接收或執行計算的時間。行動裝置離線、IoT 閘道器暫存、第三方 API 批次同步、網路重試與訊息佇列壅塞,都可能讓兩者相差很久。若直接依處理時間彙總,昨天發生但今天才抵達的事件就會被算進今天,日報、帳務期間與異常偵測都可能失真。
我們通常要求每筆事件至少帶有穩定的 event_id、event_time、ingest_time、來源識別與版本資訊。event_time 應由最接近業務事實的系統產生,並統一保存為 UTC;報表再依明確的業務時區切日。若來源時鐘不可信,不應默默把接收時間當成事件時間,而要記錄時間品質、設備時鐘偏移或推定規則,讓下游知道這個時間欄位的可信程度。
用水位線決定何時結算,而不是假設資料準時
水位線代表系統相信某個事件時間之前的大部分資料已經抵達。它不是目前看過的最大時間,也不應只是固定等待幾分鐘。合理的等待期要根據各來源的延遲分布、網路特性、重送機制,以及業務何時需要看到結果來設定。等待越久,結果越完整,但即時性與狀態儲存成本也越高;等待越短,報表較快產出,卻需要更頻繁地更正。
不同用途可以採用不同的結算政策。營運儀表板可先提供暫定數字,計費或財務資料則應有較嚴格的關帳與更正流程。實作前至少要明確回答以下問題:
- 可接受延遲:每個來源通常會晚多久,最晚仍要自動接納到什麼程度?
- 視窗定義:依小時、曆日、班次或帳期彙總時,邊界採用哪個時區與規則?
- 關窗後政策:晚到事件要重開視窗、更新既有結果、寫入調整項,還是送往人工審核?
- 結果語意:消費端看到的是暫定值、最新修訂值,還是已正式結算且不再變動的版本?
正確入帳需要冪等、可更正的端到端設計
水位線只決定何時計算,不能自行解決重複資料。來源重試、訊息重新投遞或作業復原都可能讓同一事件再次出現,因此去重鍵必須穩定,寫入也要具備冪等性。對可更新的業務事件,還要以來源版本、變更序號或有效時間判斷先後,不能假設最後抵達的就是最新版本。否則較舊但晚到的更新可能覆蓋正確狀態。
儲存層最好保留不可變的原始事件,再由整理層產生可查詢的最新狀態。聚合結果若可能被晚到資料修正,目的端需支援 upsert、撤回後重算,或以追加式調整分錄表達差額。純追加但沒有更正語意的資料表很容易重複計數。對已正式關帳的期間,通常不直接改寫既有總額,而是保留原結算版本並新增可追溯的調整紀錄;對尚未關帳的營運數據,則可安全地重算受影響的分區。批次補數與即時管線也應共用相同轉換規則,避免兩套邏輯產生不同答案。
以對帳與可觀測性驗證結果
晚到資料策略不能只在設計文件裡成立。監控應區分 event_time 與 ingest_time 的落差,按來源追蹤延遲分布、超過水位線的事件、重複事件、被重開的視窗、調整筆數與未能解析的時間欄位。只看管線是否成功並不足夠;作業全數顯示綠燈,仍可能把資料記在錯誤日期。
測試要涵蓋視窗邊界、跨日與跨月、來源時鐘偏移、同一事件重送、舊版本晚到、服務中斷後回補,以及重跑作業等情境。上線後則以來源明細、原始事件與彙總表進行定期對帳,並讓每個結果可以追溯到事件、規則版本與結算版本。成熟的事件時間設計不承諾資料永遠準時,而是讓延遲發生時,系統仍能一致地更新、清楚地說明,並在需要時完整重現計算結果。