{"id":2123,"date":"2008-04-22T18:09:37","date_gmt":"2008-04-22T18:09:37","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/odatateam\/2008\/04\/22\/optimistic-concurrency-amp-data-services\/"},"modified":"2008-04-22T18:09:37","modified_gmt":"2008-04-22T18:09:37","slug":"optimistic-concurrency-amp-data-services","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/odata\/optimistic-concurrency-amp-data-services\/","title":{"rendered":"Optimistic Concurrency &amp; Data Services"},"content":{"rendered":"<p>Different applications have different requirements around consistency and how concurrent modifications are handled. I\u2019ll oversimplify and put all these applications in two buckets: either you care about controlling concurrent changes or you don\u2019t. <\/p>\n<p>If you\u2019re creating a REST interface to your data and don\u2019t care about concurrency (e.g. no deep consistency rules, or nice units of change that change in whole consistent ways), then you can use the basic HTTP methods to retrieve (GET) and manipulate (POST, PUT, DELETE) resources directly without any more context than the representations of your resources. You get \u201clast one wins\u201d semantics on updates in this case. <\/p>\n<p>On the other hand, if you do care about concurrency in your REST interface, there are more aspects to take into consideration. If your resources are atomic (no further structure that\u2019s interesting from the concurrent changes perspective than the resource as a whole), then you can have an out of band mechanism for creating a \u201cversion number\u201d for each resource -typically a monotonically increasing number- and use HTTP\u2019s existing mechanism to ensure you overwrite stuff that you know about. In HTTP you can stick an \u201centity tag\u201d or ETag to your responses that contain an opaque value used to denote the version or state of a resource. Later on, when you want to modify a resource, you can use that value in a \u201cif-match\u201d request header to make sure that your knowledge about the state of the resource you\u2019re modifying is still current. If it\u2019s not the resource in the sever would have an ETag that won\u2019t match the one you provided and you\u2019d get back a 412 \u201cPrecondition failed\u201d status code. All that is standard HTTP 1.1 stuff described in RFC 2616. (ETags are also used for caching and conditional gets in addition to the scenario I described here). <\/p>\n<p>Now, REST data services that expose structured data have to deal with various challenges beyond the basics, which I\u2019ll go into details below. While I discuss this in the context of the ADO.NET Data Services Framework (Astoria), I\u2019m sure some of these problems apply to a broader set of applications. <\/p>\n<p><b>Creating ETags: concurrency tokens<\/b> <\/p>\n<p>The data services framework has to deal with the fact that we don\u2019t control the data sources that we expose through the REST interface. Sometimes each entity that we turn into a resource will have a nice clean property that\u2019s a timestamp or similar and maps perfectly to ETag semantics (e.g. whenever we change the value in a significant way the value of this property changes). However, often the schema of the underlying data is not under the control of the service developer so we have to work with what we have. What that means in practice is that you can tell the data services framework which properties of each entity type are \u201cconcurrency tokens\u201d. Changing those values means that you chanced the version of the resource.  <\/p>\n<p>The way you do that in the framework is by using an [ETag(props\u2026)] attribute in your class or an annotation in your EDM schema. For types that don\u2019t have any concurrency tokens we won\u2019t generate ETags for the responses for those types, and they get \u201clast one wins\u201d update behavior. <\/p>\n<p>Once you indicated which property or properties are your concurrency tokens we can produce ETags by using the values of those properties for the particular instance we\u2019re returning. <\/p>\n<p>During update the data services runtime works with the data source to determine whether the concurrency token values that were marshaled through ETags and if-match headers still match, and if so perform the update\/delete operation. If they don\u2019t match a 412 response is sent to the client. <\/p>\n<p><b>Including ETags in headers and\/or payloads<\/b> <\/p>\n<p>The HTTP spec describes the ETag response header to transport the entity tag for a given resource. That works great for us for cases were we respond with a single entity (e.g. an entry in Atom terms), but it doesn\u2019t when we return a collection of entries from a URL (e.g. an Atom feed). For the latter scenario, we include the ETag as part of the resource representation (in the entry for Atom, in the \u201c__metadata\u201d property for JSON), for example: <\/p>\n<p>&lt;entry m:type=&#8221;BikesModel.Customer&#8221; m:etag=&#8221;&#8216;A%20Bike%20Store'&#8221;&gt; <\/p>\n<p>&nbsp; &lt;!&#8211; rest of the entry &#8211;&gt; <\/p>\n<p>&lt;\/entry&gt; <\/p>\n<p><b>Validation during side-effecting operations<\/b> <\/p>\n<p>Concurrency tokens are validated whenever you perform an operation that affects the state of an existing resource. In the data services REST interface that means HTTP PUT and DELETE methods. <\/p>\n<p>As I mentioned above, validation happens during update processing by extracting the \u201coriginal\u201d values from the ETag (which was sent back through the if-match header) and comparing them with the data in the data source. If they are the same, we consider the whole resource the same and proceed with the modification. <\/p>\n<p>An interesting question is whether presenting an ETag in a if-match header should be mandatory for resources that have concurrency tokens. Put another way: should the decision of whether it\u2019s ok to potentially overwrite changes based on state knowledge be up to the client or restricted by the server? The HTTP spec defines a special value of \u201c*\u201d for the if-match header that effectively means \u201cany value will match\u201d. The behavior that we are planning for is that if an entity type has concurrency tokens then we\u2019ll always require an \u201cif-match\u201d header in modification operations. The header value can be an actual ETag obtained through a GET request or \u201c*\u201d meaning \u201cI know this type supports concurrency control, but I\u2019ll overwrite it anyway\u201d.  <\/p>\n<p><b>Almost, but not quite, a perfect match<\/b> <\/p>\n<p>HTTP ETags and conditional operations are almost a perfect match to what we need to handle concurrent activity in RESTful data services. There are, however, a few glitches. This is where we get into the fine-print that\u2019s not necessarily popular knowledge. Mike brought up many of these details I wasn\u2019t aware of. <\/p>\n<p>ETags can be \u201cstrong entity tags\u201d or \u201cweak entity tags\u201d. Weak ETags are very similar to what happens when we have entities for which only some properties are designated concurrency tokens. From section 13.3.3 of the HTTP spec: <\/p>\n<p>\u201cHowever, there might be cases when a server prefers to change the <\/p>\n<p>&nbsp;&nbsp; validator only on semantically significant changes, and not when <\/p>\n<p>&nbsp;&nbsp; insignificant aspects of the entity change. A validator that does not <\/p>\n<p>&nbsp;&nbsp; always change when the resource changes is a &#8220;weak validator.&#8221; \u201c <\/p>\n<p>The problem is that weak ETags only apply to GET operations, they cannot be used for PUT\/DELETE which is what we\u2019re trying to do. <\/p>\n<p>For cases where you own the data, the data services framework can expose a compliant interface by using constructs such as timestamps (if using a database as a data source), where any change in the entity will reflect in the ETag. You can also used a relaxed form of ETags where the entity might change but the ETag stay the same. It\u2019s not completely HTTP compliant and may confuse intermediate systems, but it may be your only option in some scenarios. <\/p>\n<p>As always, thoughts and feedback is welcome. <\/p>\n<p>Pablo Castro<br>Software Architect<br>Microsoft Corporation <\/p>\n<p>This post is part of the transparent design exercise in the Astoria Team. To understand how it works and how your feedback will be used please look at <a href=\"https:\/\/blogs.msdn.com\/astoriateam\/archive\/2007\/07\/20\/transparency-in-the-design-process.aspx\">this post<\/a>. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Different applications have different requirements around consistency and how concurrent modifications are handled. I\u2019ll oversimplify and put all these applications in two buckets: either you care about controlling concurrent changes or you don\u2019t. If you\u2019re creating a REST interface to your data and don\u2019t care about concurrency (e.g. no deep consistency rules, or nice units [&hellip;]<\/p>\n","protected":false},"author":512,"featured_media":3253,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[27],"class_list":["post-2123","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-odata","tag-design-notes"],"acf":[],"blog_post_summary":"<p>Different applications have different requirements around consistency and how concurrent modifications are handled. I\u2019ll oversimplify and put all these applications in two buckets: either you care about controlling concurrent changes or you don\u2019t. If you\u2019re creating a REST interface to your data and don\u2019t care about concurrency (e.g. no deep consistency rules, or nice units [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/2123","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/users\/512"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/comments?post=2123"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/2123\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media\/3253"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media?parent=2123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/categories?post=2123"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/tags?post=2123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}