樂觀並行控制 (OCC) 是一種策略,用於管理共用資源,並在多位使用者或多個程序同時修改相同資源時,防止更新遺失或競爭狀況。
舉例來說,使用 Identity and Access Management 時,IAM 政策是附加至專案、bucket 或服務等資源的共用資源。如果沒有並行控制,同時修改可能會覆寫最近的政策變更,不小心撤銷合法存取權或授予未經授權的權限。
如要實作 OCC,系統通常會使用資源結構上的版本編號或 etag (實體標記) 欄位。
OCC 的運作方式
假設有兩個程序 A 和 B,同時嘗試更新共用資源:
- 程序 A 讀取資源的目前狀態。
- 程序 B 讀取相同的目前狀態。
- 程序 A 修改副本,並將其寫回伺服器。
- 程序 B 修改副本,並將其寫回伺服器。
由於程序 B 在不知道程序 A 已變更資源的情況下覆寫資源,因此程序 A 的更新會遺失。
OCC 導入了每次修改實體時都會變更的專屬指紋,藉此解決這個問題。在許多系統 (例如 IAM) 中,這個指紋是 etag。伺服器會在每次寫入時檢查這個標記:
- 讀取資源時,伺服器會傳回
etag(專屬指紋)。 - 傳回修改後的資源時,必須包含原始
etag。 - 如果儲存的
etag與您傳送的etag不符 (表示另一個程序在您讀取資源後修改了資源),寫入作業就會失敗,並傳回ABORTED或FAILED_PRECONDITION錯誤。
這項失敗會向用戶端發出信號,要求重試整個程序:重新讀取更新後的狀態、重新套用變更,並使用新的 etag 再次嘗試寫入。
實作 OCC 迴圈
OCC 實作的核心是重試迴圈,會讀取、修改及嘗試儲存資源,並在成功寫入後結束。為避免在高度競爭期間發生無限迴圈,請設定合理的重試次數上限。
迴圈步驟
下表說明 OCC 重試迴圈中的每個步驟:
| 步驟 | 動作 | 導入範例 |
|---|---|---|
| 閱讀 | 擷取目前的資源狀態,包括 etag。 |
let mut policy = client .get_iam_policy() .set_resource(&secret_name) .send() .await?; |
| 修改 | 將變更套用至本機 struct。 | policy.bindings.push(new_binding); |
| 撰寫及檢查 | 嘗試使用現有的 etag 儲存修改後的資源,並檢查是否有並行錯誤代碼。 |
match client .set_iam_policy() .set_resource(&secret_name) .set_policy(policy) .send() .await { Ok(p) => return Ok(p), Err(e) => { /* ... */ } } |
| 成功或重試 | 如果寫入成功,則退出迴圈。如果因並行錯誤而失敗,請遞增重試計數器,並從「讀取」步驟繼續迴圈。 | - |
範例
以下範例說明如何實作 OCC 迴圈,透過向成員授予 roles/secretmanager.secretAccessor 角色,更新 Secret Manager 密鑰資源的 IAM 政策。
如要執行這個範例,請先在 Cargo.toml 檔案中宣告依附元件:
cargo add google-cloud-secretmanager-v1
接著,實作 OCC 重試迴圈:
完整程式碼
下列程式碼顯示完整函式,以及所有必要的匯入項目: