世代番号と前提条件

オブジェクトの世代番号を使用してユーザーはデータリソースを一意に識別し、前提条件を適用して複数ステップのトランザクションのアトミック性を保証することができます。

世代

オブジェクトのバージョニングが有効でない場合であっても、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 を使用すると、ヘッダーのメタ世代番号がリクエストされたオブジェクトのメタ世代番号と一致する場合にのみリクエストが実行されます。

  • GET リクエストまたは HEAD リクエストで前提条件 If-Modified-Since を使用します。これらのリクエストは、最新のオブジェクト作成時点(オブジェクトの最終変更)が、前提条件で指定された時点よりも後である場合にのみ実行されます。

  • GET リクエストまたは HEAD リクエストで ETag と前提条件 If-Match または If-None-Match を使用します。これらのリクエストはそれぞれ、リクエストされたオブジェクトが前提条件で指定された ETag と一致する場合または一致しない場合にのみ実行されます。

  • 同じリクエストで複数の前提条件を使用します。たとえば、x-goog-if-generation-match を使用していて、同時に x-goog-if-metageneration-match も使用できます。

JSON API での前提条件

JSON API では、オブジェクトバケットのリソースを含むレスポンスの generation プロパティと metageneration プロパティを介して、世代番号とメタ世代番号を取得できます。オブジェクトやバケットのリソースは、オブジェクトバケットGET リクエストのレスポンスの本文に入って返されます。

リクエストで前提条件を使用する場合は、その前提条件を URL の末尾にクエリ パラメータとして追加します。前提条件ごとに、それと同じ名前のクエリ パラメータを追加してください。たとえば、次のリクエストには ifGenerationMatch が使用されています: https://www.googleapis.com/storage/v1/b/testgrid-triage-testing/o/test?ifGenerationMatch=1122334455

この例の場合、API によりこのリクエストが実行されるのは、オブジェクトの世代が 1122334455 である場合のみです。

JSON API は、バケット、オブジェクト、ACL などのすべてのリソースのための HTTP 1.1 ETag や HTTP If-Match ヘッダーと If-None-Match ヘッダーをサポートしています。ETag は、リソースが返されると必ずレスポンス ヘッダーの一部として返され、リソースそのものにも含まれます。

以下に、リクエストされたオブジェクトの状態を条件とするリクエストに使用できる前提条件の例を示します。

  • 前提条件 ifGenerationMatchifGenerationNotMatch を使用すると、プロパティの世代番号がリクエストされたオブジェクトの世代番号と一致するまたは一致しない場合にのみリクエストが実行されます。0 で世代番号の代わりに ifGenerationMatch を使用すると、リクエストのオブジェクト名と一致するライブ オブジェクトがバケットに存在しない場合にのみリクエストが成功します。

  • 前提条件 ifMetagenerationMatchifMetagenerationNotMatch を使用すると、バケットまたはオブジェクトでリクエストが実行されます。このようなリクエストはそれぞれ、プロパティのメタ世代番号が、リクエストされたバケットやオブジェクトのメタ世代番号と一致するまたは一致しない場合にのみ成功します。

  • リクエストのヘッダーとして、ETag と前提条件 If-Match または If-None-Match を使用します。これらのリクエストはそれぞれ、リクエストされたオブジェクトが前提条件で指定された ETag と一致する場合または一致しない場合にのみ実行されます。

  • 同じリクエストで複数の前提条件を使用します。たとえばオブジェクトのリクエスト中に ifGenerationMatch を使用していて、同時に ifMetagenerationMatch も使用できます。

世代番号やメタ世代番号の前提条件は、ACL オペレーションでは受け付けられません。代わりに access-control エントリ リソース ETag を使用してください。この ETag は、保持されているオブジェクトまたはバケットのリソースからアクセス可能な各 access-control エントリ リソース内にあります。

競合状態の例

読み取り - 変更 - 書き込みの同時実行

バケットやオブジェクトのメタデータ更新のための一般的なパターンには、現在の状態の読み取り、ローカルでの変更の適用、変更されたメタデータの Cloud Storage への送信と書き込みが含まれます。2 つ以上の独立したプロセスがシーケンスを同時に試行する場合、このパターンは不安定になることがあります。

たとえば、共同編集者がバケットにアクセスできるようにするための 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 に到達せず、レスポンスがありません。

最初のリクエストへのレスポンスがないため、オブジェクト削除のための 2 つめのリクエストを発行します。このリクエストは成功し、削除を確認するレスポンスが返されます。1 分後、新しい file.txt をアップロードすることになり、アップロードが成功します。

後からルーターの接続不良が回復し、失われたと思われていた元の削除リクエストが Cloud Storage に送信されると、競合状態が生じます。新しい file.txt が存在するため、Cloud Storage に送信されたリクエストは成功します。Cloud Storage はレスポンスを送信しますが、このレスポンスは受信されません。これはクライアントがリスニングを停止しているためです。新しいファイルが削除されるだけでなく、意図に反して 2 度目の削除が行われたことに気づけないことになります。

次の図に、この状況の詳細を示します。

競合状態の防止

上記の状況を回避するには、file.txt のメタデータを取得して、現在の世代を確認する必要があります。その後、世代番号を使用する前提条件 if-generation-match で削除リクエストを送信します。前提条件を使用すると、削除リクエストが Cloud Storage に到達するタイミングや削除リクエストが送信された回数にかかわらず、指定の世代番号を持つオブジェクトのみが削除されるようになります。前提条件 if-generation-match により、世代の異なる file.txt を変更しようとする意図せぬ試行は失敗し、レスポンス コード 412 Precondition Failed が返されるようになります。

削除リクエストの後のアップロード リクエストでも同様のネットワーク中断が競合状態を生じさせる可能性があります。これらの競合状態は、アップロード リクエストに if-generation-match:0 を適用して防止できます。前提条件を使用すると、アップロードの再試行によって誤ってオブジェクトが 2 回書き込まれることがないようになります。これは、前提条件によって、現在の世代のオブジェクトが存在しない場合にのみリクエストが成功するようになるためです。

これらの前提条件を使用することで、削除リクエストやアップロード リクエストを実行する際にデータが誤って失われることがなくなります。次の図に詳細を示します。

if-generation-match:0 の制限

最初のオブジェクトが削除された場合、if-generation-match:0 でオブジェクトの作成が 2 度行われることを防ぐことはできません。これは、オブジェクトの不在を一意に識別できないためです。たとえば次のような場合、データは失われていないものの、ファイルの状態は想定と異なるものになります。

  1. 最初に、GET のメタデータに対する file.txt リクエストによって世代番号を確認します。レスポンスには file.txt が存在しないことが示されます。

  2. ファイルが存在しないことを確認したので、前提条件 file.txtif-generation-match:0 のアップロードをリクエストします。しかし、中間ルーターが一時的に接続不良を起こしているためにリクエストがタイムアウトします。

  3. 1 度失敗したため、アップロード リクエストを前提条件 if-generation-match:0 で再試行します。このリクエストは成功します。

  4. すぐに file.txt の削除リクエストを送信し、リクエストが成功します。

  5. ルーターの接続不良が回復し、Cloud Storage への最初のアップロード リクエストが送信されると、リクエストの前提条件は引き続き適合するため、file.txt が再度作成されます。前提条件の有無に関係なく、file.txt が意図せずもう一度アップロードされます。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Cloud Storage ドキュメント