ConcurrencyException in debugger
I am seeing an exception when I run the website under the debugger. I'm using Raven.Embedded
https://groups.google.com/forum/?fromgroups#!topic/ravendb/YYNQ02e96fI
The short answer
The ResetItemTimeout method in the RavenSessionStateStoreProvider will sometimes attempt to update stale documents, causing RavenDB to throw a ConcurrencyException. These exceptions are caught and swallowed.
You are seeing the ConcurrencyException only because you are debugging the application. I suspect that if you set Tools->Options->Debugging->General->'Enable Just my Code' in Visual Studio, you will no longer see the exception.
The longer answer
Generally the System.Web.SessionState.SessionStateModule manages concurrency using a 'lock' field on the SessionState item (i.e. a property on the document store in RavenDB). For example, it will not attempt to update an item until it has obtained the lock for that record. However, it doesn't obtain the lock before calling ResetItemTimeout() on the store-provider (in this case the RavenSessionStateStoreProvider). What this means is that in the ResetItemTimeout() method, when the following occurs:
- The session-state-item document is retrieved from the RavenDB document-store,
- The item's expiry is updated.
- The item document is saved back to the RavenDB document-store.
it is possible for another ASP.NET request to have arrived, and caused the document to be updated between steps 1 and 3. Hence the document is stale.
In a relational database, where individual columns can be updated, this is not an issue, as the 'expiry' column can still be updated without touching anything else in the record. But in RavenDB documents are atomic, so attempting to update the expiry field using a stale version of the document will result in overwriting the previous update. This means data will be lost, and is obviously not an acceptable outcome.
One solution is to set WaitForNonStaleResultsAsOfLastWrite on the query, and perhaps this is the better choice. But my rationale for not taking that approach was as follows:
All the ResetItemTimeout() method is trying to accomplish is to set the session-data expiry to NOW + SessionTimeout. If we are getting a concurrency-exception, it is because another thread has got in and performed an update on the item in RavenDB, which will have updated the expiry anyway. It seemed to me that setting WaitForNonStaleResultsAsOfLastWrite would just mean we wait longer for the same result.
Any opinion on this is welcome.
I wish I had some time to look at the problem is detail. A few observation:
- I already have Enable Just My Code checked. I think this is the default - apart from having Reshaper installed it's a pretty standard, new VS2010 installation.
- This isn't an unpredictable race-condition - it happens every time I start the site under the debugger.
- From your explanation, it looks like it would be a shame to block with WaitForNonStaleResults - I assume that would damage performance to some degree?
- Assuming it's benign, it would definitely be worth noting this issue in the readme.markdown.
Thanks again - really appreciated. Pete.