ETag's, Optimistic Concurrency and SSDS

I've been kind of quiet the last month or so but that's because I've been totally heads down implementing some major features for this sprint in SSDS.  Our next release to production, which should be in the next few weeks,  is coming up fast so I thought over the next few posts to the blog I would cover some of the highlights of the release.

We've covered a lot of ground in this sprint.  We've added some features, which I believe, that people will be pretty excited about.  I've decided to start the ball rolling with one of the features that was requested early on of the service... ETag support. 

ETag's in SSDS

It may be useful to quickly review the role that ETag's play in a RESTful service.  The ETag header value is typically used by a client application to determine if the content of a give resource has changed over some period of time.  In SSDS we use the value of the, "Version" property as the ETag header value for a given entity.  As documented previously, the version value will be changed when a update takes place to a entity. 

At this point, you may be asking yourself when do I get one of these things back to me?  Beginning with the next rollout ETag's will be returned to the caller on all POST, PUT, and single entity GET operations.  Now, with query operations we can't return back a ETag value simply because the response doesn't actually refer to any one particular entity in the service.  Instead, if you would like to determine what would be the ETag value for any one entity in the EntitySet simply extract the Version property value from the entity of interest.

Now that we know when ETag's come back to us we likely should review how we would get the ETag value from the response.  ETag's are a common Http header and are well supported as shown in the sample below which retrieves an ETag from a HttpWebResponse.

 HttpWebRequest request = CreateRequest();
using(HttpWebResponse response = request.GetResponse())
{
    string etagValue = response.Headers[HttpResponseHeader.ETag];
    // do something interesting with the ETag.
}

Conditional Operations

Now that we have ETag's we can use these to execute operations in a conditional manner.  In order to support these sort of operations we've added support for two other headers to the service the, "If-Match" and, "If-None-Match" headers.  We support these headers over all operations except for query though the following scenarios will likely fallout to be the most common:

  • Conditional GET - This is actually the most common case, browsers typically operate in this manner.  In short, they first retrieve a entity which retrieves a entity with a ETag.  Then, on any subsequent calls to the same resouce a, "If-None-Match" header is provided on the request with the ETag from the initial GET operation.  If, and only if, the resource has changed then the complete entity definition is returned to the caller. 

If the entity hasn't been updated then a 304 (NotModified) is returned and only the headers are returned to the caller. 

  • Conditional PUT - In this case, we only want to update the contents of an entity if we know that we have the latest and greatest.  In this case we would provide a, "If-Match" header value with the ETag value of the entity that we wish to update. 

In this case, if a later version of the entity is found in the service then we will return a 412 (PreConditionFailed) to the caller and the caller will have to retrieve the entity again and decide if they still need to update the entity again.

  • Conditional DELETE - This is case pretty much the exact same as the Conditional PUT case.  The only difference here being that the verb we've chosen to use is the DELETE verb rather then PUT.  I wanted to explicitly call out though just to illustrate our support for it.

HEAD support

There are some cases where the caller wishes to discover if the content they have is the latest greatest.  Typically, you would like this operation to be light weight in nature (which would exclude the returning of the entire entity) and only give you back the ETag or other relevant headers.  The Http specification provides a verb for this called, "HEAD" and with this sprint we've added support for it to the service. 

You use the HEAD verb just like you would the normal GET operation except that we will only return back to you the headers that are of interest.  This verb, will perhaps not so obvious in usefulness here, will become more obvious in my one of my next posts.

What about SOAP?

While I haven't covered SOAP in this post we have added support for these operations to the SOAP service.  I'll be covering the specifics of how this works in my next post.  Keep an eye out for this shortly!