AIP-154

Resource freshness validation

APIs often need to validate that a client and server agree on the current state of a resource before taking some kind of action on that resource. For example, two processes updating the same resource in parallel could create a race condition, where the latter process "stomps over" the effort of the former one.

ETags provide a way to deal with this, by allowing the server to send a checksum based on the current content of a resource; when the client sends that checksum back, the server can ensure that the checksums match before acting on the request.

Guidance

A resource may include an etag field on any resource where it is important to ensure that the client has an up to date resource before acting on certain requests:

// A representation of a book.
message Book {
  // Other fields...

  // This checksum is computed by the server based on the value of other
  // fields, and may be sent on update and delete requests to ensure the
  // client has an up-to-date value before proceeding.
  string etag = 99;
}
  • The etag field must be a string, and must be named etag.
  • The etag field should not be given any behavior annotations
  • The etag field must be provided by the server on output, and values should conform to RFC 7232.
  • If a user sends back an etag which matches the current etag value, the service must permit the request (unless there is some other reason for failure).
  • If a user sends back an etag which does not match the current etag value, the service must send a FAILED_PRECONDITION error response (unless another error takes precedence, such as PERMISSION_DENIED if the user is not authorized).
  • If the user does not send an etag value at all, the service should permit the request. However, services with strong consistency or parallelism requirements may require users to send etags all the time and reject the request with an INVALID_ARGUMENT error in this case.

Note: ETag values should include quotes as described in RFC 7232. For example, a valid etag is "foo", not foo.

Declarative-friendly resources

[^reviewing]

A resource that is declarative-friendly (AIP-128) must include an etag field.

Etags on request methods

In some situations, the etag needs to belong on a request message rather than the resource itself. For example, an Update standard method can "piggyback" off the etag field on the resource, but the Delete standard method can not:

message DeleteBookRequest {
  // The name of the book.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "library.googleapis.com/Book"
    }];

  // The current etag of the book.
  // If an etag is provided and does not match the current etag of the book,
  // deletion will be blocked and a FAILED_PRECONDITION error will be returned.
  string etag = 2;
}

An etag field may also be used on custom methods, similar to the example above.

Strong and weak etags

ETags can be either "strongly validated" or "weakly validated":

  • A strongly validated etag means that two resources bearing the same etag are byte-for-byte identical.
  • A weakly validated etag means that two resources bearing the same etag are equivalent, but may differ in ways that the service does not consider to be important.

Resources may use either strong or weak etags, as it sees fit, but should document the behavior. Additionally, weak etags must have a W/ prefix as mandated by RFC 7232.

Further reading

  • For how to retry on errors in client libraries, see AIP-194.

Changelog

  • 2020-10-06: Added declarative-friendly resource requirement.
  • 2020-09-02: Clarified that other errors may take precedence over FAILED_PRECONDITION for etag mismatches.
  • 2020-09-02: Add guidance for etags on request messages.
  • 2019-09-23: Changed the title to "resource freshness validation".