I just discovered that every request in an ASP.Net web application gets a Session lock at the beginning of a request, and then releases it at the end of the request!
In case the implications of this are lost on you, as it was for me at first, this basically means the following:
Anytime an ASP.Net webpage is taking a long time to load (maybe due to a slow database call or whatever), and the user decides they want to navigate to a different page because they are tired of waiting, THEY CAN'T! The ASP.Net session lock forces the new page request to wait until the original request has finished its painfully slow load. Arrrgh.
Anytime an UpdatePanel is loading slowly, and the user decides to navigate to a different page before the UpdatePanel has finished updating... THEY CAN'T! The ASP.net session lock forces the new page request to wait until the original request has finished its painfully slow load. Double Arrrgh!
So what are the options? So far I have come up with:
- Implement a Custom SessionStateDataStore, which ASP.Net supports. I haven't found too many out there to copy, and it seems kind of high risk and easy to mess up.
- Keep track of all requests in progress, and if a request comes in from the same user, cancel the original request. Seems kind of extreme, but it would work (I think).
- Don't use Session! When I need some kind of state for the user, I could just use Cache instead, and key items on the authenticated username, or some such thing. Again seems kind of extreme.
I really can't believe that the ASP.Net Microsoft team would have left such a huge performance bottleneck in the framework at version 4.0! Am I missing something obvious? How hard would it be to use a ThreadSafe collection for the Session?
For ASPNET MVC, we did the following:
SessionStateBehavior.ReadOnly
on all controller's action by overridingDefaultControllerFactory
SessionStateBehaviour.Required
Create custom ControllerFactory and override
GetControllerSessionBehaviour
.AcquireSessionLockAttribute
Hook up the created controller factory in
global.asax.cs
Now, we can have both
read-only
andread-write
session state in a singleController
.Just to help anyone with this problem (locking requests when executing another one from the same session)...
Today I started to solve this issue and, after some hours of research, I solved it by removing the
Session_Start
method (even if empty) from the Global.asax file.This works in all projects I've tested.
OK, so big Props to Joel Muller for all his input. My ultimate solution was to use the Custom SessionStateModule detailed at the end of this MSDN article:
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstateutility.aspx
This was:
This has made a HUGE difference to the feeling of "snapiness" to our application. I still can't believe the custom implementation of ASP.Net Session locks the session for the whole request. This adds such a huge amount of sluggishness to websites. Judging from the amount of online research I had to do (and conversations with several really experienced ASP.Net developers), a lot of people have experienced this issue, but very few people have ever got to the bottom of the cause. Maybe I will write a letter to Scott Gu...
I hope this helps a few people out there!
Marking a controller's session state as readonly or disabled will solve the problem.
You can decorate a controller with the following attribute to mark it read-only:
the System.Web.SessionState.SessionStateBehavior enum has the following values:
I started using the AngiesList.Redis.RedisSessionStateModule, which aside from using the (very fast) Redis server for storage (I'm using the windows port -- though there is also an MSOpenTech port), it does absolutely no locking on the session.
In my opinion, if your application is structured in a reasonable way, this is not a problem. If you actually need locked, consistent data as part of the session, you should specifically implement a lock/concurrency check on your own.
MS deciding that every ASP.NET session should be locked by default just to handle poor application design is a bad decision, in my opinion. Especially because it seems like most developers didn't/don't even realize sessions were locked, let alone that apps apparently need to be structured so you can do read-only session state as much as possible (opt-out, where possible).
Unless your application has specially needs, I think you have 2 approaches:
Session is not only thread-safe but also state-safe, in a way that you know that until the current request is completed, every session variable wont change from another active request. In order for this to happen you must ensure that session WILL BE LOCKED until the current request have completed.
You can create a session like behavior by many ways, but if it does not lock the current session, it wont be 'session'.
For the specific problems you mentioned I think you should check HttpContext.Current.Response.IsClientConnected. This can be useful to to prevent unnecessary executions and waits on the client, although it cannot solve this problem entirely, as this can be used only by a pooling way and not async.