要求先決條件

本頁將討論要求先決條件,您可以使用這些條件,在資源處於非預期狀態時,防止要求套用至資源。

簡介

在對 Cloud Storage 提出要求時使用先決條件,只有在目標資源符合先決條件中定義的條件時,要求才會繼續執行。先決條件檢查可確保值區或物件處於預期狀態,讓您安全地對物件執行「讀取 - 修改 - 寫入」更新和條件作業。

我們常在變異要求 (例如上傳、刪除或中繼資料更新) 中使用先決條件,以避免發生競爭狀況。如果重複傳送相同的要求,或獨立程序各自嘗試修改同一項資源,可能會發生競爭狀況。詳情請參閱「競爭情況和資料損毀的範例」。在連續要求中擷取物件中繼資料和資料時,也常會使用先決條件,確保物件在兩次要求之間沒有變更。

先決條件

Cloud Storage 支援在前提條件中使用多種不同的不可變更資源屬性:

下表列出 JSON API 和 XML API 支援的先決條件:

JSON API XML API 說明
ifGenerationMatch 查詢參數 x-goog-if-generation-match 標題 如果目標資源的 generation 符合先決條件中使用的值,要求就會繼續執行。如果值不相符,要求就會失敗並傳回 412 Precondition Failed 回應。
ifMetagenerationMatch 查詢參數 x-goog-if-metageneration-match 標題 如果目標資源的 metageneration 符合先決條件中使用的值,要求就會繼續執行。如果值不相符,要求就會失敗並傳回 412 Precondition Failed 回應。
ifGenerationNotMatch 查詢參數 不適用 如果目標資源的 generation 與先決條件中使用的值不符,要求就會繼續執行。如果值相符,要求就會失敗,並傳回 304 Not Modified 回應。
ifMetagenerationNotMatch 查詢參數 不適用 如果目標資源的 metageneration 與先決條件中使用的值不符,要求就會繼續執行。如果值相符,要求就會失敗,並傳回 304 Not Modified 回應。
If-Match 標題 If-Match 標題 適用於擷取資料的要求。如果目標資源的 ETag 符合先決條件中使用的值,要求就會繼續執行。如果值不相符,要求就會失敗並傳回 412 Precondition Failed 回應。
If-None-Match 標題 If-None-Match 標題 適用於擷取資料的要求。如果目標資源的 ETag 與先決條件中使用的值不符,要求就會繼續執行。如果值相符,要求就會失敗,並傳回 304 Not Modified 回應。
不適用 If-Modified-Since 標題 如果目標資源的 Last-Modified 日期晚於先決條件中使用的值,要求就會繼續執行。如果目標資源不符合這項前提條件,要求就會失敗並傳回 304 Not Modified 回應。
不適用 If-Unmodified-Since 標題 如果目標資源的 Last-Modified 日期早於或等於先決條件中使用的值,要求就會繼續執行。如果目標資源不符合這項前提條件,要求就會失敗並傳回 412 Precondition Failed 回應。

物件組合前置條件

執行物件組合時,JSON APIXML API 都支援下列功能:

  • 目的地物件的 generation-match 和 metageneration-match 先決條件。

  • 每個來源物件的產生版本比對先決條件。使用這項前置條件可防止獨立程序覆寫組合的預期元件時,使用到不正確的元件。如果您使用先決條件,且發生這類覆寫作業,compose 作業就會失敗,並傳回 412 Precondition Failed 回應。

物件副本的前置條件

在 Cloud Storage 中複製或重寫物件時,JSON APIXML API 都支援對目標物件使用標準前提條件。每個 API 都額外支援來源物件的前置條件:

  • JSON API 支援來源物件的產生和中繼產生前提條件,這些條件是透過以 ifSource 為前置字元的查詢參數指定。

  • XML API 支援的所有先決條件,都可用於來源物件。這些前提條件會指定在以 x-goog-copy-source- 為前置字元的標頭中。

生成比對先決條件中的 0

生成相符前提條件接受值 0 做為特殊情況。如果要求中包含值為 0 的 generation-match 先決條件,則只有在值區中沒有指定名稱的物件,或值區中只有物件的非目前版本時,要求才會繼續執行。如果使用指定名稱的版本為使用中版本,要求就會失敗,並顯示 412 Precondition Failed 狀態碼。

最佳做法和注意事項

  • 您可以在單一要求中使用多個先決條件。如果未滿足任何前提條件,整體要求就會失敗。

  • 值區沒有產生編號,但有中繼產生編號。您不應在 bucket 要求中使用指定產生編號的先決條件。

  • 如果您在物件要求中使用中繼產生先決條件,請務必同時使用產生先決條件。這樣可避免要求在其他物件上成功執行,因為該物件剛好具有通過先決條件的中繼產生編號。

  • 如果值區同時有使用中和非現行物件版本,除非要求中明確包含產生版本編號,否則物件要求不適用於非現行版本。也就是說,如果一般要求使用先決條件,且使用中的版本不符合先決條件,要求就會失敗,無論非現行版本是否符合先決條件都一樣。

  • 一般來說,您應使用產生編號和中繼產生編號先決條件,而非 ETag 先決條件。generation 和 metageneration 號碼兩者搭配使用就能追蹤所有物件更新 (包括中繼資料變更),比使用 ETag 更可靠。此外,產生編號和中繼產生編號在各個 API 中都一致,但 ETag 並非如此

  • 先決條件無法用於 XML API 多部分上傳作業。嘗試停用會導致 400 NotImplemented 錯誤。

先決條件的成本

許多使用先決條件的架構都要求您在主要要求之前,先提出物件中繼資料要求,以判斷目前的產生編號和/或中繼產生編號:

  • 額外要求表示您可能會增加額外的往返傳送時間,導致整體作業延遲時間中的網路延遲部分加倍,這對易受延遲時間影響的作業來說是個重要的因素。

依您的應用程式而定,有些方法可減少使用先決條件的影響,例如:

  • 在本機儲存物件的產生編號和中繼產生編號,這樣您就會知道先決條件所使用的正確編號。
  • 讓應用程式知道哪個物件是新建的,這樣您就會知道何時要使用 if-generation-match:0 先決條件。

範例:使用前提條件

以下範例在要求中使用了 generation-match 先決條件,用來上傳物件。如要繼續處理要求,值區中必須已儲存具有指定名稱的物件,且現有物件的產生編號必須與先決條件中提供的編號相符:

指令列

使用 --if-generation-match 旗標和一般指令:

gcloud storage cp OBJECT_LOCATION gs://DESTINATION_BUCKET_NAME --if-generation-match=GENERATION

其中:

  • GENERATION 是您要取代的物件預期產生編號。例如:1122334455667788

  • OBJECT_LOCATION 是物件的本機路徑。例如:Desktop/dog.png

  • DESTINATION_BUCKET_NAME 是您要向其上傳物件的值區名稱。例如:my-bucket

JSON API

  1. 安裝並初始化 gcloud CLI,以便為 Authorization 標頭產生存取權杖。

  2. 使用 cURL 透過 POST 物件要求呼叫 JSON API:

    curl -X POST --data-binary @OBJECT_LOCATION \
      -H "Authorization: Bearer $(gcloud auth print-access-token)" \
      -H "Content-Type: OBJECT_CONTENT_TYPE" \
      "https://storage.googleapis.com/upload/storage/v1/b/BUCKET_NAME/o?uploadType=media&name=OBJECT_NAME"&ifGenerationMatch=GENERATION"

    其中:

    • OBJECT_LOCATION 是物件的本機路徑。例如:Desktop/dog.png
    • OBJECT_CONTENT_TYPE 是物件的內容類型。例如:image/png
    • BUCKET_NAME 是您要向其上傳物件的值區名稱。例如:my-bucket
    • OBJECT_NAME 是您要為物件命名的名稱。例如:dog.png
    • GENERATION 是您要取代的物件預期產生編號。例如:1122334455667788

XML API

  1. 安裝並初始化 gcloud CLI,以便為 Authorization 標頭產生存取權杖。

  2. 使用 cURL 透過 PUT 物件要求呼叫 XML API:

    curl -X PUT --data-binary @OBJECT_LOCATION \
      -H "Authorization: Bearer $(gcloud auth print-access-token)" \
      -H "Content-Type: OBJECT_CONTENT_TYPE" \
      -H "x-goog-if-generation-match: GENERATION" \
      "https://storage.googleapis.com/BUCKET_NAME/OBJECT_NAME"

    其中:

    • OBJECT_LOCATION 是物件的本機路徑。例如:Desktop/dog.png
    • OBJECT_CONTENT_TYPE 是物件的內容類型。例如:image/png
    • GENERATION 是您要取代的物件預期產生編號。例如:1122334455667788
    • BUCKET_NAME 是您要向其上傳物件的值區名稱。例如:my-bucket
    • OBJECT_NAME 是您要為物件命名的名稱。例如:dog.png

使用前提條件的情境

下列情境探討競爭狀況和快取範例,這些範例可從使用前提條件中獲益。

多次重試要求

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

請考慮下列情況:您想要刪除儲存在其中一個值區中的物件 file.txt。之後,您想在值區中新增同名物件。如要達成此目的,請發出刪除要求來刪除物件。但網路狀況 (例如,中繼路由器暫時遺失連線) 造成要求無法送達 Cloud Storage,所以您沒有收到回應。

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

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

下圖顯示發生的狀況:

防止競爭狀況

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

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

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

物件中繼資料關聯

物件的資料和中繼資料是個別實體,兩者共同定義 Cloud Storage 中的物件。由於物件資料和中繼資料是分開儲存,因此您在處理物件中繼資料時,物件資料可能會變更。

請參考以下案例:

  • 您想下載物件的中繼資料和資料,但必須透過兩項獨立要求從 Cloud Storage 擷取。您先要求物件中繼資料,但獨立程序或使用者在您要求物件資料前,取代了該物件。您仍可成功要求物件資料,但現在您擁有舊物件的中繼資料和新物件的資料。

  • 您想更新物件的中繼資料,因此擷取物件的目前中繼資料,以判斷物件的目前狀態。在您傳送要求以更新中繼資料並進行所需修改之前,獨立程序或使用者會取代物件。您仍可成功變更新物件的中繼資料,但現在會與您預期的物件資料建立關聯。

防止競爭狀況

為避免發生這種情況,您應使用初始要求傳回的物件中繼資料產生編號,然後在第二個要求的 generation-match 先決條件中使用這個值。這麼做可確保中繼資料與資料正確相符,或第二次要求會因 412 Precondition Failed 回應碼而失敗,讓您為新物件要求正確的中繼資料。

如果您擔心物件中繼資料可能會在第一次和第二次要求之間變更,也可以複製初始要求中的中繼產生編號,並在第二次要求中以中繼產生編號比對先決條件使用。

本地副本新鮮度

如果您有儲存在 Cloud Storage 中的物件本機副本,通常會希望本機副本與儲存在 bucket 中的副本保持同步。不過,如果儲存在值區中的物件沒有變更,您就不想浪費時間和資源再次下載,尤其是大型物件。

如要避免不必要地下載仍為最新的內容,您可以將本機副本的產生編號做為 generation-not-match 先決條件中的值,並將該先決條件納入下載要求:

  • 如果值區中的資料與本機副本相符,系統會比對生成編號,導致前提條件失敗。因此,整體要求會失敗並傳回 304 Not Modified 回應,且不會不必要地下載資料。

  • 如果 bucket 中的資料已變更,產生編號不符,先決條件就會成功。這表示整體要求會正常進行,並下載更新版本的內容。

後續步驟