Today, I wanted to simulate waiting for a long-running blocking process (5 to 30 seconds) from within an AsyncController in an MVC3 web role. However, to begin, I just started with 1 second, to get things going. Yes, the wisdom of this is questionable, since the blocking operation cannot currently be run asynchronously on an I/O Completion Port to an external service, but I wanted to see what the performance limit is for this particular situation.
In my web role, I deployed 6 small instances. The only controller was an AsyncController, with two simple methods intended to simulate a 1000ms blocking operation.
The MVC3 web role controller was simply this:
public class MessageController : AsyncController
{
public void ProcessMessageAsync(string id)
{
AsyncManager.OutstandingOperations.Increment();
Task.Factory.StartNew(() => DoSlowWork());
}
public ActionResult ProcessMessageCompleted()
{
return View("Message");
}
private void DoSlowWork()
{
Thread.Sleep(1000);
AsyncManager.OutstandingOperations.Decrement();
}
}
Next, I applied stress to the web role from Amazon EC2. Using 12 servers, I ramped the load up slowly, and got close to 550 requests/second. Any attempt to push beyond this was met with apparent thread starvation and subsequent errors. I assume that we were hitting the CLR thread limit, which I understand to be 100 threads per CPU. Figuring some overhead for the AsyncController, and an average of 550/6 = 92 requests per second per server for a 1000ms blocking operation seems to fit that conclusion.
Is this for real? I have seen other people say similar things, where they reached 60 to 80 requests per second per instance with this type of load. The load on this system will be comprised mainly of longer-running operations, so 92 requests per second at 1000ms is going way down when the 5000ms tasks come online.
Short of routing the requests for the blocking I/O through multiple separate web role front ends to fan this load out to more cores, is there any way to get higher than this apparent limit of 90 or so requests per second at 1000ms block time? Have I made some kind of obvious error here?