要求先決條件

本頁面說明要求前置條件,可用於防止要求在資源處於非預期狀態時套用至資源。

簡介

在向 Cloud Storage 提出的要求中使用前置條件時,只有在目標資源符合前置條件中定義的條件時,要求才會繼續執行。前置條件檢查可確保 bucket 或物件處於預期狀態,讓您安全地執行讀取、修改及寫入更新,以及有條件的作業。

前置條件通常用於防止變更要求 (例如上傳、刪除或中繼資料更新) 發生競爭狀況。如果重複傳送相同要求,或獨立程序嘗試修改相同資源,就可能發生競爭狀況。詳情請參閱「競爭狀況和資料損毀範例」。在連續要求中擷取物件中繼資料和資料時,也常會使用前置條件,確保物件在兩次要求之間沒有變更。

先決條件

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 API 和 XML API 都支援下列項目:

  • 目的地物件的 generation-match 和 metageneration-match 前置條件。

  • 每個來源物件的生成比對前提條件。使用這項前置條件,可避免在獨立程序覆寫組合結構的其中一個預期元件時,使用不正確的元件。如果您使用前提條件,且發生這類覆寫作業,compose 作業就會失敗,並傳回 412 Precondition Failed 回應。

物件複製前置條件

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

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

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

generation-match 先決條件中的 0 值

generation-match 前置條件接受 0 值做為特殊情況。如果要求中包含值為 0 的產生版本相符前提條件,則只有在值區中沒有指定名稱的物件,或值區中只有非目前版本的物件時,要求才會繼續。如果指定名稱的即時版本存在,要求會失敗,並傳回 412 Precondition Failed 狀態碼。

最佳做法和注意事項

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

  • 值區沒有版本編號,但有中繼資料版本編號。請勿在 bucket 要求中使用指定版本編號的前置條件。

  • 如果在物件要求中使用中繼層代前置條件,請務必一併使用層代前置條件。這樣一來,如果其他物件的中繼資料版本編號剛好符合前置條件,要求就不會成功。

  • 如果 bucket 同時有使用中和非現行的物件版本,除非要求中明確包含版本編號,否則物件要求不適用於非現行版本。這表示對於使用前置條件的一般要求,如果使用中版本與前置條件不符,要求就會失敗,無論非現行版本是否與前置條件相符。

  • 一般來說,建議您使用世代和中繼世代前置條件,而非 ETag 前置條件。生成編號和中繼資料版本編號會共同追蹤所有物件更新 (包括中繼資料變更),提供比 ETag 更強大的保證。此外,生成和中繼資料版本編號在各個 API 中是一致的,但 ETag 則否。

  • XML API 多部分上傳不支援前置條件。嘗試這麼做會導致 400 NotImplemented 錯誤。

先決條件的成本

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

  • 額外要求代表您最多可以增加額外的來回時間,將整體作業延遲時間的網路部分增加一倍,這可能是延遲時間敏感作業的重要因素。

視應用程式而定,您可以採取一些方法來減少使用前提條件的影響,例如:

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

範例:使用前提條件

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

指令列

使用 --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 是要上傳物件的 bucket 名稱。例如: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 是您要上傳物件的 bucket 名稱。例如: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 是您要上傳物件的 bucket 名稱。例如:my-bucket。
    • OBJECT_NAME 是您要為物件指定的名稱。例如:dog.png。

使用前置條件的情境

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

多次重試要求

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

假設您想刪除儲存在其中一個 bucket 的物件 file.txt。之後,您想在 bucket 中新增同名的新物件。為此,您發出刪除要求來刪除物件。不過,網路狀況 (例如中繼路由器暫時失去連線) 會導致要求無法傳送至 Cloud Storage,因此您不會收到回應。

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

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

下圖顯示發生的狀況:

防止競爭狀況

為避免上述情況發生,請先取得 file.txt 的中繼資料,判斷目前的世代。接著,您可以在刪除要求中加入世代比對前置條件,並使用該世代。這項前置條件可確保「只有」具有該特定版本編號的物件會遭到刪除,無論刪除要求何時傳送至 Cloud Storage,或傳送了多少次具有前置條件的刪除要求。如果嘗試刪除其他生成版本的 file.txt,系統會傳回 412 Precondition Failed 回應代碼,表示作業失敗。

類似的網路中斷可能會導致刪除要求後的上傳要求發生競爭狀況,因此您可以在上傳要求中加入世代比對前置條件,並使用 0 值,避免發生許多這類競爭狀況。使用這項前置條件可確保重試上傳時不會意外寫入物件兩次,因為只有在物件目前沒有任何世代時,前置條件才會允許要求繼續執行。

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

物件中繼資料關聯

物件的資料和中繼資料是獨立的實體,兩者共同定義 Cloud Storage 中的物件。由於兩者各自獨立,因此在處理物件中繼資料時,物件資料可能會發生變更。

請參考下列案例:

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

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

防止競爭狀況

為避免發生這種情況,您應使用物件中繼資料的初始要求中傳回的世代編號,然後在第二個要求中,將這個值用於世代比對前提條件。這樣一來,中繼資料就能與資料正確對應,或第二個要求失敗並傳回 412 Precondition Failed 回應代碼,讓您要求新物件的正確中繼資料。

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

本機副本更新間隔

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

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

  • 如果 bucket 中的資料仍與本機副本相符,版本編號就會相符,導致前置條件失敗。因此,整體要求會失敗並傳回 304 Not Modified 回應,且不會不必要地下載資料。

  • 如果 bucket 中的資料已變更,版本編號不相符,則前置條件成功。也就是說,整體要求會正常進行,並下載更新版本的內容。

後續步驟