具代表性的面試主題

後端面試:如何用 ETag 與 If-Match 防止遺失更新?

後端中等
Offer.cc 編輯團隊發佈 更新

題幹

兩位編輯者讀取同一份文件並提交修改。如何使用 ETag 與 If-Match,讓第二次寫入不能靜默覆蓋第一次修改?

題目與情境

API 為文件回傳版本驗證器,多個用戶端可以並行讀寫;要求拒絕過期寫入,同時維持介面可重試且可觀測。

面試官考察什麼

  • 區分快取表示與寫入前置條件。
  • 選擇強 ETag,並在變更方法上執行 If-Match
  • 過期版本回傳 412,並定義安全的用戶端恢復路徑。

作答前的釐清問題

  • ETag 代表精確儲存表示,還是只代表弱語意版本?
  • PUT、PATCH、DELETE,還是所有寫操作都必須帶前置條件?
  • 用戶端是否自動合併欄位,還是必須由使用者解決衝突?
  • 重試是否經過負載平衡 API 和同一個交易型資料儲存?

30 秒回答框架

我會在每個可編輯表示上回傳強 ETag。用戶端在 PUT、PATCH 或 DELETE 中傳送 If-Match。伺服器在同一交易內比較它並執行更新;版本不相符就回傳 412,不產生副作用。回應可提供目前表示或重新取得訊號,同時記錄衝突與缺少前置條件的指標。用戶端重新取得、明確合併,再用新 ETag 重試。

分步深入

1. 產生驗證器

GET 回傳文件及由規範化儲存版本產生的 ETag。資料庫修訂號通常比對大型載荷雜湊更簡單,只要相關表示每次變化都會更新。If-Match 保護精確寫入狀態,應使用強驗證器。

2. 原子執行前置條件

更新必須在同一個條件操作中檢查期望版本並寫入新版本。概念 SQL 如下:

sql
UPDATE documents
SET body = :new_body, version = version + 1
WHERE id = :id AND version = :expected_version;

影響行數為零時回傳 412,不執行副作用。先在應用程式內檢查 ETag、稍後再寫入會留下檢查與提交之間的競態。

3. 區分 412 與其他失敗

412 表示提供的前置條件為假,是並行衝突;不是 JSON 格式錯誤,也不是驗證失敗。請求結構無效用 400,驗證或權限問題用 401/403,資源按介面策略不可用時用 404。如此用戶端才能選擇重新取得合併,而非統一重試。

4. 設計用戶端恢復

收到 412 後取得目前文件,展示衝突欄位或差異。只有欄位語意和權限規則允許時才自動合併。重試必須使用新回傳的 ETag,並維持資源層級的冪等性;不要盲目重放舊請求。

5. 維持快取與副本一致

驗證器應來自寫入路徑可見的已提交狀態。落後副本可能回傳舊 ETag,造成不必要衝突;寫請求轉到其他節點時,仍必須由主交易執行版本檢查。記錄衝突率、缺少 If-Match 比率和重試成功率。

高品質示範回答

「我會為每份可編輯文件回傳強 ETag,並要求變更請求帶 If-Match。伺服器在更新資料列的同一交易中比較版本;沒有匹配資料列就回傳 412,且不執行副作用。用戶端重新取得文件,展示差異或按明確規則合併,再用新 ETag 重試。我會把 412 與驗證、權限錯誤分開,並監控舊讀、衝突和重試成功率。」

常見誤區

  • 先檢查 ETag 再分開更新 → 仍存在競態 → 在一個原子版本條件更新中完成。
  • 精確寫入使用弱驗證器 → 比較可能失真 → 使用強驗證器。
  • 所有過期寫入都回傳 409 → 用戶端無法識別協定前置條件 → If-Match 失敗使用 412。
  • 盲目重試舊載荷 → 可能覆蓋第一次修改 → 重新取得、明確合併,再用新標籤重試。

追問與回答

ETag 只能用於快取嗎?

不能。If-None-Match 常用於快取校驗,If-Match 則讓寫入依賴目前表示。只要比較強度和產生策略正確,同一驗證器可以承擔兩種用途。

用戶端不傳送 If-Match 怎麼辦?

若資源要求樂觀並行,應按契約拒絕,回傳明確的前置條件要求或 400 策略。靜默接受無條件寫入會重新引入遺失更新;各方法必須保持一致。

412 回應應回傳最新文件嗎?

在權限和載荷大小允許時可以回傳,但契約仍應要求重新取得或明確解決衝突。回傳資料不代表用戶端可以不帶最新驗證器覆蓋它。

公開來源

同類題目