@mikehadlow, @CraigCav: Using the #TPL with ASP.NET MVC for scalable web applications – not working?

Mike Hadlow posted about the out of the box ASP.NET MVC support (AsyncControllers) for developing scalable web tiers. He gives reasons why the “old” APM style of writing AsyncControllers is cumbersome and then shows how can we utilize TPL library to make code more maintanable and laments that MVC still doesn’t have full/deep support for TPL Library… Craig Cavalier takes it from there and, with the help of ASP.NET MVC Futures and TPL Extensions Library shows how that works end to end. Very nice… I had to try it. Got a test  mvc site with simple example that uses “old” style AsyncController approach and that shows which part is executing from which thread. This is the result:

Seems, fine. We get to not block on the ThreadPool thread – as designed for. Code is old APM style, and this is how the code for this controller  looks like:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;

namespace MvcApplication3.Controllers {
	public class HomeController : AsyncController {
		[HttpGet]
		public void IndexAsync() {
			AsyncManager.OutstandingOperations.Increment();
			ViewData["indexAsync"] = Helper.GetThreadInfo("IndexAsync");
			Task.Factory.StartNew(LongOperation, TaskCreationOptions.LongRunning) ;
		}

		public ActionResult IndexCompleted(string reault) {
			ViewData["longOperation"] = AsyncManager.Parameters["result"];
			ViewData["indexCompleted"] = Helper.GetThreadInfo("IndexCompleted");
			return View();
		}

		void LongOperation() {
			try {
				Thread.Sleep(1234);
				AsyncManager.Parameters["result"] = Helper.GetThreadInfo("LongOperation");
			}
			finally {
				AsyncManager.OutstandingOperations.Decrement();
			}
		}
	}
}

Then I tried to do the same for Craig’s example… and that doesn’t look good, we are now just grabbing additional thread from the thread pool, while blocking on the original thread. The very reason why AsyncController was there in the first place, seems to be negated.

Here are the results (tried many times with the same results):

Here is the code for this case:

using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
using AsyncControllerHelper;
using Suteki.AsyncMvcTpl.Services;

namespace Suteki.AsyncMvcTpl.Controllers {
	public class HomeController : Controller {
		readonly UserService userService = new UserService();

		public HomeController() {
			IControllerDescriptorCache descriptorCache = new AsyncControllerDescriptorCache();
			ActionInvoker = new BetterAsyncControllerActionInvoker(descriptorCache);
		}

		[HttpGet]
		public Task<ViewResult> Index() {
			ViewData["indexAsyncPre"] = Helper.GetThreadInfo("IndexPre");

			var _view = from user in userService.GetCurrentUser()
									select View(user);

			ViewData["indexAsyncPost"] = Helper.GetThreadInfo("IndexPost");

			return _view;
		}

		[HttpGet]
		public Task DoStuff() {
			var doStuff = from user in userService.GetCurrentUser()
										from _ in userService.SendUserAMessage(user, "Hi From the MVC TPL experiment2")
										select View("index", user);
			return doStuff.ContinueWith(x => { });
		}
	}
}

I may be doing something wrong. Will check later – it is Sunday afternoon after all…

From mike’s post:
“… You should only ever use this technique if you have an immediate scalability concern and you know that it is caused by a threadpool full of threads blocked by long running IO operations.”

Mike’s original post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: