We have a website that is struggling with concurrent users right now.
Here is the very high-level background of the project:
- Legacy ASP.NET MVC 3 project (.NET 4)
- Can't do any major rewriting of core code
- Main entry point that takes the longest time to execute is the
SubmitSearch
action on theSearch
controller. Average time to respond is 5-10 seconds.
So as the second point outlines, we don't want to spend too much time on this project rewriting large sections. However, we want to attempt to increase concurrent users. We're not looking to change anything else or increase performance since it would require much more work.
What we are seeing is that as more people hit SubmitSearch
, the web site in general slows down. That's most likely due to all the IIS threads being locked up executing the search.
We are looking to implement AsyncController
and making the SubmitSearch
action execute on a normal CLR thread. Here's how we wanted to implement it:
Assume this is the original SubmitSearch
method:
/// <summary>
/// Submits a search for execution.
/// </summary>
/// <param name="searchData">The search data</param>
/// <returns></returns>
public virtual ActionResult SubmitSearch(SearchFormModel searchData)
{
//our search code
}
The quickest way we were hoping to convert to AsyncController
is to simply do this:
/// <summary>
/// Submits a search for execution.
/// </summary>
/// <param name="searchData">The search data</param>
/// <returns></returns>
protected virtual ActionResult SubmitSearch(SearchFormModel searchData)
{
//our search code
}
/// <summary>
/// Asynchronous Search entry point
/// </summary>
/// <param name="searchData"></param>
public void SubmitSearchAsync(SearchFormModel searchData)
{
AsyncManager.OutstandingOperations.Increment();
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
ActionResult result = SubmitSearch(searchData);
AsyncManager.Parameters["result"] = result;
AsyncManager.OutstandingOperations.Decrement();
});
return;
}
/// <summary>
/// Called when the asynchronous search has completed
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
public ActionResult SubmitSearchCompleted(ActionResult result)
{
//Just return the action result
return result;
}
Of course this didn't work because all through-out the code, we are referencing HttpContext.Current
, which we know ends up being null
in this approach.
So we were then hoping to do this with SubmitSearchAsync
:
/// <summary>
/// Asynchronous Search entry point
/// </summary>
/// <param name="searchData"></param>
public void SubmitSearchAsync(SearchFormModel searchData)
{
AsyncManager.OutstandingOperations.Increment();
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
ActionResult result = null;
AsyncManager.Sync(() =>
{
result = SubmitSearch(searchData);
});
AsyncManager.Parameters["result"] = result;
AsyncManager.OutstandingOperations.Decrement();
});
return;
}
This fixes the issue.
So here's my concern:
Does wrapping the execution of SubmitSearch
in the AsyncManager.Sync
method defeat the purpose of using this model? In other words, when we are within the AsyncManager.Sync
method, are we back on the IIS threads, which puts us back at square one?
Thanks