You know the time you use a development platform for years and begin to think that you can finally think of yourself as a veteran in that technology. And then you find a problem that challenges your understanding of the framework and you begin questioning yourself. Well, this is precisely one of those times.
The application had an existing web page that would call another web service which was known to be notoriously slow. Any time you would try to load this page, it would take more than a minute for this page to load.
While working on the new page, I happened to open the slow page in another tab right before I tried to test the new AJAX call. The browser was waiting in another tab on this slow page and I was on the current one waiting for the AJAX call to respond.
What I noticed was that the AJAX call did not respond till the first slow page finished loading in another tab. It would normally take less than a second to respond. However, it finished right after the first request was completed.
This was surprising and it appeared that the AJAX call was queued behind the first slow request. This should logically not happen as we have multiple threads on the server which can service more than one request at a time. This is IIS, a mature web server and it cannot be the case that it can only respond to one request at a time.
After making sure the behavior was repeatable in a test web application, I checked the application pool settings on IIS to make sure all the settings were sane and preferably set to default values.
After some googling around, I came across these articles:
- When a Single ASP.NET Client makes Concurrent Requests for Writeable Session Variables
- I just discovered why all ASP.Net websites are slow, and I am trying to work out what to do about it
And there it was! ASP.NET locks the session object during the request life cycle. This means that any request from the same user will queue behind a previous request!
This means that as a user, it is of no use to open a another URL in a new tab in the same application while another page is loading. The second request will not begin executing until the first one has finished. The second request will simply wait for the first one to complete before it gets to lock the session variable for itself and continue execution.
Now this seems highly inefficient at first sight. Why would anyone implement a web server in such a manner. But it seems that this is a very old behavior from the early days of ASP.NET when we did not have a web full of client side frameworks and asynchronous requests and the priority for web platform developers was to keep the desktop to web transition simple for existing developers. Microsoft sacrificed performance in favor of the integrity of the session variable. I would agree that for the time, this was the better decision as we developers did not have to worry about race conditions in our code and access the session variable freely in the request pipeline. This essentially makes the session variable thread safe.
However in today’s world, where many of the applications have moved to client side platforms and frameworks like Angular, React etc. Session locking can be a severe limitation to the scalability of the web application if you happen to have some slow pages.
To be fair, if you opt out of session state or reduce the session state to read only mode, you will not face this behavior as it only applies to requests that can write to the session state. However it is a gotcha that needs to be considered when working with ASP.NET.
It’s humbling to know after working all these years as a .NET developer that something so crucial can go unnoticed for so long.
Learning something new every day… 🙂