MJRichardson / RavenDbSessionStateStoreProvider

An ASP.NET session-state store-provider implementation using RavenDB for persistence.
Other
18 stars 6 forks source link

ConcurrencyException in debugger #3

Open completer opened 12 years ago

completer commented 12 years ago

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

MJRichardson commented 12 years ago
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:

  1. The session-state-item document is retrieved from the RavenDB document-store,
  2. The item's expiry is updated.
  3. 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.

completer commented 12 years ago

I wish I had some time to look at the problem is detail. A few observation:

Thanks again - really appreciated. Pete.