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!

Comments

  • Anonymous
    June 21, 2008
    Jeff just sent me a note indicating that he has a post on the upcoming ETag support in SSDS. To be clear

  • Anonymous
    June 22, 2008
    Good to see ETag support is coming for single entities. I'm looking forward to exercising this cool new feature. It is also important that ETag support be added for queries. When you think about it, ETag for queries is very valuable as these queries can consume much bandwidth and reduce performance when you send repeated data between client and server.

  • Anonymous
    June 23, 2008
    Mamund, Let me make sure I understand your request first on the query side. Are you suggesting that we issue a ETag that represents the query result or over the indiviual entities within the result?   We already do the latter (that value will be in the Version property element in the results).  The former would be pretty expensive to implement within the service. Did I understand your request or is there something I'm missing? --Jeff--

  • Anonymous
    June 24, 2008
    The comment has been removed

  • Anonymous
    June 29, 2008
    In my last post I described how ETag's can be used in the upcoming sprint 3 version of SSDS. In this