9.3 Update Concurrency Handling
SData uses ETags to detect concurrent modifications. When this mechanism is active, the provider MUST include an ETag value in every resource entry it returns.
ETag in GET responses
If the resource is obtained from a single read, the ETag value is included twice in the response. Once as an ETag HTTP header and once in an <http:etag> element inside the entry. See note at the end of the Read Example section. These two ETag values MUST be identical. Here is a typical read response:
200 OK Content-Type: application/atom+xml; type=entry **ETag: gJaGtgHyuAwW6jMI4i0njA ** <entry xmlns:sdata="http://schemas.sage.com/sdata/2008/1" xmlns:http="http://schemas.sage.com/sdata/http/2008/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2005/Atom"> <id>http://www.example.com/sdata/myApp/myContract/-/salesOrders('43660')</id> <title>Sales Order 43660</title> <!-- entry elements skipped for clarity ... --> ** <http:etag>gJaGtgHyuAwW6jMI4i0njA==</http:etag> **</entry>
If the resource is obtained from a query, the provider MUST include an <http:etag> in every entry. Here is a typical query response:
200 OK Content-Type: application/atom+xml; type=feed <feed xmlns:sdata="http://schemas.sage.com/sdata/2008/1" xmlns:http="http://schemas.sage.com/sdata/http/2008/1" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:sle="http://www.microsoft.com/schemas/rss/core/2005" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2005/Atom"> <title>Sage App | Sales Orders</title> <id>http://www.example.com/sdata/myApp/myContract/-/salesOrders</id> <!-- feed elements skipped for clarity ... --> <entry> <id>http://www.example.com/sdata/myApp/myContract/-/salesOrders('43660')</id> <title>Sales Order 43660</title> <!-- entry elements skipped for clarity ... --> ** <http:etag>gJaGtgHyuAwW6jMI4i0njA==</http:etag> ** </entry> <entry> <!-- entry elements skipped for clarity --> <id>http://www.example.com/sdata/myApp/myContract/-/salesOrders('43661')</id> <title>Sales Order 43661</title> <!-- entry elements skipped for clarity ... --> ** <http:etag>3nqPeQqoGoxQB5xf3NIijw==</http:etag> ** </entry> <!-- more entries ... --> </feed>
In this case the provider MAY include an ETag HTTP header in the query response. This ETag header value can be used for caching but not for concurrency handling as it applies to the whole feed rather than individual resources.
PUT request
If the consumer updates resources, it MUST keep track of the ETag value(s) that it received from the provider. It MUST pass this value through an If-Match header when submitting PUT requests to update resources. Here is a typical example:
PUT /sdata/myApp/myContract/-/salesOrders('43661') Content-Type: application/atom+xml; type=entry **If-Match: 2nXz9DZYR2pqmcXi/ZCbYA==** <entry xmlns:sdata="http://schemas.sage.com/sdata/2008/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2005/Atom"> <id>http://www.example.com/sdata/myApp/myContract/-/salesOrders('43661')</id> <title>Sales Order 43661</title> <updated>2008-03-31T13:46:45Z</updated> <sdata:payload> <salesOrder xmlns="http://schemas.sage.com/myContract"> <shipDate>2008-04-05</shipDate> </salesOrder> </sdata:payload> </entry>
PUT responses
When processing a PUT request, the provider MUST compare the value of the HTTP If-Match header with its current ETag value for the resource.
If the consumer doesn't include the If-Match header in its PUT request, the provider MUST respond with a 400 Bad Request response.
If the ETag values are identical, the provider knows that the resource has not been modified since the consumer read it. So it should accept the request and respond with a 200 OK status code, unless the submitted payload contains invalid data. The response includes the updated payload and the new ETag value for the modified resource:
**200 OK **Content-Type: application/atom+xml; type=entry **ETag: STFtZgHEkPz7TyH98YEmWA== ** <entry xmlns:sdata="http://schemas.sage.com/sdata/2008/1" xmlns:http="http://schemas.sage.com/sdata/http/2008/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2005/Atom"> <id>http://www.example.com/sdata/myApp/myContract/-/salesOrders('43661')</id> <title>Sales Order 43661</title> <!-- entry elements skipped for clarity ... --> ** <http:etag>STFtZgHEkPz7TyH98YEmWA==</http:etag> **</entry>
On the other hand, if the ETag values differ, the provider knows that the resource has been modified since the consumer read it. In this case it MUST respond with a 412 Precondition Failed status code, and it MUST include the current version of the entry in its response (with the current ETag value):
**412 Precondition Failed **Content-Type: application/atom+xml; type=entry **ETag: 3nqPeQqoGoxQB5xf3NIijw== ** <entry xmlns:sdata="http://schemas.sage.com/sdata/2008/1" xmlns:http="http://schemas.sage.com/sdata/http/2008/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.w3.org/2005/Atom"> <id>http://www.example.com/sdata/myApp/myContract/-/salesOrders('43661')</id> <title>Sales Order 43661</title> <!-- entry elements skipped for clarity ... --> ** <http:etag>3nqPeQqoGoxQB5xf3NIijw==</http:etag> **</entry>
If the consumer submitted the update from an edit form, it can refill the form with the new values that it received and resubmit later with the new ETag that it received. A smart consumer can memorize the payload it got from the initial GET request and attempt a three-way merge. The memorized data is the common ancestor, the current consumer data and the data received from the provider are the two siblings that need merging.
There is a difference between request and response in the way we handle the ETag value. In responses that contain a single entry, we need the provider to include the value twice (ETag header and <http:etag> element). In update requests, we only need the ETag value once (If-Match header). This is because SData tries to keep processing as lean as possible on the consumer side, even if this means extra work on the provider side. By including the information twice in responses, we allow the consumer to extract the information from the <http:etag> element regardless of whether it got the entry from a query or a read operation. But we don't need the consumer to send the information twice. It may send it twice, though, as SData also defines an <http:ifMatch> element but this element is designed for batch requests and is not required in regular update requests.
Metadata property
In the schema, the sme:supportsETag property indicates whether the ETag concurrency handling mechanism is supported or not on a given resource kind. See Resource Kind Definition section for details. For example:
<xs:element name="salesOrder" type="tns:salesOrderType" sme:role="resourceKind" sme:pluralName="salesOrders" sme:label="Sales Order" sme:canGet="true" sme:canPost="true" sme:canPut="true" sme:canDelete="true" **sme:supportsETag="true"** sme:batchingMode="syncOrAsync" />
If the sme:supportsETag property is set to false, the ETag mechanism is inactive and concurrent updates are handled with the “last one who writes wins” default rule.
SData providers MAY support this concurrency handling protocol. They MUST support it on all resource kinds that have the sme:supportsETag flag set to true.