<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Jeff's thoughts on Software Architecture, Large Scale Services and the Technical world at large : ETag</title><link>http://blogs.msdn.com/jcurrier/archive/tags/ETag/default.aspx</link><description>Tags: ETag</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>What happened to SSDS PUT/POST responses?</title><link>http://blogs.msdn.com/jcurrier/archive/2008/07/25/what-happened-to-ssds-put-post-responses.aspx</link><pubDate>Fri, 25 Jul 2008 20:06:03 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8772512</guid><dc:creator>jcurrier</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/jcurrier/comments/8772512.aspx</comments><wfw:commentRss>http://blogs.msdn.com/jcurrier/commentrss.aspx?PostID=8772512</wfw:commentRss><wfw:comment>http://blogs.msdn.com/jcurrier/rsscomments.aspx?PostID=8772512</wfw:comment><description>&lt;p&gt;Since the rollout I've seen at least one or two messages regarding the altered responses we're now sending with the REST head of the service.&amp;#160; There are several reasons why we've changed the service to now not return the entity body back to you again.&amp;#160; These are as follows:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;&lt;strong&gt;Version&lt;/strong&gt; - Prior to this rollout the only mechanism that we had to communicate version information to you was via the entity body of the response (specifically the version element in the body).&amp;#160; However, now that we have true ETag support we now can simply return this value back to caller via the ETag header per the Http spec.&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt; - Although at this point we don't charge for the service we eventually will.&amp;#160; Included within this billing structure are charges for ingress &amp;amp; egress (bandwidth usage in essence).&amp;#160; Therefore, by not returning the body to the caller (which the caller originally provided us with anyway) this is a net cost savings for you the caller.&amp;#160; &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Hope this helps to explain a bit more of the thinking behind the change.&amp;#160; As always we're open to feedback.&lt;/p&gt;  &lt;p&gt;--Jeff--&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8772512" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/jcurrier/archive/tags/SSDS/default.aspx">SSDS</category><category domain="http://blogs.msdn.com/jcurrier/archive/tags/SQL+Server+Data+Services/default.aspx">SQL Server Data Services</category><category domain="http://blogs.msdn.com/jcurrier/archive/tags/ETag/default.aspx">ETag</category></item><item><title>Optimistic Concurrency with SOAP in SSDS</title><link>http://blogs.msdn.com/jcurrier/archive/2008/06/29/optimistic-concurrency-with-soap-in-ssds.aspx</link><pubDate>Sun, 29 Jun 2008 23:47:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8668458</guid><dc:creator>jcurrier</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/jcurrier/comments/8668458.aspx</comments><wfw:commentRss>http://blogs.msdn.com/jcurrier/commentrss.aspx?PostID=8668458</wfw:commentRss><wfw:comment>http://blogs.msdn.com/jcurrier/rsscomments.aspx?PostID=8668458</wfw:comment><description>&lt;P&gt;In my last &lt;A href="http://blogs.msdn.com/jcurrier/archive/2008/06/22/etag-s-optimistic-concurrency-and-ssds.aspx" mce_href="http://blogs.msdn.com/jcurrier/archive/2008/06/22/etag-s-optimistic-concurrency-and-ssds.aspx"&gt;post&lt;/A&gt; I described how ETag's can be used in the upcoming sprint 3 version of SSDS.&amp;nbsp; In this post I'd like to talk about how this same functionality is exposed through our SOAP service head.&lt;/P&gt;
&lt;P&gt;In many ways designing this particular feature for the REST head was much easier than for the SOAP service head.&amp;nbsp; This is because the Http specification describes, in a fair bit of detail, what the semantics are around things like ETag's.&amp;nbsp; The SOAP specification provides no such guidance and as a result we've rolled our own implementation here to support this feature in a way that makes sense for our service.&amp;nbsp; &lt;/P&gt;
&lt;H4&gt;&lt;STRONG&gt;VersionMatch&lt;/STRONG&gt;&lt;/H4&gt;
&lt;P&gt;As I mentioned earlier, since there is no ETag concept natively in SOAP we've introduced a new object called, "VersionMatch".&amp;nbsp; We've augmented the Scope struct in the system to now include this so that version is now also considered a qualifier for operations that take place via the SOAP service head.&lt;/P&gt;
&lt;P&gt;The VersionMatch object, as shown in the class diagram below, captures two things.&amp;nbsp; The first, is the version of the entity in question.&amp;nbsp; This could be the version of the entity we've just created, the version that I, as a caller, want to retrieve, or finally the one I want to update or delete.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;The second concept is something we refer to as the, "MatchType" or VersionMatchType.&amp;nbsp; This is used to determine how the service should compare the version when executing the requested operation.&amp;nbsp; This could mean that we only perform the operation if the version matches the version in the store (the VersionMatchType.Match case), or only perform the operation if it doesn't match (the VersionMatchType.NotMatch case) or finally to ignore the version completely when performing the operation.&amp;nbsp; NOTE: By default we use the Ignore semantic unless otherwise instructed.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Class Diagram of the modified Scope object along with the new VersionMatch object and VersionMatchType enum.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/blogfiles/jcurrier/WindowsLiveWriter/OptimisticConcurrencywithSOAPinSSDS_B357/image_4.png" mce_href="http://blogs.msdn.com/blogfiles/jcurrier/WindowsLiveWriter/OptimisticConcurrencywithSOAPinSSDS_B357/image_4.png"&gt;&lt;IMG style="BORDER-TOP-WIDTH: 0px; BORDER-LEFT-WIDTH: 0px; BORDER-BOTTOM-WIDTH: 0px; BORDER-RIGHT-WIDTH: 0px" height=311 alt=image src="http://blogs.msdn.com/blogfiles/jcurrier/WindowsLiveWriter/OptimisticConcurrencywithSOAPinSSDS_B357/image_thumb_1.png" width=538 border=0 mce_src="http://blogs.msdn.com/blogfiles/jcurrier/WindowsLiveWriter/OptimisticConcurrencywithSOAPinSSDS_B357/image_thumb_1.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Now, that we've covered a bit of the changes to how we address things in the system let's see how we can apply these new features to some common scenarios.&lt;/P&gt;
&lt;H4&gt;&lt;STRONG&gt;Conditional Get&lt;/STRONG&gt;&lt;/H4&gt;
&lt;P&gt;Conditional retrieval is an interesting case if a mid-tier or client application is maintaining a cache.&amp;nbsp; This shows up many times in the web browser case where large files are cached until they've been either refreshed or updated on the service side.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;We can now perform a similar operation now using the SOAP service head.&amp;nbsp; I've created a sample below which illustrates how a caller can now conditionally retrieve an entity only if the version has changed.&lt;/P&gt;&lt;PRE&gt;            &lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt; (SitkaSoapServiceClient client = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; SitkaSoapServiceClient("&lt;SPAN style="COLOR: #8b0000"&gt;SitkaSoapEndpoint&lt;/SPAN&gt;"))
            {
                &lt;SPAN style="COLOR: #0000ff"&gt;try&lt;/SPAN&gt;
                {
                    &lt;SPAN style="COLOR: #008000"&gt;// Create a reference to some entity that we know exists and one that&lt;/SPAN&gt;
                    &lt;SPAN style="COLOR: #008000"&gt;// we have a reference to in our cache (the version we have is 2000).&lt;/SPAN&gt;
                    Scope entityScope = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; Scope();
                    entityScope.AuthorityId = "&lt;SPAN style="COLOR: #8b0000"&gt;some_authority&lt;/SPAN&gt;";
                    entityScope.ContainerId = "&lt;SPAN style="COLOR: #8b0000"&gt;some_container&lt;/SPAN&gt;";
                    entityScope.EntityId = "&lt;SPAN style="COLOR: #8b0000"&gt;123&lt;/SPAN&gt;";
                    entityScope.VersionMatch = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; VersionMatch();
                    entityScope.VersionMatch.Version = 2000;
                    entityScope.VersionMatch.MatchType = VersionMatchType.NotMatch;

                    &lt;SPAN style="COLOR: #008000"&gt;// Then, retrieve the entity *only* if the version doesn't match the&lt;/SPAN&gt;
                    &lt;SPAN style="COLOR: #008000"&gt;// one that I already know about.&lt;/SPAN&gt;
                    Entity theEntity = client.Get(entityScope);

                }
                &lt;SPAN style="COLOR: #0000ff"&gt;catch&lt;/SPAN&gt; (FaultException&amp;lt;Error&amp;gt; ex)
                {
                    &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt; (ex.Detail.StatusCode == ErrorCodes.EntityNotModified)
                    {
                        &lt;SPAN style="COLOR: #008000"&gt;// there is no later version.&lt;/SPAN&gt;
                    }
                }
            }&lt;/PRE&gt;
&lt;H4&gt;&lt;STRONG&gt;Conditional Update and Delete&lt;/STRONG&gt;&lt;/H4&gt;
&lt;P&gt;The next two cases are really very similar to one another.&amp;nbsp; In both of these cases we really only want to update (or delete) the entity if we know that there has been no other modifications to the entity in question.&amp;nbsp; This way we can be certain that we've not overwritten someone else's changes (in the update case) or perhaps deleted an entity which now (since the update) may not have needed to be deleted.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;The code listing below illustrates both of these cases.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Conditional Update in SOAP&lt;/STRONG&gt;&lt;/P&gt;&lt;PRE&gt;            &lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt;(SitkaSoapServiceClient client = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; SitkaSoapServiceClient("&lt;SPAN style="COLOR: #8b0000"&gt;SitkaSoapEndpoint&lt;/SPAN&gt;"))
            {
                &lt;SPAN style="COLOR: #0000ff"&gt;try&lt;/SPAN&gt;
                {
                    &lt;SPAN style="COLOR: #008000"&gt;// Create a reference to some entity that we know exists.&lt;/SPAN&gt;
                    Scope entityScope = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; Scope();
                    entityScope.AuthorityId = "&lt;SPAN style="COLOR: #8b0000"&gt;some_authority&lt;/SPAN&gt;";
                    entityScope.ContainerId = "&lt;SPAN style="COLOR: #8b0000"&gt;some_container&lt;/SPAN&gt;";
                    entityScope.EntityId = "&lt;SPAN style="COLOR: #8b0000"&gt;123&lt;/SPAN&gt;";

                    &lt;SPAN style="COLOR: #008000"&gt;// Then, retrieve the entity (any version will do).&lt;/SPAN&gt;
                    Entity theEntity = client.Get(entityScope);

                    &lt;SPAN style="COLOR: #008000"&gt;// Update some properties on the Entity.&lt;/SPAN&gt;
                    theEntity.Properties["&lt;SPAN style="COLOR: #8b0000"&gt;FavoriteStorageService&lt;/SPAN&gt;"] = "&lt;SPAN style="COLOR: #8b0000"&gt;SSDS&lt;/SPAN&gt;";

                    &lt;SPAN style="COLOR: #008000"&gt;// Next, update the version match properties on the scope we'll be using to update&lt;/SPAN&gt;
                    &lt;SPAN style="COLOR: #008000"&gt;// the entity with.&lt;/SPAN&gt;
                    entityScope.VersionMatch = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; VersionMatch();
                    entityScope.VersionMatch.Version = theEntity.Version;
                    entityScope.VersionMatch.MatchType = VersionMatchType.Match;

                    &lt;SPAN style="COLOR: #008000"&gt;// Finally, update the entity if the version check clears.&lt;/SPAN&gt;
                    client.Update(entityScope, theEntity);

                }&lt;SPAN style="COLOR: #0000ff"&gt;catch&lt;/SPAN&gt;(FaultException&amp;lt;Error&amp;gt; ex)
                {
                    &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt;(ex.Detail.StatusCode == ErrorCodes.PreconditionFailed)
                    {
                        &lt;SPAN style="COLOR: #008000"&gt;// Our version check failed so we don't have the latest and greatest.&lt;/SPAN&gt;
                    }
                }
            }&lt;/PRE&gt;
&lt;P&gt;&lt;STRONG&gt;Conditional Delete in SOAP&lt;/STRONG&gt;&lt;/P&gt;&lt;PRE&gt;            &lt;SPAN style="COLOR: #0000ff"&gt;using&lt;/SPAN&gt;(SitkaSoapServiceClient client = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; SitkaSoapServiceClient("&lt;SPAN style="COLOR: #8b0000"&gt;SitkaSoapEndpoint&lt;/SPAN&gt;"))
            {
                &lt;SPAN style="COLOR: #0000ff"&gt;try&lt;/SPAN&gt;
                {
                    &lt;SPAN style="COLOR: #008000"&gt;// Create a reference to some entity that we know exists.&lt;/SPAN&gt;
                    Scope entityScope = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; Scope();
                    entityScope.AuthorityId = "&lt;SPAN style="COLOR: #8b0000"&gt;some_authority&lt;/SPAN&gt;";
                    entityScope.ContainerId = "&lt;SPAN style="COLOR: #8b0000"&gt;some_container&lt;/SPAN&gt;";
                    entityScope.EntityId = "&lt;SPAN style="COLOR: #8b0000"&gt;123&lt;/SPAN&gt;";

                    &lt;SPAN style="COLOR: #008000"&gt;// Next, set the version match properties on the scope we'll be using to remove&lt;/SPAN&gt;
                    &lt;SPAN style="COLOR: #008000"&gt;// the entity with.&lt;/SPAN&gt;
                    entityScope.VersionMatch = &lt;SPAN style="COLOR: #0000ff"&gt;new&lt;/SPAN&gt; VersionMatch();
                    entityScope.VersionMatch.Version = 2000; &lt;SPAN style="COLOR: #008000"&gt;// the version of the entity we know about.&lt;/SPAN&gt;
                    entityScope.VersionMatch.MatchType = VersionMatchType.Match;

                    &lt;SPAN style="COLOR: #008000"&gt;// Finally, remove the entity if the version check clears.&lt;/SPAN&gt;
                    client.Delete(entityScope);

                }&lt;SPAN style="COLOR: #0000ff"&gt;catch&lt;/SPAN&gt;(FaultException&amp;lt;Error&amp;gt; ex)
                {
                    &lt;SPAN style="COLOR: #0000ff"&gt;if&lt;/SPAN&gt;(ex.Detail.StatusCode == ErrorCodes.PreconditionFailed)
                    {
                        &lt;SPAN style="COLOR: #008000"&gt;// Our version check failed so we don't have the latest and greatest.&lt;/SPAN&gt;
                    }
                }
            }&lt;/PRE&gt;&lt;PRE&gt;&amp;nbsp;&lt;/PRE&gt;
&lt;P&gt;That covers the optimistic concurrency examples for the Sprint 3 bits of SSDS.&amp;nbsp; I'm certain that there are other scenarios here that I'm not covering and I'd really like to hear other scenarios if you have them to share.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;Next time, I'll cover perhaps the biggest feature of the sprint (and one of our most requested features of the service that we have now completed for sprint 3....).&amp;nbsp; Look for more shortly :-)&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8668458" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/jcurrier/archive/tags/SSDS/default.aspx">SSDS</category><category domain="http://blogs.msdn.com/jcurrier/archive/tags/SQL+Server+Data+Services/default.aspx">SQL Server Data Services</category><category domain="http://blogs.msdn.com/jcurrier/archive/tags/ETag/default.aspx">ETag</category></item></channel></rss>