Execute Ajax calls concurrently without session locking

Large Sitecore applications can be bogged down by pressure on the session state store. This can also be an issue in regular ASP .NET sites and this SO article helps to explain the issue and provides several solutions. Sitecore provides options in this great KB article. In a previous article I wrote about a solution when session locking is slowing down MVC pages.

Another perhaps even more common scenario is when session locking is slowing down multiple Ajax calls. Sitecore’s previously mentioned KB article recommends to not send multiple Ajax calls back to Sitecore. This is good advice and in general it is a good practice to combine Ajax calls as much as possible. However often there are scenario’s where this can be challenging, for example when there are multiple independent components on a page and they each need to load data through Ajax. Before diving into the solution lets look at the issue with multiple Ajax calls.

Ajax calls and session locking

The issue with multiple Ajax calls is that IIS will execute them one at a time. In this article lets look at below simple controller method, which will just wait for 10 seconds and then return:

public class SessionLockTestController : SitecoreController
{
    public async Task<JsonResult> ServiceCall()
    {
        await Task.Delay(10000);

        return Json(new { Ok = true }, JsonRequestBehavior.AllowGet);
    }
}

This Sitecore article describes how to register a custom route. Below image from the network panel shows what happens when a page makes 3 concurrent Ajax requests to this method:

  • Each call starts at the same time
  • The first call completes after 10 seconds
  • The second call completes after 20 seconds, because it had to wait for the first call to finish before it could start
  • The third call completes only after 30 seconds, because it had to wait for the second call to complete before it could start

As can be seen the final Ajax call only completes after 30 seconds. This locking mechanism will have a significant impact on the page loading performance, and also puts a lot of stress on the session store as each locked requests polls the session store frequently to see if it can start executing.

Reducing the locking of Ajax calls

Before unblocking the call it is important to understand what the relationship is between the Ajax call and the session in your solution. There are 3 possibilities which will be discussed separately.

  1. Ajax calls do not use session
  2. Ajax calls read from the session, but do not update it
  3. Ajax calls update/write to the session

Below sections will discuss how to address each scenarios

1. Ajax calls do not use session

This is the easiest scenario. If an action does not use session state, then the controller should disable the session state as can be seen below. In this case the Session object will be null. This will make the action not be subject to any session locking.

[SessionState(SessionStateBehavior.Disabled)]
public class SessionLockTestController : SitecoreController
{
    public async Task<JsonResult> ServiceCall()
    {
        await Task.Delay(10000);

        return Json(new { Ok = true }, JsonRequestBehavior.AllowGet);
    }
}

Below is the same code calling this method. No request is blocked now and they all execute at the same time.

2. Ajax calls read from the session, but do not update it

Sometimes an action method needs access to the session, but does not need to update it. In this case the sessionstate behavior should be set to readonly as can be seen in below code:

[SessionState(SessionStateBehavior.ReadOnly)]
public class SessionLockTestController : SitecoreController
{
    public async Task<JsonResult> ServiceCall()
    {
        await Task.Delay(10000);

        return Json(new { Ok = true }, JsonRequestBehavior.AllowGet);
    }
}

There are a couple of important things to note about readonly session state:

  • The code will have access to the session, an can update it. However any change to the session will be lost after the request.
  • Multiple requests in readonly mode will execute at the same time
  • A request in readonly mode still needs a read lock on the session, and cannot execute at the same time as a request which has read and write access to the session i.e. SessionStateBehavior.Required. For example if one Ajax call is setup in a controller with SessionStateBehavior.Required, then any other Ajax calls with SessionStateBehavior.ReadOnly will still have to wait

3. Ajax calls update/write to the session

This is the most complicated scenario. As far as I know there is no way for Ajax calls to run concurrently, and each update the session, even if you would address the concurrency issues in the application.

A different approach needs to be used since there is no option to execute the requests at the same time while also updating the session. Here are some approaches which might work:

  • Only update session state on initial page load, and use Ajax calls to retrieve additional information which should not be stored in session
  • Have a single Ajax call do all the updates to the session
  • Store any updates somewhere on the client, for example local storage, and update the session at a later point
  • Store data server side in some other store outside of session, and have the application manage concurrency

My Ajax calls are always fast anyway, should I not worry about this?

NO! Each Ajax call will still need to obtain a lock, and this check happens only every 500ms. Consider below method which returns immediately:

public JsonResult ServiceCall()
{
    return Json(new { Ok = true }, JsonRequestBehavior.AllowGet);
}

Below is the same network window with the default session locking, it still takes more than a second before the 3rd API is complete.

Compare this to the same network diagram of the same code without locking below. In this case all Ajax calls return in under 40 ms!

Conclusion

This article showed the detrimental effect the session locking mechanism can have on multiple Ajax calls. Multiple techniques were described to significantly mitigate or eliminate this issue. It is important to remember to try to avoid using session as much as possible, as it comes with scalability and availability concerns. Consider using more advanced and extensible options instead of session if a significant amount of user data needs to be stored server side. The session locking mechanism is on by default. Being able to disable the session can result in large performance improvements.