What Happens in Beginprocessrequest()

What happens in BeginProcessRequest()?

What you might be seeing is commonly referred to as thread agility in .NET.

What you're probably seeing as far as the results underneath the topical label (i.e. Application code in System.Web.HttpApplication.BeginRequest()) is a thread agility problem; in most cases the time you see here isn't necessarily code being executed but the web context waiting for the threads to be released back to it from a reader-writer lock.

The Application_BeginRequest() "pause" is one that is pretty pervasive in a ASP.NET web stack. In general when you see long load times in BeginRequest, you are dealing with ASP.NET thread agility and/or thread locks - especially when dealing with IO and session based operations. Not that it's a bad thing, this is just how .net makes sure your threads remain concurrent.

The time gap generally occurs between BeginRequest and PreRequestHandlerExecute. If the application is writing several things to session then ASP.NET will issue a reader-writer lock on HttpContext.Current.Session.

A good way to see if this is an issue that you might be facing would be to check the thread IDs to see if agility is an issue - the IDs will be different for a given request.

For instance. While debugging, perhaps you could add the following to your Global.asax.cs:

protected void Application_BeginRequest(Object sender, EventArgs e) { 
Debug.WriteLine("BeginRequest_" + Thread.CurrentThread.ManagedThreadId.ToString());
}

Open up the debug output window (From Visual Studio: View >> Output, then select "Debug" from the "show output from" dropdown).

While debugging, hit a page where you have seen the long time. Then view the output log - if you see multiple id's then you might be suffering from this.

This is why you might see the delay sometimes but not other times, the application code might be using session a little differently or session or IO operations might be higher or lower from page to page.

If this is the case some things you can do to help speed things up depending on how session is used on the site or each given page.

For pages that do not modify session:

   <% @Page EnableSessionState="ReadOnly" %>

For pages that do not use session state:

<% @Page EnableSessionState="False" %>

If the app does not use session (web.config):

<configuration>
<system.web>
<sessionState mode="Off" />
</system.web>
</configuration>

So let's take the following example:

User loads a page, then decides to go to another page before the first request is done loading ASP.NET will force a session lock causing the new page request load to wait until the first page request finishes. With ASP.NET MVC each action locks the user session for synchronization; causing the same issue.

All of the time it took for the lock to be release will be reported via new relic, not to mention the ones where the user abandoned the session and the thread coming back is looking for a user who no longer exists.

Incidentally the UpdatePanel control causes the same behavior -

http://msdn.microsoft.com/en-us/magazine/cc163413.aspx

What can be done:

This locking problem is one of the reasons Microsoft has the SessionStateUtility class -

http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstateutility.aspx

So that you can override the default behavior if you face this problem as seen here in this
Redis implementation:https://github.com/angieslist/AL-Redis

There are many options to the default state provider used by .net based websites. But know generally this transaction time indicates that threads are being locked and waiting on requests to the server to be completed.

HowTo: BeginProcessRequest

The issue here is that, logically, you still require the thread to be stalled. Your process needs to accept the HttpContext in its ProcessRequest() method, do something (Db access), and write something back to the context.

The stateless nature of HTTP means that socket connections are opened, the request is made, the client awaits a response, and the connection is closed. You don't have an open connection just waiting around for your handler to write something back to the client. In your example above, where does EndProcessRequest() get its HttpContext object to write its output? If it's the same one that was passed in to BeginRequest, which it must be if you're trying to get your output to go back to that client, then the client is going to spend the entire time between request and response waiting - while maintaining an open connection - for the server to do its thing and send some output. That context and its related connection must be kept open as long as your separate thread is spinning, which means that you are, in fact, blocking the ASP.NET thread in the mean time.

You gain nothing from launching an additional thread unless you are launching more than one, and can guarantee that they will complete in a reasonable amount of time.

Also, this is unnecessary. In order for the thread exec time to be a problem, you would have try to process more requests "simultaneously" than the server can handle. If your "db access" method is taking too long, you'll wind up queuing your requests, which will eventually lead to timeouts. The solution here is to keep your response times (page render times and all db access, etc that rendering requires) as short as possible.

Summary:

  1. Don't fire off a single additional thread - this is only useful if you are running multiple threads

  2. You must make sure that any additional threads you initiate will complete within the alloted time for an HTTP request

  3. The calling ASP.NET thread will have to wait until a response is written back to the Http context, or until a timeout occurs. This may result in orphaned threads if you are launching new ones in the ProcessRequest method.

In short, don't do it. If you need to initiate processes from BeginRequest that are asynchronous and do not have to write output back to the client, consider writing a windows service to handle these requests. Log each request into a table, and have your service poll that table to see what needs to be done. Have your client poll that table to see when a result is ready (possibly via AJAX). This may not be appropriate for your application, but is one way to handle this kind of problem.

Why is IHttpAsyncHandler being called over IHttpHandler?

Brad Wilson responded to my post on the Asp.net forums with the following answer http://forums.asp.net/t/1547898.aspx:

Short answer: yes.

With the addition of AsyncController,
the MvcHandler class needs to be an
IHttpAsyncHandler now, which means
that as far as the ASP.NET core
runtime is concerned, the entry points
are now BeginProcessRequest and
EndProcessRequest, not ProcessRequest.

It sounds like ProcessRequest is not even called anymore, but I could be mistaken. I can say that I haven't seen it in my testing.

Where is the asynchronous part here?

When this handler receives a request, it will call BeginProcessRequest.
It does not block the calling thread.

When processing is complete it will call EndProcessRequest.

This will leave your asp.net worker process threads free to serve other requests while this is processing.
When EndProcessRequest is called, you get to send the processed result back to the client.

Here is a lot of explanation about how asp.net manages threads during an async request lifecycle.

During the lifetime of an asynchronous page, the context starts with
just one thread from the ASP.NET thread pool. After the asynchronous
requests have started, the context doesn’t include any threads. As the
asynchronous requests complete, the thread pool threads executing
their completion routines enter the context. These may be the same
threads that initiated the requests but more likely would be whatever
threads happen to be free at the time the operations complete.

If multiple operations complete at once for the same application,
AspNetSynchronizationContext will ensure that they execute one at a
time. They may execute on any thread, but that thread will have the
identity and culture of the original page.

Some reasoning on when to use async:

Having an async handler is only useful if to process the request you
have other async steps available (such as an off-box database call or
a long hard drive read that you can call async as well.) To do this
properly you would chain async methods (ie BeginProcessRequest would
call FileStream.BeginRead with the same (or seperate) callback and
handle accordingly.)
Refer here:
http://msdn.microsoft.com/en-us/library/system.web.ihttpasynchandler.aspx

Take a look at this very detailed explaination on how to use asynchronous handlers

To build a truly effective asynchronous handler, you must spawn an
additional thread by hand in response to BeginProcessRequest. There
are three important aspects to building a successful asynchronous
handler. First, construct a class that supports IAsyncResult to return
from BeginProcessRequest. Then, spawn the thread to perform your
request processing asynchronously. Finally, notify ASP.NET that you
are finished processing the request and are ready to return the
response.

To summarize: if you are not creating a processing thread, or waiting for a long time, an async handler won't do much good. When waiting, the request has no threads associated with it. This allows asp.net to scale well even with long waiting tasks.

Intermittent delays in System.Web.HttpApplication.BeginRequest(), not SessionState related

New Relic doesn't have instrumentation specific to ServiceStack and WCF is very basic without custom instrumentation. Without more information, it's very difficult to offer any advice. Thread agility is also a primary culprit and I'd recommend investigating that route first.

It is possible New Relic is attributing the time to a method it shouldn't. I would probably start by opening a ticket with New Relic Support and include all your info (agent logs, IIS/ASP.NET configuration, custom handlers? and permalinks to your New Relic graphs).

Trying to add functionality to MvcHandler

Hey, I also just ran into this problem. My breakpoints in ProcessRequest() were never being hit. I don't have a full-proof resolution to your problem, but I do have a work-around which might get you what you want. Try overriding the BeginProcessRequest() method from MvcHandler instead of ProcessRequest(). The Context should contain the route (action and controller) information if that's what you're looking for.



Related Topics



Leave a reply



Submit