1

Hedge - this could end up being a 'dumb newbie' question, but if not it could be one of the more interesting posts today.

Basically, I'm seeing that merely adding a Global.asax component to a web application causes the runtime to execute page requests serially, whereas web applications without a Global.asax process requests in parallel as expected. Impact would be that scalability goes out the window if you use Global.asax - I'm skeptical that's actually the case but am currently blocked on any better explanation.

Here's the scenario:

  1. Create a new blank web application.
  2. Add a page that takes 5 seconds or so to complete. I used the below but the exact method of delay doesn't seem to matter:

    DateTime PageStartTime = DateTime.Now;

    while (DateTime.Now < PageStartTime.AddMilliseconds(5000)) { System.Threading.Thread.Sleep(0); }

  3. Add another page to use as a utility to reset the app domain. This isn't strictly necessary to see the problem behavior but is helpful in repeating the tests:

    HttpRuntime.UnloadAppDomain();

  4. After building, start a browser with one tab (or window) for the app domain reset, and 3 tabs (or windows) for the long-running page.

  5. F5 the 'reset' page, wait a sec, then F5 the 3 'delay page' tabs. You'll see that (as expected) the 3 pages end about the same time, i.e. that they're clearly running in parallel. Run all the F5's you want and they'll still parallel as expected, presumably until the cows come home.

  6. Back to the project, add a 'Global Application Class' (Gloabl.asax) component. No need to add any code - mere presence of the component is the test basis.

  7. After building, return to browser and quickly F5 the 4 tabs. Again, the long-running pages parallel as expected, at least if you hit the 3 tabs fairly quickly. Now for the good part - wait a sec until all complete, then (without using the reset tab) F5 the 3 'delay' pages again - you'll see that:

    a. The total time for all 3 to run is about 15 seconds - 3 times longer than expected. The pages appear to be processed serially, i.e. one after the other.

    b. The order of completion for the second and third tabs is random, suggesting that ASP hasn't even decided which should run first until after the first request has completed.

    c. If you reset (unload) the app domain, you get another chance to have the pages parallel, but after they complete that first cycle they go right back to serial execution.

  8. Back to the project, remove the Global.asax component, build, and retest - the page requests again parallel as expected for the long-haul.

Cool, eh?

Stuff I tried:

  1. Various .Net versions - 3.5 and 4,
  2. Various web servers - WebDev, UltiDev, IIS,
  3. Different ways of delaying the page - real work, with and without yield, etc,
  4. Browser usage - one browser window with multiple tabs, versus dedicated browser windows,
  5. Code placement - tried both page 'Load' and 'PreRender' events for delay code, ~. Other stuff now rolled off of gray-haired memory...

Clearly the above isn't a representative or useful project, but I stumbled on this doing something that has both intentionally long-running pages and an actual need for Global.asax - mainly for global static variables. I imagine most 'normal' web apps would run fast enough that few would notice if they reverted to sequential request execution, but this behavior sure broke mine - been hammering this for three days so any ideas most appreciated!

Thanks, David

3 Answers3

6

The culprit is the Session_Start event handler that's added by default to Global.asax.cs:

protected void Session_Start(object sender, EventArgs e)
{

}

The presence of this event handler causes ASP.NET to create and associate an actual session with the session cookie, even if you never store anything in session state. And once there's an actual session, requests are serialized.

In your scenario, just delete the empty Session_State event handler, and requests will be executed in parallel again.

(There's also an empty Session_End event handler in Global.asax.cs, but it does not affect whether requests are serialized.)

Michael Liu
  • 52,147
  • 13
  • 117
  • 150
1

My guess is that for some reason you got a session assigned to your requests and different browser tabs/windows share the same session. If this is so, the runtime will execute requests sequentially.

ASP.net session request queuing

ASP.NET MVC and Ajax, concurrent requests?

If you sniff the traffic with an http debugger, you can easily verify this.

Community
  • 1
  • 1
Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
  • This is my exact experience too. To prevent blocking other requests I disconnect a request from a session (don't send cookies back) and send some kind of token as part of the URL. – siva.k Sep 29 '13 at 20:50
  • Thanks so much Wiktor & Siva - adding 'EnableSessionState="false"' to the Page directive does the trick. Makes sense now after reading the excellent writeups! – david mcbride Sep 29 '13 at 23:01
0

The ASP.NET session might be holding your requests pending...

Try the below code. More information available in the page

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
public class HomeController : Controller
{
}
Kavi
  • 181
  • 1
  • 6