As you probably know by now, we are using Couchbase for caching data on our web site. However Couchbase contains support for not just cache data, but also for persistent data that is stored on disk using a NoSQL architecture. Once we were actively using Couchbase for our caching tier, the next step was to use it for storing data. The first thing we decided to move to Couchbase, was our session data, which up until that point had been stored in MySQL.
Storing session data in Couchbase was pretty easy to get started with using the Couchbase ASP.NET library which contains a port of the Enyim project Memcached provider to Couchbase. I started with this library and created my own fork on GitHub, so I could add the features we needed for our version (and also to fix any bugs we ran across). You can find my fork of the ASP.NET provider library using the URL below:
The first thing we noticed when we starting using the new session state provider is that the web site would lock up for about 30 seconds when rendering certain web pages. It turns out that there were some nasty bugs in the Enyim code that would crop up if you had a lot of parallel requests to the same session state store item. This is not something most web sites would have to deal with, but in our case we have some pages that generate a flurry of AJAX requests to the server when they load that would cause the session state provider to leave a session stuck in the locked state. When this happens IIS will spin attempting to get a write lock on the session state item, until a timeout occurs (usually 30 seconds) at which time it will forcibly clear the lock and continue on.
Examining the session state code in detail and comparing it line by line to our existing MySQL session state provider (which did not have this problem), we discovered the primary cause of the issue was that the code to release the locks on a session state item was not using the Couchbase CAS operation. Hence you could end up with a situation where one thread had a lock on the session and would clear it, but it’s cleared value would get overwritten by another thread attempting to lock it and it would never get cleared. The fix for this is included in the session state provider library in the above link.
Disabling Session Exclusive Access for Performance
One annoying feature that IIS has is that it considers session state to be exclusive and will block concurrent access to the session state unless you mark the session state as read only. You can read more about it on the MSDN documentation pages for the ASP.NET session state, but the important part is as follows:
Concurrent Requests and Session State
Access to ASP.NET session state is exclusive per session, which means that if two different users make concurrent requests, access to each separate session is granted concurrently. However, if two concurrent requests are made for the same session (by using the same SessionID value), the first request gets exclusive access to the session information. The second request executes only after the first request is finished. (The second session can also get access if the exclusive lock on the information is freed because the first request exceeds the lock time-out.) If the EnableSessionState value in the @ Page directive is set to ReadOnly, a request for the read-only session information does not result in an exclusive lock on the session data. However, read-only requests for session data might still have to wait for a lock set by a read-write request for session data to clear.
Although this seems like a reasonable requirement, and in fact ensures that two threads may not stomp on the same session data, it can in fact lead to performance issues when you have pages that make a lot of concurrent AJAX requests to the server. The correct solution is to decorate your AJAX methods in ASP.NET MVC with the Read Only session state behavior using the ASP.NET MVC SessionState attribute. That way no session data is going to be written out when those AJAX methods execute so IIS will allow them to run in parallel.
However in our case we are compiling and running a ton of legacy PHP code using Phalanger, which compiles the PHP code into .NET code that executes alongside our ASP.NET MVC code. Since this kind of session locking is not implemented in PHP at all, and it is difficult (if not impossible) to change the legacy code to enable read only sessions when needed, we started looking for a way to disable this feature in our custom session handler so we could get the Phalanger compiled PHP code to perform the same as it did under real PHP.
It turns out that supporting this is quite easy by adding a new flag to the Web.config provider entry and simply avoiding any locking in the session handler if is not desired. If you are interested in the implementation, look at the code and how it uses the exclusiveAccess flag internally in the handler. If you just want to use it, you can set the value using the “exclusiveAccess” attribute of the provider entry:
<sessionState customProvider="Couchbase" mode="Custom">
<add name="Couchbase" type="Couchbase.AspNet.SessionState.CouchbaseSessionStateProvider, Couchbase.AspNet" exclusiveAccess="false" />
Although I would highly recommend you use the correct solution of marking code you need to run in parallel with the ReadOnly session state attributes, sometimes it is easier when working with legacy code to simply turn off this feature