AIP-233

Batch methods: Create

Some APIs need to allow users to create multiple resources in a single transaction. A batch create method provides this functionality.

Guidance

APIs may support Batch Create using the following two patterns:

Returning the response synchronously

rpc BatchCreateBooks(BatchCreateBooksRequest) returns (BatchCreateBooksResponse) {
  option (google.api.http) = {
    post: "/v1/{parent=publishers/*}/books:batchCreate"
    body: "*"
  };
}

Returning an Operation which resolves to the response asynchronously

rpc BatchCreateBooks(BatchCreateBooksRequest) returns (google.longrunning.Operation) {
  option (google.api.http) = {
    post: "/v1/{parent=publishers/*}/books:batchCreate"
    body: "*"
  };
  option (google.longrunning.operation_info) = {
    response_type: "BatchCreateBooksResponse"
    metadata_type: "BatchCreateBooksOperationMetadata"
  };
}
  • The RPC's name must begin with BatchCreate. The remainder of the RPC name should be the plural form of the resource being created.
  • The request and response messages must match the RPC name, with Request and Response suffixes.
  • If the batch method returns an google.longrunning.Operation, both the response_type and metadata_type fields must be specified.
  • The HTTP verb must be POST.
  • The HTTP URI must end with :batchCreate.
  • The URI path should represent the collection for the resource, matching the collection used for simple CRUD operations. If the operation spans parents, a dash (-) may be accepted as a wildcard.
  • The body clause in the google.api.http annotation should be "*".

Atomic vs. Partial Success

  • The batch create method may support atomic (all resources created or none are) or partial success behavior. To make a choice, consider the following factors:
    • Complexity of Ensuring Atomicity: Operations that are simple passthrough database transactions should use an atomic operation, while operations that manage complex resources should use partial success operations.
    • End-User Experience: Consider the perspective of the API consumer. Would atomic behavior be preferable for the given use case, even if it means that a large batch could fail due to issues with a single or a few entries?
  • Synchronous batch create must be atomic.
  • Asynchronous batch create may support atomic or partial success.

Request message

The request for a batch create method should be specified with the following pattern:

message BatchCreateBooksRequest {
  // The parent resource shared by all books being created.
  // Format: publishers/{publisher}
  // If this is set, the parent field in the CreateBookRequest messages
  // must either be empty or match this field.
  string parent = 1 [
    (google.api.resource_reference) = {
      child_type: "library.googleapis.com/Book"
    }];

  // The request message specifying the resources to create.
  // A maximum of 1000 books can be created in a batch.
  repeated CreateBookRequest requests = 2
    [(google.api.field_behavior) = REQUIRED];
}
  • A parent field should be included, unless the resource being created is a top-level resource. If a caller sets this field, and the parent field of any child request message does not match, the request must fail. The parent field of child request messages can be omitted if the parent field in this request is set.
    • This field should be required if only 1 parent per request is allowed.
    • The field should identify the resource type that it references.
    • The comment for the field should document the resource pattern.
  • The request message must include a repeated field which accepts the request messages specifying the resources to create, as specified for standard Create methods. The field should be named requests.
    • The field should be required.
  • Other fields may be "hoisted" from the standard Create request, which means that the field can be set at either the batch level or child request level. Similar to parent, if both the batch level and child request level are set for the same field, the values must match.
    • Fields which must be unique cannot be hoisted (e.g. Customer-provided id fields).
  • The request message must not contain any other required fields, and should not contain other optional fields except those described in this or another AIP.
  • The comment above the requests field should document the maximum number of requests allowed.

Response message

The response for a batch create method should be specified with the following pattern:

message BatchCreateBooksResponse {
  // Books created.
  repeated Book books = 1;
}
  • The response message must include one repeated field corresponding to the resources that were created.

Operation metadata message

  • The metadata_type message must either match the RPC name with OperationMetadata suffix, or be named with Batch prefix and OperationMetadata suffix if the type is shared by multiple Batch methods.
  • If batch create method supports partial success, the metadata message must include a map<int32, google.rpc.Status> failed_requests field to communicate the partial failures.
    • The key in this map is the index of the request in the requests field in the batch request.
    • The value in each map entry must mirror the error(s) that would normally be returned by the singular Standard Create method.
    • If a failed request can eventually succeed due to server side retries, such transient errors must not be communicated using failed_requests.
    • When all requests in the batch fail, Operation.error must be set with code = google.rpc.Code.Aborted and message = "None of the requests succeeded, refer to the BatchCreateBooksOperationMetadata.failed_requests for individual error details"
  • The metadata message may include other fields to communicate the operation progress.

Adopting Partial Success

In order for an existing Batch API to adopt the partial success pattern, the API must do the following:

  • The default behavior must be retained to avoid incompatible behavioral changes.
  • If the API returns an Operation:
    • The request message must have a bool return_partial_success field.
    • The Operation metadata_type must include a map<int32, google.rpc.Status> failed_requests field.
    • When the bool return_partial_success field is set to true in a request, the API should allow partial success behavior, otherwise it should continue with atomic behavior as default.
  • If the API returns a direct response synchronously:
    • Since the existing clients will treat a success response as an atomic operation, the existing version of the API must not adopt the partial success pattern.
    • A new version must be created instead that returns an Operation and follows the partial success pattern described in this AIP.

Rationale

Restricting synchronous batch methods to be atomic

The restriction that synchronous batch methods must be atomic is a result of the following considerations.

The previous iteration of this AIP recommended batch methods must be atomic. There is no clear way to convey partial failure in a sync response status code because an OK implies it all worked. Therefore, adding a new field to the response to indicate partial failure would be a breaking change because the existing clients would interpret an OK response as all resources created.

On the other hand, as described in AIP-193, Operations are more capable of presenting partial states. The response status code for an Operation does not convey anything about the outcome of the underlying operation and a client has to check the response body to determine if the operation was successful.

Communicating partial failures

The AIP recommends using a map<int32, google.rpc.Status> failed_requests field to communicate partial failures, where the key is the index of the failed request in the original batch request. The other options considered were:

  • A repeated google.rpc.Status field. This was rejected because it is not clear which entry corresponds to which request.
  • A map<string, google.rpc.Status> field, where the key is the request id of the failed request. This was rejected because:
    • Client will need to maintain a map of request_id -> request in order to use the partial success response.
    • Populating a request id for the purpose of communicating errors could conflict with AIP-155 if the service can not guarantee idempotency for an individual request across multiple batch requests.
  • A repeated FailedRequest field, where FailedRequest contains the individual create request and the google.rpc.Status. This was rejected because echoing the request payload back in response is discouraged due to additional challenges around user data sensitivity.

Changelog

  • 2025-03-06: Added detailed guidance for partial success behavior, and decision framework for choosing between atomic and partial success
  • 2023-04-18: Changed the recommendation to allow returning partial successes.
  • 2022-06-02: Changed suffix descriptions to eliminate superfluous "-".
  • 2020-09-16: Suggested annotating parent and requests fields.
  • 2020-08-27: Removed parent recommendations for top-level resources.
  • 2019-08-01: Changed the examples from "shelves" to "publishers", to present a better example of resource ownership.