一括リクエストの送信

このドキュメントでは、JSON API 呼び出しをバッチ処理し、Cloud Storage へのアクセス時にクライアントが行う HTTP 接続の数を減らす方法について説明します。

概要

クライアントで HTTP 接続を行うたびに、ある程度のオーバーヘッドが発生します。Cloud Storage JSON API では一括処理がサポートされており、クライアントで複数の API 呼び出しを 1 つの HTTP リクエストにまとめることができます。

バッチ処理は次のような状況で使用します。

  • 多くのオブジェクトで権限などのメタデータを更新する場合
  • 多くのオブジェクトを削除する場合

いずれの場合も、各呼び出しを個別に送信するのではなく、1 つの HTTP リクエストにまとめることができます。内部リクエストはすべて Cloud Storage JSON API に送信する必要があります。

1 つのバッチ リクエストには、100 を超える呼び出しを含めないでください。これよりも多くの呼び出しを行う必要がある場合は、複数のバッチ リクエストを使用します。バッチ リクエストのペイロードの合計は 10 MB 未満にする必要があります。

バッチ リクエストの詳細

バッチ リクエストは 1 つの HTTP リクエストにまとめられた複数の API 呼び出しで構成され、Cloud Storage バッチ エンドポイントhttps://storage.googleapis.com/batch/storage/v1)に送信できます。このセクションでは、バッチ リクエストの構文について詳しく説明し、最後にを紹介します。

バッチ リクエストの形式

バッチ リクエストは、複数の Cloud Storage JSON API 呼び出しを含む単一の標準 HTTP リクエストです。このメイン リクエストでは、multipart/mixed コンテンツ タイプを使用します。メインの HTTP リクエスト内には、ネストされた HTTP リクエストを含む複数のパーツがあります。

各パーツはそれぞれ Content-Type: application/http という形式の HTTP ヘッダーで始まり、オプションで Content-ID ヘッダーを指定することもできます。これらのヘッダーはパーツの先頭を表しますが、ネストされた HTTP リクエストとは分かれています。つまり、サーバーがバッチ リクエストを個別のリクエストにアンラップした後、パーツヘッダーは無視されます。

各パーツのボディは、それ自体が独自の動詞、URL、ヘッダー、ボディを持つ完全な HTTP リクエストになっています。これらの HTTP リクエストには、URL のパス部分のみを含める必要があります。完全な URL では未定義の動作が発生することがあります。

外側のバッチ リクエストの HTTP ヘッダーは、Content-Type などの Content- ヘッダーを除き、ネストされたすべてのリクエストに適用されます。ただし、外部リクエストとネストされたリクエストの両方で特定の HTTP ヘッダーを指定した場合、ネストされたリクエストのヘッダー値により、そのリクエストに対する外部バッチ リクエスト ヘッダーの値がオーバーライドされます。

たとえば、特定のネストされたリクエストに Authorization ヘッダーを指定した場合、そのヘッダーは指定したリクエストにのみ適用されます。外側のリクエストに Authorization ヘッダーを指定した場合、ネストされたヘッダーは、独自の Authorization ヘッダーでオーバーライドしない限り、すべてのネストされたリクエストに適用されます。

Cloud Storage がバッチ リクエストを受信すると、外側のリクエストのクエリ パラメータとヘッダー(指定した場合)を各パーツに適用し、各パーツを個別の HTTP リクエストとして処理します。

バッチ リクエストへのレスポンス

Cloud Storage のレスポンスは、multipart/mixed コンテンツ タイプを含む単一の標準 HTTP レスポンスです。このメイン レスポンスの各パーツは、バッチ リクエスト内のいずれかのリクエストに対するレスポンスです。レスポンスの順序はリクエストと同じです。

リクエストと同様に、レスポンスの各パートにはステータス コード、ヘッダー、本文などの完全な HTTP レスポンスが含まれます。また、リクエストの同様に、レスポンスの各パートはその開始箇所を表す Content-Type で始まります。ステータス コードの詳細については、Cloud Storage JSON API の HTTP ステータスとエラーコードをご覧ください。

リクエストのパートに Content-ID ヘッダーがある場合、対応するレスポンスのパートには、それに対応する Content-ID ヘッダーが含まれます。レスポンスの Content-ID ヘッダーは response- で始まり、その後にリクエストで使用されている Content-ID 値が続きます(を参照)。

次のバッチ例では、example-bucket 内の 3 つのオブジェクトのカスタム メタデータを更新します。

バッチ HTTP リクエストの例

HTTP

POST /batch/storage/v1 HTTP/1.1
Host: storage.googleapis.com
Content-Length: 960
Content-Type: multipart/mixed; boundary="===============7330845974216740156=="
Authorization: Bearer ya29.AHES6ZRVmB7fkLtd1XTmq6mo0S1wqZZi3-Lh_s-6Uw7p8vtgSwg

--===============7330845974216740156==
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: <b29c5de2-0db4-490b-b421-6a51b598bd22+1>

PATCH /storage/v1/b/example-bucket/o/obj1 HTTP/1.1
Content-Type: application/json
accept: application/json
content-length: 31

{"metadata": {"type": "tabby"}}
--===============7330845974216740156==
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: <b29c5de2-0db4-490b-b421-6a51b598bd22+2>

PATCH /storage/v1/b/example-bucket/o/obj2 HTTP/1.1
Content-Type: application/json
accept: application/json
content-length: 32

{"metadata": {"type": "tuxedo"}}
--===============7330845974216740156==
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: <b29c5de2-0db4-490b-b421-6a51b598bd22+3>

PATCH /storage/v1/b/example-bucket/o/obj3 HTTP/1.1
Content-Type: application/json
accept: application/json
content-length: 32

{"metadata": {"type": "calico"}}
--===============7330845974216740156==--

クライアント ライブラリ

C++

C++ クライアント ライブラリはバッチ リクエストをサポートしていません。

C#

C# クライアント ライブラリはバッチ リクエストをサポートしていません。

Go

Go クライアント ライブラリはバッチ リクエストをサポートしていません。

Java

詳細については、Cloud Storage Java API のリファレンス ドキュメントをご覧ください。

import com.google.api.gax.paging.Page;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.StorageBatch;
import com.google.cloud.storage.StorageOptions;
import java.util.HashMap;
import java.util.Map;

public class BatchSetObjectMetadata {
  public static void batchSetObjectMetadata(
      String projectId, String bucketName, String directoryPrefix) {
    // The ID of your GCP project
    // String projectId = "your-project-id";

    // The ID of your GCS bucket
    // String bucketName = "your-unique-bucket-name";

    // The directory prefix. All objects in the bucket with this prefix will have their metadata
    // updated
    // String directoryPrefix = "yourDirectory/";

    Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
    Map<String, String> newMetadata = new HashMap<>();
    newMetadata.put("keyToAddOrUpdate", "value");
    Page<Blob> blobs =
        storage.list(
            bucketName,
            Storage.BlobListOption.prefix(directoryPrefix),
            Storage.BlobListOption.currentDirectory());
    StorageBatch batchRequest = storage.batch();

    // Add all blobs with the given prefix to the batch request
    for (Blob blob : blobs.iterateAll()) {
      batchRequest.update(blob.toBuilder().setMetadata(newMetadata).build());
    }

    // Execute the batch request
    batchRequest.submit();

    System.out.println(
        "All blobs in bucket "
            + bucketName
            + " with prefix '"
            + directoryPrefix
            + "' had their metadata updated.");
  }
}

Node.js

Node.js クライアント ライブラリはバッチ リクエストをサポートしていません。

PHP

PHP クライアント ライブラリはバッチ リクエストをサポートしていません。

Python

詳細については、Cloud Storage Python API のリファレンス ドキュメントをご覧ください。


from google.cloud import storage


def batch_request(bucket_name, prefix=None):
    """
    Use a batch request to patch a list of objects with the given prefix in a bucket.

    Note that Cloud Storage does not support batch operations for uploading or downloading.
    Additionally, the current batch design does not support library methods whose return values
    depend on the response payload.
    See https://cloud.google.com/python/docs/reference/storage/latest/google.cloud.storage.batch
    """
    # The ID of your GCS bucket
    # bucket_name = "my-bucket"
    # The prefix of the object paths
    # prefix = "directory-prefix/"

    client = storage.Client()
    bucket = client.bucket(bucket_name)

    # Accumulate in a list the objects with a given prefix.
    blobs_to_patch = [blob for blob in bucket.list_blobs(prefix=prefix)]

    # Use a batch context manager to edit metadata in the list of blobs.
    # The batch request is sent out when the context manager closes.
    # No more than 100 calls should be included in a single batch request.
    with client.batch():
        for blob in blobs_to_patch:
            metadata = {"your-metadata-key": "your-metadata-value"}
            blob.metadata = metadata
            blob.patch()

    print(
        f"Batch request edited metadata for all objects with the given prefix in {bucket.name}."
    )

Ruby

Ruby でバッチ リクエストを行う方法については、Cloud Storage Ruby API のリファレンス ドキュメントをご覧ください。

バッチ HTTP レスポンスの例

これは、前のセクションの HTTP リクエスト例に対するレスポンスです。

HTTP/1.1 200 OK
Content-Type: multipart/mixed; boundary=batch_pK7JBAk73-E=_AA5eFwv4m2Q=
Date: Mon, 22 Jan 2018 18:56:00 GMT
Expires: Mon, 22 Jan 2018 18:56:00 GMT
Cache-Control: private, max-age=0
Content-Length: 3767

--batch_pK7JBAk73-E=_AA5eFwv4m2Q=
Content-Type: application/http
Content-ID: <response-b29c5de2-0db4-490b-b421-6a51b598bd22+1>

HTTP/1.1 200 OK
ETag: "lGaP-E0memYDumK16YuUDM_6Gf0/V43j6azD55CPRGb9b6uytDYl61Y"
Content-Type: application/json; charset=UTF-8
Date: Mon, 22 Jan 2018 18:56:00 GMT
Expires: Mon, 22 Jan 2018 18:56:00 GMT
Cache-Control: private, max-age=0
Content-Length: 846

{
 "kind": "storage#object",
 "id": "example-bucket/obj1/1495822576643790",
 .
 .
 .
 "metadata": {
  "type": "tabby"
  },
  .
  .
  .
}

--batch_pK7JBAk73-E=_AA5eFwv4m2Q=
Content-Type: application/http
Content-ID: <response-b29c5de2-0db4-490b-b421-6a51b598bd22+2>

HTTP/1.1 200 OK
ETag: "lGaP-E0memYDumK16YuUDM_6Gf0/91POdd-sxSAkJnS8Dm7wMxBSDKk"
Content-Type: application/json; charset=UTF-8
Date: Mon, 22 Jan 2018 18:56:00 GMT
Expires: Mon, 22 Jan 2018 18:56:00 GMT
Cache-Control: private, max-age=0
Content-Length: 846

{
 "kind": "storage#object",
 "id": "example-bucket/obj2/1495822576643790",
 .
 .
 .
 "metadata": {
  "type": "tuxedo"
  },
  .
  .
  .
}

--batch_pK7JBAk73-E=_AA5eFwv4m2Q=
Content-Type: application/http
Content-ID: <response-b29c5de2-0db4-490b-b421-6a51b598bd22+3>

HTTP/1.1 200 OK
ETag: "lGaP-E0memYDumK16YuUDM_6Gf0/d2Z1F1_ZVbB1dC0YKM9rX5VAgIQ"
Content-Type: application/json; charset=UTF-8
Date: Mon, 22 Jan 2018 18:56:00 GMT
Expires: Mon, 22 Jan 2018 18:56:00 GMT
Cache-Control: private, max-age=0
Content-Length: 846

{
 "kind": "storage#object",
 "id": "example-bucket/obj3/1495822576643790",
 .
 .
 .
 "metadata": {
  "type": "calico"
  },
  .
  .
  .
}

--batch_pK7JBAk73-E=_AA5eFwv4m2Q=--

リクエスト全体の形式が正しくなく、Cloud Storage でリクエストを解析してサブリクエストにできない場合は、400 エラーが返されます。それ以外の場合は、サブリクエストの一部またはすべてが失敗した場合でも、Cloud Storage から 200 ステータス コードが返されます。

リクエスト全体が 200 ステータス コードを返す場合、レスポンスにはステータス コードと各サブリクエストの結果が含まれ、サブリクエストが成功したかどうかを示します。たとえば、オブジェクトを一括削除する場合は、成功したサブリクエストごとに 204 No Content ステータス コードが含まれます。