Parallel processing of concurrent Ajax requests in Asp.Net MVC

If you develop web applications that relies on JavaScript to update the interface and you use several Ajax calls inside a single page, then you should know that all those calls are not executed in parallel on the server. On a default controller, each call to an action locks the user’s Session object for synchronization purposes, so even if you trigger three calls at once from the browser, these calls will get queued and executed one by one.

In order to speed up the requests processing you should reduce the number of actions that needs to write in the Session object or TempData to a minimum and make a dedicated controller for them. The rest of the actions can now be executed in parallel using controllers that are only reading values from Session. 

If you want to enable a certain controller to process requests in parallel from a single user, you can use an attribute named SessionState that was introduced in MVC since version 3. It’s not necessary to use a session-less controller in order to achieve parallelism, the Ready-only option of the SessionStateBehavior will let you pass security checks you may have implemented based on Session data.

ParallelController.cs
    [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
    [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
    public class ParallelController : Controller
    {
        public JsonResult Get1()
        {
            //read from session
            var x = Session["call"];
            Thread.Sleep(5000);
            return Json(new { obj = "p1" }, JsonRequestBehavior.AllowGet);
        }

        public JsonResult Get2()…
        public JsonResult Get3()…
    }

The serial controller has the same actions but writes data in the session object:

SerialController.cs
    [SessionState(System.Web.SessionState.SessionStateBehavior.Required)]
    [OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
    public class SerialController : Controller
    {
        public JsonResult Get1()
        {
            //write to session
            Session["call"] = "call";
            Thread.Sleep(5000);
            return Json(new { obj = "s1" }, JsonRequestBehavior.AllowGet);
        }

        public JsonResult Get2()…
        public JsonResult Get3()…
    }

Calling both controllers from jQuery at document ready:

Index.cshtml
    $(document).ready(function () {

        start = new Date().getTime();

        for (i = 1; i <= 3; i++) {
            $.ajax('Parallel/Get' + i, {
                cache: false
            }).done(logParallel);
        }

        for (i = 1; i <= 3; i++) {
            $.ajax('Serial/Get' + i, {
                cache: false
            }).done(logSerial);
        }

    });

In Firebug you can see on the Network tab, that all six calls are fired at once and all calls made to the parallel (SessionStateBehavior.ReadOnly) controller returns in 5 seconds along with the first call that reached the serial (Read-Write Session) controller. The Get2 call to the serial controller will wait for Get1 to complete, and Get3 will wait for Get2, lasting 10 seconds longer overall. The Firebug timeline is more accurate then my js date diff.

The result showed here where taken with the project hosted on a dedicated Webserver. On my home pc, sometimes, the results got messed up because the browser can fire some calls  too late or, on the server side, the call is queued inside the ThreadPull for a thread that is already busy. Azure would be a good place to host this test.

Prerequisites & Download

In order to easily install all the prerequisites you can use the Web Platform Installer version 4.

download source code files

Tags: , ,