November Happy Hour will be moved to Thursday December 5th.

Magnus Rahl
Jan 20, 2012
  13909
(1 votes)

Async Pages part 1: How async pages may save your (server’s) life

My account is fixed and I’m back in style with EPiServer World CRP Oracle Status! I celebrate that with publishing a three part TLDR about async pages that I haven’t been able to publish while my account was ill.

Intro

If you do only EPiServer projects and all of them are vanilla sites without integrations with or calls to any external systems you can probably stop stop reading now. EPiServer only calls it’s database and after the site is warmed up and cached such database calls are probably fairly uncommon and don’t impact performance.

If you do, on the other hand, use any kind of custom database, call webservices, do HTTP requests or do any other kind processing that isn’t “CPU-crunching-only” (perhaps even then) then you might want to look at asynchronous page processing. If you haven’t already.

If you want the background and the whys read the following section. If you just want the goodies, skip to the Solutions section. (And then probably go back to understand why those are actually goodies.)

Theory and empiric evidence (AKA problems)

I have seen many examples of this kind of asynchronous processing, but the pattern always looked ugly and cumbersome to code. And my sites worked just fine anyway until now. Or did they?

Sudden awful performance

The case at hand: Suddenly users experience spikes in load times, somtimes up to 60 seconds. CPU loads of web servers and database servers at the same time are not very high at all. So what’s going on?

Since we have access to nice tracing tools logging activities in the production environment we could soon find some requests running for very long times. They were doing web requests which eventually timed out. As you may know the default timeout of a WebRequest in ASP.NET is something like 120 seconds. Luckily we were using a 3 sec timeout but that was not enough.

The IIS Pipeline

To help understand why, here’s a recap of how ASP.NET and IIS work when delivering pages.

  • IIS starts processing the request on a thread.
  • IIS hands over the request to ASP.NET which hands it over to the ThreadPool.
  • The IIS thread is freed and can handle another request, for example for a static file which it handles itself without the help of ASP.NET.
  • A worker thread becomes free and picks up the request from the ThreadPool.
  • The worker thread checks to see how many requests are currently processing. If the number is higher than a certain threshold it puts the request on hold. Otherwise it starts processing it.
  • Once a request is processed it pretty much runs to the PreRender stage, in the case of a Page. If the page is synchronous (the default) it will run all the way through and is delivered back to the client.

The above holds for IIS7, things are slightly different in IIS6.

ASP.NET can handle a very large number of requests this way using modern hardware and if each request doesn’t take too long to complete. But synchronous requests will of course block their thread even if they are not processing, if they are waiting for an external resource like a web request. That’s what happened in our case.

Thread limit in ASP.NET

As you may know the ThreadPool can basically create any number of threads, and will create new threads if the load is low and there is work to do, within certain limits (because each thread uses memory and each switch between threads uses CPU, AKA context switching).

But ASP.NET enforces its own threshold, as mentioned above. This threshold is not set in number of threads but rather number of concurrent requests. That is of course equivalent if the requests are synchronous.

In .NET 3.5 this threshold is only 12 requests per CPU. So if your requests are synchronous take one second to complete ASP.NET can only handle 12 requests per second which isn’t a lot. And one second can be a very real delay if you consume external services, especially if those services are down and therefore have to time out before your processing completes. Other requests coming in will be queued.

In our case matters were even worse. The web requests were requests to get RSS feeds, and it turned out many of those feeds were actually set by editors to URLs on the same site. See the problem there? Yup, that’s right, requests can basically block themselves in a kind of deadlock situation if the queue fills up. One request is sitting in the pipeline waiting for the requests that are sitting behind it in the queue. This is what made response times go tectonic.

Solution

Configure ASP.NET concurrency

So how do you solve this? One way is to set the maxConcurrentRequestsPerCPU in aspnet.config to a higher value, effectively allowing more threads to process. Or use .NET 4 which sets this value to 5000 by default.

Make your Pages asynchronous

But increasing the number of concurrent synchronous requests can cause increased overhead. The only way to increase throughput is to increase the number of threads, and as we know thread creation and switching isn’t free performance wise, neither considering CPU nor memory. So the original problem is still basically there.

To learn how to write asynchronous Pages, move on to the next part: How to use asynchrony in your Pages

Jan 20, 2012

Comments

Please login to comment.
Latest blogs
Optimizely SaaS CMS + Coveo Search Page

Short on time but need a listing feature with filters, pagination, and sorting? Create a fully functional Coveo-powered search page driven by data...

Damian Smutek | Nov 21, 2024 | Syndicated blog

Optimizely SaaS CMS DAM Picker (Interim)

Simplify your Optimizely SaaS CMS workflow with the Interim DAM Picker Chrome extension. Seamlessly integrate your DAM system, streamlining asset...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Optimizely CMS Roadmap

Explore Optimizely CMS's latest roadmap, packed with developer-focused updates. From SaaS speed to Visual Builder enhancements, developer tooling...

Andy Blyth | Nov 21, 2024 | Syndicated blog

Set Default Culture in Optimizely CMS 12

Take control over culture-specific operations like date and time formatting.

Tomas Hensrud Gulla | Nov 15, 2024 | Syndicated blog

I'm running Optimizely CMS on .NET 9!

It works 🎉

Tomas Hensrud Gulla | Nov 12, 2024 | Syndicated blog

Recraft's image generation with AI-Assistant for Optimizely

Recraft V3 model is outperforming all other models in the image generation space and we are happy to share: Recraft's new model is now available fo...

Luc Gosso (MVP) | Nov 8, 2024 | Syndicated blog