Monday, April 29, 2013

Resolving Controller Blocking within .NET 4.5 and ASP.NET MVC

While recently updating a major software package for a client, several very bizarre and often erratic issues began to start to arise involving inconsistent Controller calls, Controller “blocking” and apparent “deadlocks” that were basically rendering the application (and the poor browser handling it) useless. The update was performed in two phases, the first being a simple ASP.NET MVC3 project deployment (no biggie), however the second phase was an update from .NET 4.0 to the wonderful world of asynchronous that is .NET 4.5, which is where things seemed to go awry and our story begins.


The Problem
After updating to .NET 4.5, Internet Explorer 9 and below began encountering some strange issues regarding blocked calls to Controllers (and their contained Actions) within the application. The problem was typically occurring when an AJAX request was made to a Controller and during (but not always) an additional request was made to the same controller, which would cause the browser to “lock up” and typically hang between 1-2 minutes prior to resolving the issues and executing the queued actions.

An Example Scenario
  • User clicks a link that will temporarily store a value in the ViewData to be accessed on the next Request.
  • A Redirect occurs and another Controller is accessed (as it should be) and after loading an AJAX call is made.
  • During this AJAX call (which accesses the ViewData value properly) an additional call is quickly made (such as a user immediately clicking a link to navigate to another Controller).  After clicking this link, the “deadlock” will occur and the browser will become unresponsive.
                              or
  • The call will be successfully performed (as long as it is not interrupted by another request), but attempting to perform the previous steps again will fail.
Each page in question that was causing these issues was each doing one of the following two things :
  • The View contained an jQuery-powered Grid that would perform an AJAX call to populate the Grid contents upon the page loading.
  • The Controller Action typically was reading an item from the ViewData within these Actions.
An Investigation Ensues
After continually looking at different things that could be causing this issue such as the AJAX calls themselves, the SessionState settings for the Application, Caching, Temporary Storage (ViewData, TempData and Session) nothing seemed to make sense. I attempted to use Fiddler, the web’s most efficient detective however was let down as the traffic would not reveal anything.
Developer Tools and Fiddler would both show the AJAX call being made to the Controller Action, which contained a breakpoint, however the breakpoint would never be hit (at least not until the blocking issue was resolved).
Hhere are a few of the attempted fixes :

AJAX Calls - I reviewed over the AJAX calls that were occurring and attempted to change several of the properties and parameters that were being passed into the call without any luck. I hoped that by setting the cache to false that would ensure that the AJAX requests were not being cached (as IE can be notorious for caching GET requests). I also attempted to use the timeout property in hopes that when the request timed out that it would hopefully break up the “blocking” effect.
//Disabled caching and forced an explicit timeout on the calls
$.ajaxOptions({ cache: false, timeout: 1000 });

SessionState – The fact that the Controllers within the application were clearly blocking one another made me instantly think that the culprit was the SessionState and by setting it to ReadOnly or Disabled that this would fix the problem (especially due to the fact that the ViewData was being accessed within the Controller).
[Authorize]
[SessionState(SessionStateBehavior.ReadOnly)]
public class MyController : ApplicationController
Temporary Storage Access - I thought that the issue may have been due to the use of Temporary Storage within the application so I attempted to switch out the ViewData with the Session to temporarily store some values being passed across but I was still met with nothing but defeat.
//These guys aren't any fun to play with - kick em' out!
ViewData["YourKey"] = yourValue;
Session["YourKey"] = yourValue;
TempData["YourKey"] = yourValue;
Finally! Some Classical Salvation!
After doing some significant damage to my desk and debating doing the same to my laptop, I continued to rethink what could possibly be going so wrong and then I stumbled upon a suggestion that cured all of these developmental ailments :

Change the Application Mode within the Application Pool Settings in IIS from Integrated to Classic
If you are experiencing these same issues with your application and are running into very bizarre and difficult to track down problems regarding Controller blocking, try changing the Mode on your Application Pool within IIS to Classsic Mode. (It requires a bit of tinkering when working with MVC to handle routing the old school way – wildcard mappings)
I’ve found that this is the only working solution that I have encountered aside from uninstalling .NET 4.5. Although, I am not quite sure of all of the details as to why IE9, .NET4.5 and AJAX don’t seem to want to play nice with one another, I am certainly glad to have this issue resolved.
(I still believe that this issue could in fact be a bug involving the SessionState and how Internet Explorer 9 interacts with the updated mechanisms to implement asynchronous calls  with the recent .NET 4.5 release. If any Microsoft folks are out there and would care to elaborate a bit more on the issue, I would be glad to create a proof-of-concept.)

Update with a New and Improved Fix!
After some follow-up on this issue by several great members of the ASP.NET Forums as well as a few Microsoft employees (See thread here), we were able to narrow down a more specific fix that involved the uploadReadAhead property within the web.config :
<system.webServer>
  <serverRuntime uploadReadAheadSize="0" />
</system.webServer>
It should be noted that to apply this change, you will likely need to enable this property to be overridden by making a change within IIS, you’ll just need to change the <serverRuntime> property within your applicationHost.config file to “Allow” instead of “Deny” :
<section name="serverRuntime" overrideModeDefault="Allow" />
After some additional testing, I was completely unable to reproduce the issue and it appears that this is a pretty solid fix to the problem. (This apparently was a known bug and is scheduled to be fixed in an upcoming patch.)

No comments:

Post a Comment