題幹與適用場景
這道資料工程題考察事件契約的長期演進。重點不是背出 backward 或 forward 的定義,而是從真實讀者、寫入者、序列化格式、歷史資料和發布順序推導安全方案。你需要說明相容性目標、欄位語義、註冊與測試、雙版本運行和最終收縮條件。
面試官考察什麼
- 能否區分 writer schema、reader schema 與部署時的新舊組合。
- 能否根據消費者升級順序選擇 backward、forward、full 或 transitive 限制。
- 能否處理必填欄位、列舉變化、預設值、未知欄位和歷史重播。
- 能否把 Schema Registry、契約測試、監控、回滾和所有權納入發布流程。
回答前需要釐清的問題
先列出事件格式、主題或表、所有生產者與消費者、是否有跨團隊或外部訂閱者,以及每個消費者的升級速度。確認 fulfillment_mode 是新增語義還是可由舊狀態推導,舊狀態值是否能無損映射,新列舉是否允許未知值。還要確認歷史事件是否需要重播、資料湖是否保存原始 payload、相容性策略由誰維護,以及能否短期同時發布兩個版本。
30 秒回答框架
我會先建立消費者清單和讀寫升級順序,選定相容方向並在註冊中心啟用檢查。新增欄位先保持可選或提供穩定預設值,列舉採用可擴展的相容表示;生產者先寫舊欄位和新欄位,消費者按能力逐步切換。對歷史重播定義映射與未知值策略,用契約測試、樣本重播和運行監控驗證,確認所有消費者遷移且保留窗口結束後,才刪除舊欄位或舊版本。
分步驟深入解答
1. 畫出讀寫矩陣與相容目標
把舊寫者讀舊讀者、舊寫者讀新讀者、新寫者讀舊讀者和新寫者讀新讀者分別列出。若消費者可能先於生產者升級,需要 forward 相容;若生產者先升級,需要 backward 相容;滾動發布通常要求兩者在過渡期都能工作。跨歷史版本的事件重播還要考慮 transitive 檢查,而非只比較最近一次 Schema。
2. 讓欄位語義可安全擴展
新增欄位不要直接變成所有舊讀者都必須存在的值。對 Avro 等格式,可使用 nullable union 或預設值;對 JSON,要定義缺失、null 和未知欄位的差別。列舉擴展時要求舊消費者有安全的未知分支,不能假設輸入只會出現目前幾個值。若舊狀態無法無損映射,應新增事件版本或並行欄位,不要靜默改寫含義。
3. 用註冊中心與契約測試擋住不相容發布
按主題或事件類型登記 Schema,明確 subject 命名、相容級別和 owner。生產者建置時檢查新 Schema,消費者 CI 用真實舊樣本和新樣本執行反序列化與業務斷言。測試不只驗證能解析,還要驗證預設值、未知列舉、時間與單位、空值和刪除欄位後的結果。失敗時阻止發布,不要等消費者線上報錯。
4. 採用雙寫與分階段升級
先發布能讀新舊格式的消費者,再讓生產者在同一事件中填充新欄位並保留舊欄位。觀察各消費者版本、解析錯誤、預設值使用率和事件延遲;資料管道還要檢查落表型別、分區和回填。需要無法相容的列舉或結構變化時,建立新事件類型或新主題,使用橋接程式雙發,並為兩條鏈路分別設定 owner 與停用日期。
5. 定義重播、回滾和收縮條件
歷史事件按原始 Schema 解析,再用明確映射生成目前模型,不能把今天的預設值悄悄套到過去的事實。保留新舊 Schema、轉換程式碼和樣本快照。回滾生產者時確認舊版本仍能讀取已寫入事件;當所有消費者升級、舊欄位讀取率為零、重播和資料品質檢查通過,且通知窗口結束後,才刪除舊欄位或橋接主題。
高品質示範回答
我會先盤點十幾個消費者和數個資料管道,畫出滾動發布期間的四種讀寫組合,並確認 fulfillment_mode 是可從舊狀態推導還是新增事實。如果能無損推導,我先把欄位定義為可選並提供穩定預設值,舊狀態繼續保留;列舉採用安全的未知分支。先發布能讀新舊格式的消費者,再讓生產者雙寫舊狀態與新欄位,在 Schema Registry 啟用相容檢查。CI 用舊事件、新事件、缺失欄位、未知列舉和歷史重播樣本做契約測試,線上監控解析失敗、預設值比例、各消費者版本和資料落表品質。若結構確實不相容,我會新建事件版本並用橋接程式雙發。只有所有消費者遷移、舊欄位讀取率為零、重播驗證通過且回滾窗口結束後,才收縮舊欄位和橋接鏈路。
常見錯誤
- 只說「設定 backward compatibility」,沒有列出生產者與消費者的升級順序。
- 新增必填欄位卻沒有預設值、缺失策略或舊消費者的安全路徑。
- 擴展列舉時讓舊消費者直接拋異常,或把未知值預設為錯誤的業務狀態。
- 只測 Schema 能否註冊,不測真實舊樣本、重播、落表型別和業務結果。
- 直接覆蓋欄位語義,導致歷史事件重播時被錯誤解釋。
- 沒有 owner、監控、停用日期和回滾條件,長期保留雙寫與橋接程式碼。
追問及應對
backward、forward 和 full 應該怎麼選?
看發布順序和消費方式。生產者先升級需要舊讀者能讀新資料,強調 backward;消費者先升級需要新讀者能讀舊資料,強調 forward;兩者順序不可控或需要雙向相容時選 full。若要保證對全部歷史版本相容,使用 transitive,並確認格式的實際規則。
為什麼新增欄位最好帶預設值?
舊事件沒有該欄位,讀取舊資料時需要一個明確行為。預設值讓讀者能完成解析,但它必須代表「未知或未提供」,不能偽裝成歷史事實;若沒有合理預設值,應使用可空型別、事件版本或顯式回填。
歷史事件重播時如何處理新列舉?
保存寫入時的 Schema,先按原始語義解析,再透過版本化轉換映射到目前模型。無法映射的值進入隔離佇列或人工處理,並記錄原因;不要直接丟棄事件或套用目前預設狀態。
什麼時候可以刪除舊欄位?
當所有生產者與消費者完成遷移,舊欄位讀取率和寫入率為零,歷史重播、資料品質和契約測試通過,客戶或外部訂閱者通知窗口結束,而且回滾與稽核材料仍可用時,再分階段刪除。