產生編號和先決條件

物件產生編號可讓使用者識別出資料資源並套用先決條件,以保證多步驟交易的單元性。

產生

就算沒有啟用物件版本管理,所有 Cloud Storage 物件還是會有產生編號和中繼產生編號。每次覆寫物件時即會改變產生編號,每次更新物件的中繼資料時,也會改變中繼產生編號。

由於每產生一個新的物件,物件中繼產生編號就會重設回 1,因此中繼產生編號只有在與產生編號配對時才有意義。

值區也會保留中繼產生編號,讓使用者可以唯一識別值區中繼資料狀態。由於值區沒有酬載資料,因此不會有產生編號,其中繼產生編號本身即有意義。

範例:平行上傳

平行上傳中,您必須將物件分成多個區塊,將這些區塊同時上傳到一個臨時的位置,然後執行 compose,從這些臨時區塊組合出原始物件。如果某一獨立處理程序所使用的名稱不小心與您上傳的一或多個臨時區塊的名稱相同,則當您執行 compose 嘗試組合物件時,會使用到不正確的元件而損壞物件。

使用產生編號就可以避免損壞情形發生。如果您在發出 compose 要求時納入每個上傳區塊的產生編號,結果會是系統執行 compose 使用正確的區塊組合,或要求失敗並傳回 404 Not Found 回應。

先決條件

先決條件會指示 Cloud Storage 只有在受影響物件的產生編號或中繼產生編號符合先決條件時,才能執行要求。這些針對產生編號和中繼產生編號的檢查可確保物件處於預期狀態,讓您安全地對物件執行「讀取 - 修改 - 寫入」更新和條件作業。

match 先決條件使用特定的產生編號或中繼產生編號時,要求所的 Cloud Storage 物件必須具備相同的產生編號/中繼產生編號。如果有,則要求成功。如果沒有,則要求失敗並傳回 412 Precondition Failed 回應。

match 先決條件使用 0 值而非產生編號時,則只有在要求中指定的 Cloud Storage 值區沒有任何使用中物件時,要求才會成功。如果有這類物件,則要求會失敗並傳回 412 Precondition Failed 回應。

我們常在變異要求 (上傳、刪除、複製或中繼資料更新) 中使用先決條件,以避免發生競爭狀況。如果重複傳送相同的要求,或獨立程序彼此干擾,可能會發生競爭狀況。例如,網路中斷後多次重試要求使用者對相同物件執行「讀取 - 修改 - 寫入」作業都會產生競爭狀況。

除了使用產生編號和中繼產生編號的先決條件外,還有使用 ETag 的先決條件。適用於非複合物件的 XML API ETag 只有在物件內容變更時才會變動,而適用於複合物件和 JSON API 資源的 ETag 只要內容或中繼資料變更就會變動。如要進一步瞭解 ETag,請參閱雜湊和 ETag:最佳做法

先決條件的成本

先決條件會有效能成本和帳單費用:針對每個變異作業,您也可以發出可計費的 GET 中繼資料要求以決定物件的產生編號/中繼產生編號。基於效能考量,由於先決條件增加了額外的往返傳送時間,可能讓整體作業延遲時間中的網路延遲部分加倍,這對易受延遲時間影響的作業來說是個重要的因素。基於價格考量,允許您使用先決條件的 GET 中繼資料要求會按每 10,000 個作業 $0.004 美元的費率計費。

依您的應用程式而定,有些方法可避免產生使用先決條件的相關效能和帳單費用,例如:

  • 在本機儲存物件的產生編號和中繼產生編號,這樣您就會知道先決條件所使用的正確編號。
  • 使用命名配置以避免相同物件名稱出現多種變異,這樣您就不需要使用先決條件。
  • 讓應用程式知道哪個物件是新建的,這樣您就會知道何時要使用 if-generation-match:0 先決條件。
  • 記住在執行變異作業之前 GET 呼叫的執行結果。

XML API 中的先決條件

在 XML API 中,產生編號和中繼產生編號會顯示在 x-goog-generationx-goog-metageneration 回應標頭中。這些標頭會以物件的 HEAD 要求回應傳回。

請參閱 HTTP 標頭參考資料,取得您可以使用的先決條件要求標頭完整清單,以便發出將要求物件的狀態列為條件的要求。舉例來說,您可以執行下列操作:

  • 只有在先決條件中的產生編號符合要求物件的產生編號時,才能使用 x-goog-if-generation-match 先決條件執行要求。如果您使用 0 而非產生編號,則只有在值區中沒有任何使用中物件符合要求所指定的物件時,要求才會成功。

  • 只有在標頭中的中繼產生編號符合要求物件的中繼產生編號時,才能使用 x-goog-if-metageneration-match 先決條件執行要求。

  • 搭配 GETHEAD 要求使用 If-Modified-Since 先決條件。只有在最新產生物件的建立時間 (亦即,物件的最後修改版本) 比先決條件中指定的時間更晚時,才會執行這些要求。

  • 搭配 GETHEAD 要求使用 ETag 和 If-MatchIf-None-Match 先決條件。只有在要求物件符合或不符合先決條件中指定的 ETag 時,才會執行這些要求。

  • 在同個要求中使用多個先決條件。例如,如果您使用 x-goog-if-generation-match,您也可以使用 x-goog-if-metageneration-match

JSON API 中的先決條件

在 JSON API 中,您可以透過含有物件值區資源的回應的 generationmetageneration 屬性,取得產生編號和中繼產生編號。物件或值區資源會在適用於物件適用於值區GET 要求回應主體中傳回。

若要在要求中使用先決條件,請將先決條件當成查詢參數加入網址尾端。針對每個先決條件,加入與先決條件同名的查詢參數。例如,下列要求使用 ifGenerationMatch: https://www.googleapis.com/storage/v1/b/testgrid-triage-testing/o/test?ifGenerationMatch=1122334455

在本例中,只有在物件的產生編號是 1122334455 時,API 才會執行此要求。

JSON API 還支援 HTTP 1.1 ETag 以及所有資源 (包括值區、物件和 ACL) 的對應 HTTP If-MatchIf-None-Match 標頭。每當傳回資源,就會在回應標頭中傳回 ETag,且資源本身也含有這個 Etag。

您可以使用下列先決條件範例發出要求,並將要求物件的狀態列為條件。

  • 只有在屬性中的產生編號符合/不符合要求物件的產生編號時,才能使用 ifGenerationMatchifGenerationNotMatch 先決條件執行要求。如果您在 ifGenerationMatch 中使用 0 而非產生編號,則只有在值區沒有任何使用中物件符合要求所指定的物件時,要求才會成功。

  • 使用 ifMetagenerationMatchifMetagenerationNotMatch 先決條件對值區或物件執行要求。只有在屬性中的中繼產生編號符合/不符合所要求值區或物件的中繼產生編號時,要求才會成功。

  • 使用 ETag 及 If-MatchIf-None-Match 先決條件做為要求的標頭。只有在要求物件符合或不符合先決條件中指定的 ETag 時,才會執行這些要求。

  • 在同一要求中使用多個先決條件。例如,如果您在要求物件時使用 ifGenerationMatch,則還可以使用 ifMetagenerationMatch

請注意,ACL 作業不接受產生編號和中繼產生編號先決條件;請改用存取權控管項目資源 ETag。這個 Etag 位於每個存取權控管項目資源中,而此資源可以在所含物件或值區資源中存取。

競爭狀況範例

同時讀取 - 修改 - 寫入

更新值區或物件中繼資料的常用模式包括讀取現行狀態、在本機套用修改版本,以及將修改後的中繼資料傳回 Cloud Storage 進行寫入。如果有兩個以上的獨立程序同時嘗試此序列,則會產生問題。

請考慮下列情況:您想要為協作者新增一個 ACL 項目,讓他們可以存取您的值區。同時,您的同事想要移除另一名不再需要存取值區的協作者。

為此,您和您的同事都會讀取相同的值區中繼資料初始狀態,且各自都對值區的 ACL 項目進行需要的修改。當您將您的修改內容寫回 Cloud Storage 時,系統會正確更新中繼資料。不幸的是,當您的同事上傳他的修改資料後,您的變更就遺失了,因為他沒有考慮到您的更新。因此,您的協作者 ACL 項目將會遺失,她將無法存取您的值區,在沒有回去查看 ACL 項目的情況下,也沒有人知道發生了什麼事。

防止競爭狀況

您和您的同事只要在各自的寫入作業中加入 if-metageneration-match 先決條件,就可以防止這種競爭狀況的發生。在先決條件中,您們兩位都使用值區的中繼產生編號,這是您在初始讀取作業中收到的部分中繼資料。

當您的修改作業新增 ACL 項目時,值區的中繼產生編號會變更。因為現在有使用先決條件,所以當您的同事嘗試寫入他的 ACL 項目版本時,值區的中繼產生編號與先決條件中的編號不符,系統就會透過回應碼 412 Precondition Failed 通知他更新失敗。收到這個回應碼後,您的同事就可以據此做出反應,例如使用更新的中繼資料執行新的「讀取 - 修改 - 寫入」循環。

多次重試要求

Cloud Storage 是一種分散式系統。因為要求可能會因網路或服務的各種狀況而失敗,所以 Google 建議您使用指數輪詢重試失敗要求。但分散式系統的本質有時會讓重試產生令人訝異的行為。

請思考這樣的情況:您想要刪除儲存在 Cloud Storage 中的 file.txt 檔案。之後,您想在 Cloud Storage 中新增同名檔案。

為了達成此目的,您發出刪除要求以刪除此物件。但網路狀況 (例如,中繼路由器暫時遺失連線) 造成要求無法送達 Cloud Storage,所以您沒有收到回應。

因為您沒有收到第一個要求的回應,所以就發出該物件的第二次刪除要求,這次成功了,且您收到確認刪除的回應。一分鐘後,您決定上傳新的 file.txt,且上傳成功。

如果中斷連線的路由器隨後重新連線,並將您原本好像丟失的刪除要求傳送到 Cloud Storage,則會出現競爭狀況。當此要求送達 Cloud Storage 時,要求會成功,因為有新的 file.txt。Cloud Storage 會傳送回應,但您因用戶端停止監聽而不會收到這個回應。結果不僅新檔案遭到刪除 (與您的目的相反),您也不知道發生了第二次刪除。

下圖顯示發生的狀況:

防止競爭狀況

為了防止上述狀況的發生,您一開始應先取得 file.txt 的中繼資料,以便確定目前的產生編號。然後傳送刪除要求,搭配使用產生編號的 if-generation-match 先決條件。使用先決條件可確保不論刪除要求何時送達 Cloud Storage,或傳送了多少次搭配先決條件的刪除要求,都「只有」具備該特定產生編號的物件會遭到刪除。使用 if-generation-match 先決條件,任何嘗試變更不同產生編號的 file.txt 都會失敗,並傳回回應碼 412 Precondition Failed

由於類似的網路中斷問題會造成在刪除要求後發出的上傳要求出現競爭狀況,只要將 if-generation-match:0 套用於上傳要求就可以避免許多競爭狀況。使用這個先決條件可確保重試的上傳作業不會不小心寫入物件兩次,因為先決條件只有在物件沒有任何當前產生編號時,才會允許要求成功。

使用這些先決條件,就可以在執行刪除和上傳要求時防止資料不小心遺失。下圖說明此狀況:

if-generation-match:0 的限制

如果第一個物件遭到刪除,if-generation-match:0 無法防止建立該物件兩次,因為缺少物件不具唯一識別性。請考慮下列情況:沒有任何資料遺失,但您獲得了一個意外的檔案:

  1. 首先,您對 file.txt 的中繼資料發出 GET 要求以找出其產生編號。在回應中,您發現 file.txt 不存在。

  2. 知道這一點後,您使用 if-generation-match:0 先決條件發出上傳 file.txt 的要求,但因中繼路由器連線暫時中斷,要求逾時。

  3. 第一次失敗後,您再次使用 if-generation-match:0 先決條件重試上傳要求。這次要求成功。

  4. 不久後,您傳送刪除 file.txt 的要求,要求成功。

  5. 如果之前連線中斷的路由器現在重新連線,並將您的第一個上傳要求傳送至 Cloud Storage,與此要求一起發出的先決條件仍然符合,所以 file.txt 會重新建立。不論有沒有先決條件,file.txt 都會在您預期之外上傳第二次。

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Cloud Storage
需要協助嗎?請前往我們的支援網頁