Running parallel requests in an ASP .Net MVC application for faster page load times

Gepostet von Florian am 25.05.2022

ASP.Net Dotnet performance mvc

Website and app performance is becoming increasingly important as time goes by. In the last few years, users have become much more demanding when it comes to how long it should take for a site to load. I caught myself hitting the “back” button on my phone after 2 seconds when I still saw a white page after clicking on a Google link. Apart from delighting users, page speed also influences your Google rankings and more.

Recently, I noticed some requests were taking a very long time on a site I was working on. I knew these requests weren’t loading lots of data, but some of them took as much as 5 seconds to finish. This page was calling our API to fetch Json data. It was starting 5 parallel requests from the browser.

When we called each of them individually, they didn’t take longer than 1 second each. 🤔 But if they were called together on one page, they took up to 5 seconds.

This led me to investigate how .Net applications handle simultaneous requests. What I found out was that our application was using session locking. We never actively configured it in the application. It’s a behaviour of the .Net MVC and also .Net WebApi that activates as soon as you use the session somewhere in your project. In my tests, I also built an API with ASP.NET Core (.NET6) but it didn‘t seem to do any session locking even after configuring a session service.

This is safe, default behaviour. If your code is performing multiple reads and writes to your session inside a request, it wouldn‘t be safe to let multiple requests edit the same session all at once.

Can we run the requests in parallel?

In many cases, we have stateless methods that return data without ever accessing the session. Or methods that only read from the session. In those cases, we don’t want the server to only let one request through per user. What we want is for all the requests to be processed at the same time.

Replicating the behaviour

When we’re not using the session

I started a new MVC project that uses the DefaultSessionProvider. In a few lines of code, I had a method that has a predictably long-running operation and returns data. This simple API controller will run everything in parallel. If it receives 3 requests, it returns 3 results after 1 second.

When we start using the session

As soon as I started to access the session somewhere in our code, the requests started running in sequence. Somehow, the framework automatically detects if we‘re accessing the session in the code. It starts to apply session locking as a safety measure to only allow one request at a time per user. This code doesn’t have to be in the same controller method. It doesn’t even have to be in the same class. That‘s why it can be hard to spot it in a real-world application.

How to disable the session-based locking for only some requests

Our project was a 10-year-old MVC application. In the beginning, there were only HTML controller methods. As the application grew, some controller methods were added that returned Json. I wanted to keep the behaviour the same for all methods that return HTML and might access the session. But I also wanted to have our stateless Json data calls run in parallel.

The way we can achieve this is by adding the attribute [SessionState(SessionStateBehavior.Disabled)] to the controller class. This will disable the session locking mechanism for all methods inside of this controller.

The first controller is running all requests in parallel.

And the second controller is running all requests in sequence.

To trigger 5 simultaneous requests, I used the Chrome console with this one liner:

for(var i=0; i<5; i++) { fetch('https://localhost:7127/second/foo').then(x=> console.log( new Date() + ' done'))}