You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: azure/ConsiderationsForServiceDesign.md
+30-2Lines changed: 30 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -148,6 +148,7 @@ The following are recommended names for properties that match the associated des
148
148
| lastModifiedAt | The date and time the resource was last modified. |
149
149
| deletedAt | The date and time the resource was deleted. |
150
150
| kind | The discriminator value for a polymorphic resource |
151
+
| etag | The entity tag used for optimistic concurrency control, when included as a property of a resource. |
151
152
152
153
### `name` vs `id`
153
154
@@ -172,7 +173,7 @@ Before releasing your API plan to invest significant design effort, get customer
172
173
As your service evolves over time, it will be natural that you want to remove operations that are no longer needed. For example, additional requirements or new capability in your service, may have resulted in a new operation that, effectively, replaces an old one.
173
174
Azure has a well established breaking changes policy that describes how to approach these kinds of changes. As part of this policy, the service team is required to clearly communicate to customers when their API is changing, e.g. deprecating operations. Often, this is done via an email to the address that is attached to the Azure subscription.
174
175
175
-
However, given how many organizations are structured, it's common that this email address is different from the actual people writing code against your API. To address this, the service API should declare that it may return the `azure-deprecating` header, to indicate that this operation will be removed in the future. There is a simple string convention, specifed in the [Azure REST API Guidelines](https://aka.ms/azapi/guidelines) that provides more information about the forthcoming deprecation.
176
+
However, given how many organizations are structured, it's common that this email address is different from the actual people writing code against your API. To address this, the service API should declare that it may return the `azure-deprecating` header, to indicate that this operation will be removed in the future. There is a simple string convention, specified in the [Azure REST API Guidelines](https://aka.ms/azapi/guidelines#deprecating-behavior-notification) that provides more information about the forthcoming deprecation.
176
177
This header is targeted at developers or operation professionals, and it is intended to give them enough information and lead time to properly adapt to this change. Your documentation should reference this header and encourage logging and alerting practices based on its presence.
177
178
178
179
## Avoid Surprises
@@ -234,7 +235,7 @@ PATCH must never be used for long-running operations -- it should be reserved fo
234
235
If a long-running update is required it should be implemented with POST.
235
236
236
237
There is a special form of long-running operation initiated with PUT that is described
237
-
in [Create (PUT) with additional long-running processing](#create-put-with-additional-long-running-processing).
238
+
in [Create (PUT) with additional long-running processing](./Guidelines.md#put-operation-with-additional-long-running-processing).
238
239
The remainder of this section describes the pattern for long-running POST and DELETE operations.
239
240
240
241
This diagram illustrates how a long-running operation with a status monitor is initiated and then how the client
@@ -492,6 +493,33 @@ and the number of results to return, respectively.
492
493
493
494
Note that when `top` specifies a value larger than the server-driven paging page size, the response will be paged accordingly.
494
495
496
+
## Conditional Requests
497
+
498
+
When designing an API, you will almost certainly have to manage how your resource is updated. For example, if your resource is a bank account, you will want to ensure that one transaction--say depositing money--does not overwrite a previous transaction.
499
+
Similarly, it could be very expensive to send a resource to a client. This could be because of its size, network conditions, or a myriad of other reasons.
500
+
Both of these scenarios can be accomplished with conditional requests, where the client specifies a _precondition_
501
+
for execution of a request, based on its last modification date or entity tag ("ETag").
502
+
An ETag identifies a 'version' or 'instance' of a resource and is computed by the service and returned in an `ETag` response header for GET or other operations on the resource.
503
+
504
+
### Cache Control
505
+
506
+
One of the more common uses for conditional requests is cache control. This is especially useful when resources are large in size, expensive to compute/calculate, or hard to reach (significant network latency).
507
+
A client can make a "conditional GET request" for the resource, with a precondition header that requests that
508
+
data be returned only when the version on the service does not match the ETag or last modified date in the header.
509
+
If there are no changes, then there is no need to return the resource, as the client already has the most recent version.
510
+
511
+
Implementing this strategy is relatively straightforward. First, you will return an `ETag` with a value that uniquely identifies the instance (or version) of the resource. The [Computing ETags](./Guidelines.md#computing-etags) section provides guidance on how to properly calculate the value of your `ETag`.
512
+
In these scenarios, when a request is made by the client an `ETag` header is returned, with a value that uniquely identifies that specific instance (or version) of the resource. The `ETag` value can then be sent in subsequent requests as part of the `If-None-Match` header.
513
+
This tells the service to compare the `ETag` that came in with the request, with the latest value that it has calculated. If the two values are the same, then it is not necessary to return the resource to the client--it already has it. If they are different, then the service will return the latest version of the resource, along with the updated `ETag` value in the header.
514
+
515
+
### Optimistic Concurrency
516
+
517
+
Optimistic concurrency is a strategy used in HTTP to avoid the "lost update" problem that can occur when multiple clients attempt to update a resource simultaneously.
518
+
Clients can use ETags returned by the service to specify a _precondition_ for the execution of an update, to ensure that the resource has not been updated since the client last observed it.
519
+
For example, the client can specify an `If-Match` header with the last ETag value received by the client in an update request.
520
+
The service processes the update only if the ETag value in the header matches the ETag of the current resource on the server.
521
+
By computing and returning ETags for your resources, you enable clients to avoid using a strategy where the "last write always wins."
522
+
495
523
## Getting Help: The Azure REST API Stewardship Board
496
524
The Azure REST API Stewardship board is a collection of dedicated architects that are passionate about helping Azure service teams build interfaces that are intuitive, maintainable, consistent, and most importantly, delight our customers. Because APIs affect nearly all downstream decisions, you are encouraged to reach out to the Stewardship board early in the development process. These architects will work with you to apply these guidelines and identify any hidden pitfalls in your design.
Copy file name to clipboardExpand all lines: azure/Guidelines.md
+30-25Lines changed: 30 additions & 25 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -799,7 +799,7 @@ While removing a value from an enum is a breaking change, adding value to an enu
799
799
<ahref="#deprecation"name="deprecation"></a>
800
800
### Deprecating Behavior Notification
801
801
802
-
When the [API Versioning](#API-Versioning) guidance above cannot be followed and the [Azure Breaking Change Reviewers](mailto:[email protected]) approve a [breaking change](#123-definition-of-a-breaking-change) to a specific API version it must be communicated to its callers. The API version that is being deprecated must add the `azure-deprecating` response header with a semicolon-delimited string notifying the caller what is being deprecated, when it will no longer function, and a URL linking to more information such as what new operation they should use instead.
802
+
When the [API Versioning](#api-versioning) guidance above cannot be followed and the [Azure Breaking Change Reviewers](mailto:[email protected]) approve a breaking change to a specific API version it must be communicated to its callers. The API version that is being deprecated must add the `azure-deprecating` response header with a semicolon-delimited string notifying the caller what is being deprecated, when it will no longer function, and a URL linking to more information such as what new operation they should use instead.
803
803
804
804
The purpose is to inform customers (when debugging/logging responses) that they must take action to modify their call to the service's operation and use a newer API version or their call will soon stop working entirely. It is not expected that client code will examine/parse this header's value in any way; it is purely informational to a human being. The string is _not_ part of an API contract (except for the semi-colon delimiters) and may be changed/improved at any time without incurring a breaking change.
805
805
@@ -1020,28 +1020,40 @@ Depending on the requirements of the service, there can be any number of "input"
1020
1020
1021
1021
<ahref="#condreq"name="condreq"></a>
1022
1022
### Conditional Requests
1023
-
When designing an API, you will almost certainly have to manage how your resource is updated. For example, if your resource is a bank account, you will want to ensure that one transaction--say depositing money--does not overwrite a previous transaction.
1024
-
Similarly, it could be very expensive to send a resource to a client. This could be because of its size, network conditions, or a myriad of other reasons. To enable this level of control, services should leverage an `ETag` header, or "entity tag," which will identify the 'version' or 'instance' of the resource a particular client is working with.
1025
-
An `ETag` is always set by the service and will enable you to _conditionally_ control how your service responds to requests, enabling you to provide predictable updates and more efficient access.
1026
1023
1027
-
<ahref="#condreq-return-etags"name="condreq-return-etags">:ballot_box_with_check:</a> **YOU SHOULD** return an `ETag` with any operation returning the resource or part of a resource or any update of the resource (whether the resource is returned or not).
1024
+
The [HTTP Standard][] defines request headers that clients may use to specify a _precondition_
1025
+
for execution of an operation. These headers allow clients to implement efficient caching mechanisms
1026
+
and avoid data loss in the event of concurrent updates to a resource. The headers that specify conditional execution are `If-Match`, `If-None-Match`, `If-Modified-Since`, `If-Unmodified-Since`, and `If-Range`.
<ahref="#condreq-support"name="condreq-support">:white_check_mark:</a> **DO** honor any precondition headers received as part of a client request.
1037
+
1038
+
The HTTP Standard does not allow precondition headers to be ignored, as it can be unsafe to do so.
1039
+
1040
+
<ahref="#condreq-unsupported-error"name="condreq-unsupported-error">:white_check_mark:</a> **DO** return the appropriate precondition failed error response if the service cannot verify the truth of the precondition.
1028
1041
1029
-
<ahref="#condreq-support-etags-consistently"name="condreq-support-etags-consistently">:ballot_box_with_check:</a> **YOU SHOULD** use `ETag`s consistently across your API, i.e. if you use an `ETag`, accept it on all other operations.
1042
+
Note: The Azure Breaking Changes review board will allow a GA service that currently ignores precondition headers to begin honoring them in a new API version without a formal breaking change notification. The potential for disruption to customer applications is low and outweighed by the value of conforming to HTTP standards.
1030
1043
1031
-
You can learn more about conditional requests by reading [RFC7232](https://datatracker.ietf.org/doc/html/rfc7232).
1044
+
While conditional requests can be implemented using last modified dates, entity tags ("ETags") are strongly
1045
+
preferred since last modified dates cannot distinguish updates made less than a second apart.
1032
1046
1033
-
#### Cache Control
1034
-
One of the more common uses for `ETag` headers is cache control, also referred to a "conditional GET." This is especially useful when resources are large in size, expensive to compute/calculate, or hard to reach (significant network latency). That is, using the value of the `ETag` , the server can determine if the resource has changed. If there are no changes, then there is no need to return the resource, as the client already has the most recent version.
1047
+
<ahref="#condreq-return-etags"name="condreq-return-etags">:ballot_box_with_check:</a> **YOU SHOULD** return an `ETag` with any operation returning the resource or part of a resource or any update of the resource (whether the resource is returned or not).
1035
1048
1036
-
Implementing this strategy is relatively straightforward. First, you will return an `ETag` with a value that uniquely identifies the instance (or version) of the resource. The [Computing ETags](#computing-etags) section provides guidance on how to properly calculate the value of your `ETag`.
1037
-
In these scenarios, when a request is made by the client an `ETag` header is returned, with a value that uniquely identifies that specific instance (or version) of the resource. The `ETag` value can then be sent in subsequent requests as part of the `If-None-Match` header.
1038
-
This tells the service to compare the `ETag` that came in with the request, with the latest value that it has calculated. If the two values are the same, then it is not necessary to return the resource to the client--it already has it. If they are different, then the service will return the latest version of the resource, along with the updated `ETag` value in the header.
This section gives a summary of the processing to perform for precondition headers.
1052
+
See the [Conditional Requests section of the HTTP Standard][] for details on how and when to evaluate these headers.
1041
1053
1042
-
When supporting conditional read strategies:
1054
+
[Conditional Requests section of the HTTP Standard]: https://datatracker.ietf.org/doc/html/rfc9110#name-conditional-requests
1043
1055
1044
-
<ahref="#condreq-for-read-behavior"name="condreq-for-read-behavior">:white_check_mark:</a> **DO** adhere to the following table for guidance:
1056
+
<ahref="#condreq-for-read-behavior"name="condreq-for-read-behavior">:white_check_mark:</a> **DO** adhere to the following table for processing a GET request with precondition headers:
@@ -1050,15 +1062,7 @@ When supporting conditional read strategies:
1050
1062
1051
1063
For more control over caching, please refer to the `cache-control`[HTTP header](https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control).
1052
1064
1053
-
#### Optimistic Concurrency
1054
-
An `ETag` should also be used to reflect the create, update, and delete policies of your service. Specifically, you should avoid a "pessimistic" strategy where the 'last write always wins." These can be expensive to build and scale because avoiding the "lost update" problem often requires sophisticated concurrency controls.
1055
-
Instead, implement an "optimistic concurrency" strategy, where the incoming state of the resource is first compared against what currently resides in the service. Optimistic concurrency strategies are implemented through the combination of `ETags` and the [HTTP Request / Response Pattern](#http-request--response-pattern).
1056
-
1057
-
<ahref="#condreq-no-pessimistic-update"name="condreq-no-pessimistic-update">:warning:</a> **YOU SHOULD NOT** implement pessimistic update strategies, e.g. last writer wins.
1058
-
1059
-
When supporting optimistic concurrency:
1060
-
1061
-
<ahref="#condreq-behavior"name="condreq-behavior">:white_check_mark:</a> **DO** adhere to the following table for guidance:
1065
+
<ahref="#condreq-behavior"name="condreq-behavior">:white_check_mark:</a> **DO** adhere to the following table for processing a PUT, PATCH, or DELETE request with precondition headers:
@@ -1070,6 +1074,7 @@ When supporting optimistic concurrency:
1070
1074
| DELETE |`If-Match`| value of ETag | value does NOT match the latest value on the server |`412-Preconditioned Failed`| Response body SHOULD be empty.|
1071
1075
1072
1076
#### Computing ETags
1077
+
1073
1078
The strategy that you use to compute the `ETag` depends on its semantic. For example, it is natural, for resources that are inherently versioned, to use the version as the value of the `ETag`. Another common strategy for determining the value of an `ETag` is to use a hash of the resource. If a resource is not versioned, and unless computing a hash is prohibitively expensive, this is the preferred mechanism.
1074
1079
1075
1080
<ahref="#condreq-etag-is-hash"name="condreq-etag-is-hash">:ballot_box_with_check:</a> **YOU SHOULD** use a hash of the representation of a resource rather than a last modified/version number
@@ -1084,7 +1089,7 @@ While it may be tempting to use a revision/version number for the resource as th
1084
1089
1085
1090
<ahref="#condreq-weak-etags-allowed"name="condreq-weak-etags-allowed">:heavy_check_mark:</a> **YOU MAY** consider Weak ETags if you have a valid scenario for distinguishing between meaningful and cosmetic changes or if it is too expensive to compute a hash.
1086
1091
1087
-
<ahref="#condreq-etag-depends-on-encoding"name="condreq-etag-depends-on-encoding">:white_check_box:</a> **DO**, when supporting multiple representations (e.g. Content-Encodings) for the same resource, generate different ETag values for the different representations.
1092
+
<ahref="#condreq-etag-depends-on-encoding"name="condreq-etag-depends-on-encoding">:white_check_mark:</a> **DO**, when supporting multiple representations (e.g. Content-Encodings) for the same resource, generate different ETag values for the different representations.
0 commit comments