ASP.NET Concurrent Ajax Requests and Session State Blocking

Intro

Today I ran into a pretty interesting and puzzling issue while trying to load many different long running content areas on a screen asynchronously via Ajax & jQuery.

It appeared that if I shot off Ajax Request A, B, C, D… that they would always return one after the other (roughly). They never deviated from this pattern and were not appearing to be operating asynchronously from an ASP.NET perspective. To try to figure out what is going on here consider the following C# and JS code:

Example Code

 

public class TestController : Controller
    {
        public ActionResult Index()
        {
            System.Threading.Thread.Sleep(10000);
            return new EmptyResult();
        }
    }

 

$(function() {
    for (var i = 0; i < 10; i++) {
        $.get('@Url.Action("Index", "Test")')
    }
})

 

 

Check out what (unexpectedly) happens in Firebug’s Net tab:

 before

Firebug Behavior… What?

It appears that my Thread.Sleep of 10 seconds is causing each request to return one after the other (apparently ASP.NET is queuing them differently than Firebug did here in this example), but only after the previous request in the queue has completed. The 10 requests actually completed in almost exactly 100 seconds, as expected given this behavior.

Upon seeing this issue I was initially very confused and began to question my core  ASP.NET understanding such as: is ASP.NET blocking other requests simply because of this Sleep()? Shouldn’t ASP.NET be spinning up other threads to deal with the concurrent request load up to the configured limit?

 

An Answer, Straight from Microsoft

Luckily it turned out to be something I have never encountered (but I bet people will be seeing more of as the web goes Ajax) Session State blocking! Take a look at the Microsoft Session State article bottom of the page called Concurrent Requests and Session State, let me sum it up:

  • Only one request at a time may have Read/Write access to a particular session (as determined by session id cookie) at a time. R/W access is default
  • Any additional requests requiring ANY session access will be blocked until the previous R/W request has completed

 

Why?

Well then… it looks like when ASP.NET was written (over 10 years ago now right?) concurrent ASP.NET processed requests (as opposed to static files) almost never happened as Gmail didn’t make its big Ajax foray until 2005. I’m guessing this behavior exists because because you can get into some interesting situations when it comes to merging written session data keys across multiple possible concurrent requests. I think this might be worth revisiting now in 2011 and allowing for something like “last wins for the same session key”. There are other situations that could occur but I’d rather read some documentation about the expected behavior (with do’s and don’ts) rather than have this occur. Luckily there is a less elegant but workable solution for MVC for right now:

 

A Solution for MVC 3

Luckily Microsoft has provided ENUM values in System.Web.SessionState.SessionStateBehavior that allow us to give up rights to an exclusive session lock. Mainly the values:

  • ReadOnly – Doesn’t block other requests because this request can’t update session
  • Disabled – Can’t block, and your best option for performance in StateServer & SQL modes because no serialization needs to occur. Remember that all of session is serialized and de-serialized per user every time. Not just particular keys your are accessing.

 

Throw a new for MVC 3 attribute with your desired Enum value on your Controller class as such:

 

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
    public class TestController : Controller
    {
        public ActionResult Index()
        {
            System.Threading.Thread.Sleep(10000);
            return new EmptyResult();
        }
    }

 

(It’s too bad you can’t do this at the action level. My initial thought is that is too late in the ASP.NET pipeline events once Action discovery occurs unfortunately)

 

Now take a look at our Firebug Net tab (that’s more like it! but what about that light brown bar…):

after

Browser Concurrent Request Blocking

Browsers limit the concurrent connections per server that can be used at a given time. I have read this is to be nice, to developers and server admins maybe? The brown bar in Firefox indicates a request that is being blocked until the connection frees up for another request. This can be changed by editing configuration entries but it’d be tough to ask for that on a publicly facing website!

 

Conclusion

Hopefully this helped you diagnose some odd Ajax performance issues! I know the solution isn’t really ideal if you wanted to update session. I find the more I develop web applications the less I use session in favor of using the DOM, jQuery Data, JSON etc, but that certainly doesn’t work for everything! I’m thinking I might explore the possibility of a “MergeSessionStateProvider” if the proper ASP.NET hooks are there to implement it. I think given where the web is going something like this might be becoming more and more critical for the best possible user experience.

Thanks for reading!

19 thoughts on “ASP.NET Concurrent Ajax Requests and Session State Blocking

  1. GREAT Summary! I wished I had read it before I spent far too much time troubleshooting. Still, thanks for confirming my findings.

  2. Excellent post. The session state was what was blocking my MVC app from streaming results even though I was running periodic refreshes via jquery to get them. Once I set the SessionStateBehaviour to ReadOnly I could see a tremendous difference. Thank you very much.

  3. Lock mechanism exist on both provider and session module (IIS Session Module). You can develop custom session module, but you still need provider without locking or You can develop custom provider without locking but you still need IIS session module and it is not so simple to implement at that level.

    The Solution is UnlockedStateProvider [aka Unlocked]

    Follow the white rabbit 😛 (Check the demo project, it explains everything.)

    1. Awesome! Good to know someone has implemented this. More recently I’ve actually ended up disabling Session State and using distributed caches (or even just calling the DB for user info or whatever). Any state I want to have I generally get from the cache/db and then hydrate into UserContext object with DI scoped to a request. Thanks for the reply!

  4. Holy crap, I am so glad we found this post! We were having an issue where multiple ajax requests on a page were causing hundreds of connections to get stuck in the RequestAcquireState state, and we were pulling our hair out. We were down for almost a full 24 hours, and no amount of server updates or config changes seemed to help. We finally found this article, implemented the attribute on our controller, and it works PERFECTLY! You are an absolute life saver. If you are ever in Tuscaloosa, AL, I owe you a beer! Thanks a ton.

    Derrick

    1. Glad it helped! Yeah nearly absurd that is the behavior! Most things these days I remove all the SessionProvider, Membership Provider etc. stuff and do it from scratch (there are much better ways to handle distributed session state these days with Redis etc) Works well in a pinch as long as you turn that part off for sure!

  5. Hi there to every one, the contents present at this website are in fact remarkable for people experience,
    well, keep up the good work fellows.

  6. I applied the attribute on an an asp.net mvc 4 website but the issue is still not resolved.

    Anyone else able to get this working in MVC 4 or above?

    1. Its a late reply but I getting the same issue as you highlight. Any help. I have lost my session object as well when there are concurrent ajax requests. One of the request got the session but other requests not able to get the session and its set the session is nothing and user is logged out. Please help.

  7. Hello @John Culviner

    Its a bit late but I getting the same issue as you highlight. Any help. I add this attribute but issue is still there. I have lost my session object as well when there are concurrent ajax requests. One of the request got the session but other requests not able to get the session and its set the session is nothing and user is logged out. Please help.

  8. Hello same issue i am facing here when i am calling multiple ajax request from my mvc application at same time its blocking the request and wait for some time, I have used session state behavior with both disable and read only properties on controller level but not work.
    Kindly help me.

  9. .Net core fixed this

    on the “regular” asp.net, you can give “attribute” to the code , “ReadOnly” works too but Disabled is for the example below:

    How to Fix:
    Disable Session for the Action: If the action does not need to access session state, you can disable session state for it by using [SessionState(SessionStateBehavior.ReadOnly)] or [SessionState(SessionStateBehavior.Disabled)] attribute, which will prevent the requests from blocking each other:

    csharp
    Copy code
    [SessionState(SessionStateBehavior.Disabled)]
    public class TestController : Controller
    {
    public ActionResult Index()
    {
    System.Threading.Thread.Sleep(10000);
    return new EmptyResult();
    }
    }

Leave a Reply

Your email address will not be published. Required fields are marked *

Proudly powered by WordPress | Theme: Cute Blog by Crimson Themes.