Showing posts with label async. Show all posts
Showing posts with label async. Show all posts

Wednesday, January 14, 2009

Fire and Forget Email, Webservices and More in ASP.NET

Often times when you're working on a web site you want to fire and forget an email, a web method or, most common in our case, a Facebook call. There's a good chance there's a Framework method available to do that for you quite simply. They're suffixed with the word Async. For email there's the System.Net.Mail.SmtpClient class. The following dirt simple code will send an email for you asynchronously:

Download the sample code

var s = new SmtpClient();
s.SendCompleted +=
(sender2, e2) =>
{
//do something when the send is done.
//retry if error, etc.
};

s.SendAsync(from.Text, to.Text, "", message.Text, null);

Well, that's pretty darn simple! Create a new SmtpClient. Call SendAsync and pass in your message data. Cool. There's even a whole set of classes to help you with attachments, multiple formats (like html and text), etc. From your console app or Windows Service this will work beautifully! The problem is, in an ASP.NET page this won't work. If you do this in a Page_Load or button click event, for example, you'll get the following helpful error message.

Asynchronous operations are not allowed in this context. Page starting an asynchronous operation has to have the Async attribute set to true and an asynchronous operation can only be started on a page prior to PreRenderComplete event.

Basically what ASP.NET is saying is that it's not prepared for you to make an Async call. No problem! ASP.NET has a nifty page directive. Just set Async="True". The MSDN documentation says: "Makes the page an asynchronous handler (that is, it causes the page to use an implementation of IHttpAsyncHandler to process requests)." What does that mean? Well, there are a whole bunch of posts on this, so if you're not familiar, search around for asp.net async page and come back here. Also do a search for "async" in my blog. I've posted about it a lot. It's one of my favorite features in ASP.NET.

So, now you've got the Async page directive down and you think all is good. But then, suddenly, you notice page load times start to increase. Your phone is ringing. Users are complaining. After mere minutes of debugging (after all you're a kung fu debugger right?) you realize your ASP.NET page is waiting for the email to send. "What the heck is going on here? This was an Async call," you mumble under your breathe. You curse Microsoft, and write an angry blog post about it. What happened?

When you set that Async="True" directive on your page you told ASP.NET that you want to do page rendering asynchronously. However, what you didn't realize is that you're doing things asynchronously with regards to the use of threads, and not the serving of the page. Let me clarify. With Async="True" ASP.NET waits for all Async calls to complete before finishing page rendering. It's designed so you can kick off long running IO operations like calling a database, web service, writing files, and sending email, without tying up a valuable worker thread in your ASP.NET threadpool. Instead, the IO operation gets queued up down in unmanaged Windows land and IOCP magic and the shared IO threads kick in. If you truly want to fire-and-forget, and not have your Async calls affect your page load time, here's your answer.

using (new SynchronizationContextSwitcher())
{
var s = new SmtpClient();
s.SendCompleted +=
(sender2, e2) =>
{
//do something when the send is done.
//retry if error, etc.
};

s.SendAsync(from.Text, to.Text, "", message.Text, null);
}

It should be noted that in this sample code when the SendCompleted anonymous method is called, you are no longer in the ASP.NET context. The SynchronizationContextSwitcher removed this context and put you in no context, so you're just free ballin'. This is important. You can't mess with the Request, Page, Response, etc. We're talking serious multi-threading now. In fact it's even likely that delegate will be executing at the same time as some other method in your page's lifecycle, on a whole other thread. So, pass anything you want to use from the page via the last parameter on the SendAsync call, pull it out of the EventArgs in your SendCompleted handler, and don't touch that page object or anything in it.

I must confess. I didn't write this SynchronizationContextSwitcher class. It was another developer on our team (Boris) and then was improved by a random good Samaritan named Richard. It's also based on this one that's quite a bit more featureful/complicated.

Anyway, Simply wrap your send (or any Async) call in a using block like this and, for the scope of that block, any Async operations will happen as if you were not even in ASP.NET and didn't have a Request context to worry about. Your page will be served immediately without waiting for your Async call to complete. Of course, this does have caveats. By doing a true fire and forget there is now the potential your email won't get sent and you won't even know about it. ASP.NET could shut down your app domain 1/2 way through the send and you and the user would be none the wiser. So, care must be taken to either store these things in some other reliable place before the Async call, or (as in our case) usually whatever you're firing off isn't critical, so a few missed ones here and there won't matter.

public class SynchronizationContextSwitcher
: IDisposable
{
private ExecutionContext _executionContext;
private readonly SynchronizationContext _oldContext;
private readonly SynchronizationContext _newContext;

public SynchronizationContextSwitcher()
: this(new SynchronizationContext())
{
}

public SynchronizationContextSwitcher(SynchronizationContext context)
{
_newContext = context;
_executionContext = Thread.CurrentThread.ExecutionContext;
_oldContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(context);
}

public void Dispose()
{
if (null != _executionContext)
{
if (_executionContext != Thread.CurrentThread.ExecutionContext)
throw new InvalidOperationException("Dispose called on wrong thread.");

if (_newContext != SynchronizationContext.Current)
throw new InvalidOperationException("The SynchronizationContext has changed.");

SynchronizationContext.SetSynchronizationContext(_oldContext);
_executionContext = null;
}
}
}

I whipped up a small sample project to demo the effects I talk about here. There are two pages. One that is async, and one that isn't. It demos the error you get if you try to use an Async method on a non-async page, and simulates a slow email server on the async page. Then you can see the fire and forget in action.

Async methods are extremely useful, even if you're not using fire and forget. Most of the samples you see for doing asynchronous ASP.NET pages use the IAsyncResult and Begin*/End* methods. Those are pretty complicated, and if the Async method is available why not use it? I've written about the benefits of async programming quite a lot. Search for "async" up at the top right of the page.

Thursday, July 17, 2008

ASP.NET - It's not just for DataGrids after all

Last night I gave a talk at the San Francisco chapter of the Bay.NET User's group. It was a lot of fun. Thanks for the great interaction everyone! I am also thoroughly impressed that I finished nearly on time and didn't have 10 slides left! Usually I have way too much material for these things.

As promised, here are the slides from the talk.

If any other groups out there are interested in this talk, let me know!

Tuesday, December 11, 2007

Introduction to fbasync: an Asynchronous Facebook Library (Part 1)

I'd like to start off by saying asynchronous programming is easy. Building asynchronous frameworks is hard, but using them is easy. Everybody doing programming in AJAX and Flash do it every day without thinking about it. There's no reason us C# .NET programmers can't do the same!

This first article about fbasync is going to show how to use it. Your 12 year old script kiddie nephew could do it, I promise. Then later on in the series we'll dive into how the library is implemented and see if we can forever scare him away from a programming career.

Those who know me well (or have read my blog for a while) know I'm a proponent of asynchronous patterns. Why? Well, there are a lot of reasons. I've mentioned why 1 2 3 4 5 6 times. To sum it up, the two primary reasons for using asynchronous patterns are to gain better vertical scalability (to do more with one piece of hardware) and provide a better user experience. When it's as easy as writing synchronous code, I say why not? Using asynchronous patterns in your code is especially important when it comes to communicating with a web service outside of your network (like Facebook). When you don't know how long your service calls are going to take, you simply can't expect to scale up an application without going asynchronous!

In a previous article entitled Simpler Isn't Always Better I spent a lot of time bashing the event based asynchronous pattern. Well, I take it back (some of it). It turns out in the context of ASP.NET and Winforms, the event based pattern is very nice indeed. While I implemented both the event based and IAsyncResult pattern in fbasync, I'd recommend most people use the event based interface. This is what we'll be looking at today.

What you've been waiting for

Ok, enough blabbering! The fbasync library is available on CodePlex. You can download the bits right now and follow along at home. The library comes with a simple example that allows you to browse the photo albums uploaded by you and your friends. In this sample we get all the albums and related cover photos of the requested user, and the list of friends for the authenticated user on the server through asynchronous requests. Let's get it setup and take a deeper look!

  1. Make sure you have Visual Studio 2008 installed. It's required.
  2. Download and extract the zip file somewhere interesting.
  3. Open the fbasync.sln file.

Now that you have the web site loaded just make sure your web server is setup to run on port 30912 and the virtual path is "/" (in your project properties). This is the way the sample application is configured in Facebook. If you have your own application (API Key/Secret) you want to test this with, edit the web.config.

Project Setup

With this setup, you'll be able to run the fbasync.web application, add it through Facebook, and experiment with it in the Facebook iframe.

Setting up your page

Getting your Facebook application setup and working is outside the scope of this article. Check out the Facebook Developer site for introduction information. Once you decide you want a Facebook canvas page with an iframe application, return here. :)

It's quite easy to get your page connected to Facebook. Simply inherit from the fbasync.webcomponents.FacebookPage. Once we hit the 1.0 release of the library, you'll also be able to drag a component onto an existing page without messing with your inheritance hierarchy. For now, here's how it looks.


Once you've got your page setup, using the library is simple. The base page takes care of making sure the request is authenticated and gets a Facebook session based on the authentication token provided to your application. Before we go any further, make sure you set the Async directive on your page so fbasync can do things asynchronously. How async pages work was explained well by Fritz Onion in an Extreme ASP.NET article last year.



Now that you have setup the page to process requests asynchronously, fbasync can best do its magic. Override the OnFacebookSessionCreated method in your page, or add a handler for the FacebookSessionCreated event. This will be raised when fbasync has acquired a session from Facebook, using either the authentication token provided in the Facebook iframe or after a redirect through the Facebook login page. The Facebook authentication process is covered in depth in the Facebook Developer Authentication Overview. The good news is, you probably don't need to know! Fbasync will handle the most common situation for you.

Now that you've got a Facebook session, it's time to do something interesting. Let's take a look at the code in our AlbumBrowse page.


There are two interesting bits in this code (they're in giant red boxes – hard to miss). First, we setup event handlers for the *Completed events we are interested in. Second, we call the *Async methods. Notice in this example we're doing three simultaneous asynchronous requests. That's important. These requests will happen simultaneously. That, is the power of asynchronous code in action. Imagine each call takes 250ms to complete. With three calls, that's nearly 750ms of time just waiting around for Facebook. Using the asynchronous pages we end up waiting only as long as it takes for the longest running call to complete!

At this point astute readers will point out that I am in fact lying. In the default configuration of .NET, you will only ever make two simultaneous calls to a given remote IP. This is built-in to the WebRequest class. Lucky for us, we can change it in the web.config like so!

<system.net>
<connectionManagement>
<add address="*" maxconnection="100" />
</connectionManagement>
</system.net>

With this configuration entry in place we can now make up to 100 simultaneous outgoing requests to the same IP address. Much better. Even if you're not using fbasync, you should make a similar change.

Back to the example. Making asynchronous requests is interesting, but requests don't usually do us much good without dealing with the results. The list of friends on the left side of the application is generated using a ListView control and some simple codebehind. I'm going to leave the HTML markup out of it, as its dirt simple. Have a look at the code if interested. Here's the codebehind that renders the user list.


Surprised? There are no red highlights. There's nothing very interesting here. If you haven't seen many lambda functions or LINQ before, the OrderBy and Single methods might look funny, but the code here is pretty darn simple. We remove our event handler (not doing this is prone to memory leaks so I always do it). Then we bind the ListView to the Result on the event argument. And finally we put the friends name who's albums we are viewing in the placeholder. Yup, that's it. No thread locking, no monitor, no mutex, no volatiles, no interlocks, none of that scary threading stuff at all.

Let's review what happened. Our page loaded. The fbasync library got a session from Facebook (or pulled one out of the cache/session – more on this in a future post). Our GetUsersAsync method was kicked off (among others). It finished, in a background thread, and raised the GetUsersCompleted event back in the context of our ASP.NET request. We took the results and bound them to a list view. No muss, no fuss. No threading required. My grandmother could do it – well, if she knew ASP.NET.

This is getting too long already. I'm starting to feel like I'm writing a Scott Guthrie article. Coming soon: we'll wrap up the rest of the example code, and move onto how the *Async methods are implemented. I'd love to get some more contributors on this project. So, if you're interested, give me a buzz or head on over to CodePlex and get crackin! Feedback is welcome and encouraged.

Wednesday, December 5, 2007

Async Facebook Library Teaser

I've been trolling the Facebook Developer Forums recently and talking with other developers. I've also been reading a lot and trying to get a feel for what people might want out of the asynchronous Facebook library I've been (slowly) building. In particular, I've been participating in this thread about Facebook scalability issues. I thought I would share my most recent post as a bit of a teaser as what's to come. Here it is, reproduced:

tomten wrote:

...but it was way overkill for what I've done. ...

This is one myth I want to squash. Async stuff isn't hard. I promise. smile Let's take your page loading example, and make it asynchronous. I put in code comments for a "facebook is hammered" timing. Let's say every call takes you 250ms. That means that page will spend roughly one second waiting for facebook.

Page_Load {
GetSession(); // Call to Facebook 250ms
RenderHtml();
Response.Flush();
Response.Close();
// Make calls to Facebook
GetFriends(); // Call to Facebook 250ms
UpdateProfile(); // Call to Facebook 250ms
SendNotification(); // Call to Facebook 250ms
}

Now, let's make this Async. It would become (roughly -- there is also a "Completed" event handler you'd setup for the items you cared about):

Page_Load {
fbasync.GetSessionAsync();
fbasync.GetFriendsAsync();
fbasync.UpdateProfileAsync();
fbasync.SendNotificationAsync();
}

Page_PreRenderCompelete {
RenderHtml();
Response.Flush();
Response.Close();
}

This time, with the async calls in the server, we spend 0 time waiting for facebook. Just like in client side AJAX, we get a callback when the calls were complete! And, instead of a full second for our page to finish processing, it only takes 250ms, as all the facebook calls happen simultaneously (yeah I know this is a faulty assumption, but it is usually pretty close).

In addition to the shorter page lifecycle (250ms instead of 1 second) we are not tying up a processing thread (of which there is a small finite number). During that time we are waiting for facebook to finish our API calls, the asp.net engine happily processes more requests on the thread that originally kicked off processing of our page.

A shorter lifecycle is important, not only for throughput, but also for memory consumption and garbage collection efficiency. Because the objects do not live for as long, they will be garbage collected more quickly and your application will spend less time doing garbage collection.

Ok, so that's a bit of a spoiler, but, I hope it helps.

Hopefully some time this week I'll clean up the library enough that I think it's worthy to be posted publicly. I spent last night refactoring a bit and removing a lot of "who wrote this crap" code. :)

As a bit of an aside, I was excited to see the team working on the "almsot official" Microsoft .NET library for Facebook released an asynchronous version of the FacebookService. I thought maybe I wouldn't have to write one myself. However, after looking at the code, I was very disappointed by the fake asynchronous support that's in the Facebook Developer Toolkit. It made me shed a tear. I really wish they'd put a disclaimer up: "This AsyncFacebookService is not really asynchronous I/O. It will only make things worse by tying up an additional threadpool thread waiting for the delegate to the synchronous method to return.".

Friday, September 28, 2007

Asyncify Your Code

Asyncify your code. Everybody's doing it. (Chicks|Dudes)'ll dig it. It'll make you cool.

Pretty much everything I build these days is asynchronous in nature. In SoapBox products we are often waiting on some sort of IO to complete. We wait for XMPP data to be sent and received, database queries to complete, log files to be written, DNS servers to respond, .NET to negotiate Tls through a SslStream, and much more. Today I'll be talking about a recent walk down Asynchronous Lane: the AsynchronousProcessGate (if you don't like reading just download the package for source code goodness).

I ran into a problem while working on a new web application for Coversant. I needed to execute an extremely CPU and IO intesive process: creating and digitally signing a self extracting compressed file -- AKA The Package Service. This had to happen in an external process, and it had to scale (this application is publicly available on our consumer facing web site). Here's a basic sequence of the design I came up with:


Do you notice the large holes in the activation times? That's because we're asynchronous! The BeginCreatePackage web service method the page calls exits as soon as the BeginExecute method exits, which is as right when the process starts. That means we're not tying up any threads in our .NET threadpools at any layer of our application during the time a task is executing. That's a Good Thing™.

At this point I'm used to writing highly asynchronous/threaded code. However, I still wouldn't call it easy. Why do it? I'd say there are three main reasons.

  1. To provide a smooth user experience. The last thing a developer wants is for his/her software to appear sluggish. There's nothing worse than opening Windows Explorer and watching your screen turn white (that application is NOT very asynchronous).
  2. To fully and most appropriately utilize the resources of the platform (Runtime/OS/Hardware). To scale vertically, you might call it.
  3. Because it makes you cool. AKA: To bill a lot more on consulting engagements.

Microsoft recommends two asynchronous design patterns for .NET developers exposing Asynchronous interfaces. These can be found on various classes throughout the framework. The Event Based pattern comes highly recommended from Microsoft and can be found all over new components they build (like the BackgroundWorker). Personally I think the event based pattern is overrated. The hassle of managing events and not knowing if the completed event will even fire typically steers me away from this one. However, it is certainly easier for those who are new to the asynchronous world. This pattern is also quite useful in many situations in Windows Forms and ASP.NET applications, leaving the responsibility of the thread switching to the asynchronous implementation (the events are supposed to be called in the thread/context that made the Async request -- determined by the AsyncOperationsManager). If you've ever used the ISynchronizeInvoke interface on a Winforms Control or manually done Async ASP.NET Pages you can really appreciate the ease of use of this new pattern...

The second recommended pattern, and usually my preference, is called the IAsyncResult pattern. IAsyncResult and I have a very serious love/hate relationship. I've spent many days with my IM status reading "Busy - Asyncifying" due to this one. But, in the end, it produces a simple interface for performing asynchronous operations and a callback when the operation is complete (or maybe timed out or canceled). Typically you'll find IAsyncResult interfaces on the more "hard core" areas of the framework exposing operations such as Sockets, File IO, and streams in general. This is the pattern I used for the Asynchronous Process Gate in the Package Service.

The Package Service has a user interface (an AJAXified asynchronous ASP.NET 2.0 page) which calls an asynchronous web service. The web service calls another asynchronous class which wraps a few asynchronous operations through the AsynchronousProcessGate and other async methods (i.e. to register a new user account) and exposes a single IAsyncResult interface to the web service.

Confused yet? Read that last paragraph again and re-look at the sequence. In order to make this whole thing scale it had to be asynchronous or we'd be buying a whole rack of servers to support even a modest load. Also because of the nature of the asynchronous operation (high cpu/disk IO) it had to be configurably queued/throttled. I went through a few possible designs on paper. But in the end I chose to push it down as far as possible. The AsynchronousProcessGate, quite simply, only allows a set number of processes to execute simultaneously, the number of CPU's reported by System.Environment.ProcessorCount by default. It does this by exposing the IAsyncResult pattern for familiar consumption. The piece of magic used internally is something we came up with after writing a lot of asynchronous code: LazyAsyncResult<T>.

LazyAsyncResult<T> provides a generic implementation of IAsyncResult. It manages your state, your caller's state, and the completion events. It also uses Joe Duffy's LazyInit stuff for better performance (initializing the WaitHandle is relatively expensive and usually not needed).

Using the asynchronous process gate is straight forward if you're used to the Begin/End IAsyncResult pattern. You create an instance of the class, and call BeginExecuteProcess with your ProcessStartInfo. When the process is complete you will get your AsyncCallback, or you can also wait on the IAsyncResult.WaitHandle that is returned from BeginExecuteProcess. You then call EndExecuteProcess and the instance of Process that was used is returned. If an exception occurred asynchronously, it will be thrown when you call EndExecuteProcess.

The Begin Code:
static void StartProcesses()
{
AsynchronousProcessGate g = new AsynchronousProcessGate();
while (!_shutdown)
{
//keep twice as many queued as we have cpu's.
//for a real, CPU or IO intensive, operation
//you shouldn't do any throttling before the gate.
//that's what the gate is for!
if (g.PendingCount < g.AllowedInstances * 2)
g.BeginExecuteProcess(
new ProcessStartInfo("notepad.exe"),
10000,
ProcessCompleted,
g);
else
System.Threading.Thread.Sleep(100);
}
}
The End Code:
static void ProcessCompleted(IAsyncResult ar)
{
try
{
AsynchronousProcessGate g =
(AsynchronousProcessGate)ar.AsyncState;

using (Process p = g.EndExecuteProcess(ar))
Console.WriteLine("Exited with code: " +
p.ExitCode + ". " +
g.PendingCount + " notepads pending.");
}
catch (Exception ex)
{
Console.WriteLine("("
+ ex.GetType().ToString()
+ ") - " ex.Message);
}
}

Phew! After all that, the end result for SoapBox: a single self extracting digitally signed file someone can download. Oh, and a simple library you can use as an Asynchronous Process Gate! Enjoy. Look, another download link so you don't even have to scroll back up. How nice am I?

Friday, June 8, 2007

Simpler isn't always better - AsyncOperationsManager

AsyncOperationManager... die!

We have a new invitation system coming out soon for SoapBox Communicator and I was doing a little premature optimization, err, I mean, capacity planning. :) I set about to perform the simple task (or so I thought) of sending all of our email based invitations asynchronously. The architecture of the system is pretty simple. We have an ASP.NET 2.0 web site that you use inside SoapBox for sending invitations to your friends. The web site talks to an internal web service. The web service talks to a database. It's a pretty classic nTier design, with one exception. All layers are asynchronous.

The UI uses asynchronous ASP.NET pages and RegisterAsyncTask to make the call to the EnqueueEmailInvites web service. The web service uses asynchronous web methods and reads from/writes to our database using the asynchronous data methods in SqlClient.

Async Web Method

[WebMethod]
public IAsyncResult BeginEnqueueEmailInvites(EmailInvitation invite, AsyncCallback callback, object state)
{
return InvitationFactory.BeginEnqueueEmailInvites(invite, callback, state);
}

[WebMethod]
public string[] EndEnqueueEmailInvites(IAsyncResult ar)
{
return InvitationFactory.EndEnqueueEmailInvites(ar);
}
Factory Calling DAL

public static IAsyncResult BeginEnqueueEmailInvites(EmailInvitation invite,

AsyncCallback callback, object state)
{
InviteEmailAsyncState myAs = new InviteEmailAsyncState();
myAs.Invitation = invite;
InviteEmailAsyncResult myAR = new InviteEmailAsyncResult(myAs, callback, state);

int emailInviteId = invite.Mutual ? GetInvitationTypeId(Constants.InviteTypeEmailMutual) :

GetInvitationTypeId(Constants.InviteTypeEmail);
string csvAddresses = string.Join(",", new List<string>(invite.Addresses).ToArray());

Data.Invitations.BeginCreateReadInvitation(emailInviteId, invite.FromJid, invite.FromName, invite.Subject, invite.UserBodyPlain, string.Empty, csvAddresses, EmailInvitationCreatedCallback, myAR);

return myAR;
}

I write a lot of asynchronous code. A lot. I'm a bit of an asynchronous I/O zealot. In fact, I've had an article on the shelf called "Asyncify your code" for months now. l I just haven't got the sample code written for it yet. Anyway, my experience has led me to a simple conclusion: I hate the AsyncOperationManager.

Maybe it's because I write a lot of asynchronous code, or maybe just because people tend to Mistake Familiarity with Superiority, but whatever the reasoning, I can't stand it. The AsyncOperationManager is used all over the .NET 2.0 framework where you see calls ending in "Async", even though you probably don't even know it. One such instance that rubbed me the wrong way yesterday (and into this morning) is the SmtpClient. Don't get me wrong, the SmtpClient is a great improvement over the old CDO based classes, but the Async support leaves something to be desired.

First off, you can't call Send or SendAsync until the previous operation has completed. That means if you're sending out more than one email in succession you're going to need to do your own queueing. So I said to myself "Ok, this is easy, I'll rip out the loop, register an eventhandler for SendCompleted, and pass a Queue through as state to SendAsync." This led me to the next problem.

The asynchronous web method called the asynchronous data method, which led to a callback. Inside of this callback I grabbed some info from the data reader result and composed the MailMessage. I created a SmtpClient, registered for the SendCompleted event kicked off the first SendAsync, and completed the asynchronus web method. The mail sent great, but my SendCompleted event handler was never called.

Apparently, if you make an async call there is the potential that the completed event handler you registered will never get called. Yep. You can call SendAsync, and the SendCompleted event will never be raised. I admit, the circumstances this happened in were kind of abnormal, but still. That just shouldn't happen.

After a little reflectoring it was apparent this had something to do with the SynchronizationContext at the time of the call used by the AsyncOperationManager inside of the SmtpClient. I got a litte cross-eyed looking through the code in reflector so I gave up my hunt for the exact answer. My workaround: Call ThreadPool.QueueUserWorkItem and create the SmtpClient and call SendAsync inside of that callback. This gets you the generic SynchronizationContext that doesn't have anything weird associated with it, just plain ole async delegates. If someone has a better solution, please let me know.

The Hack

if (messagesToSend.Count > 0)
{
//this seems strange, but we queue up the creation of the SmtpClient to a threadpool thread.
//the synchronization context seems to become invalid when we complete the async web method call
//and we don't want to wait for the emails to go out to complete the web service call.

System.Threading.ThreadPool.QueueUserWorkItem(StartSendingEmailCallback, new object[] { myAr.MyAsyncState.Invitation, messagesToSend });
}

I have a plea to all framework designers out there. Have the decency to give me the good ole IAsyncResult based async pattern without all that extra AsyncOperationManager baggage! I don't want to have to worry about all that. I'm a big boy. I can handle my own synchronization, and I like it that way.

Monday, June 26, 2006

How to Build Scalable .NET Server Applications: Memory Management

I'll get this out of the way from the start. This series of blogs will have nothing to do with ASP.NET or web services. However, if you plan on writing you own implementation of IIS in managed code this would probably be a good place to start. :) I also won't be providing very many code examples, as I'd be flogged by our intellectual property lawyers. You will not be able to copy and paste and create your own scalable server. However, I hope to provide enough insight so you can avoid a big list of gotchas we have had to figure out the hard way. This is one piece of a huge puzzle, memory management. Yes, you do have to think about that in .NET, at least if you want to build a large scale application.

For those who don't already know, SoapBox Server is a part of our SoapBox Collaboration Platform that supports the XMPP protocol as well most of the interesting JEP extensions. At the core of SoapBox Server is a highly efficient Socket server and thread machine capable of scaling into the hundreds of thousands of simultaneous users, and it's built 100% on .NET (C# now, but used to be VB).

SoapBox Server is the first multithreaded Socket based server application I've had the pleasure of working on. During the course of building the SoapBox Server into the extremely scalable and reliable system it is today I've learned a few things (as has the rest of the team, I hope). Thanks to Chris (who already had tons of experience with such things in Win32/C++), a few bloggers out there, some books, customers finding very interesting bugs, Windbg with Son of Strike, oh and Starbucks, I'd say I'm pretty well versed in the land of building scalable server applications. I'm no Jeff Richter, mind you, but I feel I have now learned enough to at least speak intelligently about it.

In that spirit I'd like to share the fruits of our tuning and debugging work, which, if history repeats itself, will continue to evolve as we begin work on our next major revision of the product. First, I'd like to repeat something I said a couple paragraphs ago, SoapBox now scales to hundreds of thousands of simultaneous connections with a single piece of server hardware. Think about that for a second. A user brings up an IM client, connects to SoapBox Server, and then holds that connection open until they Log Out. Repeat hundreds of thousands of times. This is no simple task. The .NET CLR does not provide a magic "Process.Scalable = true" property. We have invested hundreds of hours into tuning (maybe thousands) over the life of the server on classes of hardware varying from single processor laptops to 16-way Itanium2 systems with 64GB RAM. We've been through four distinct processing models as well as quite a few iterative improvements on our Socket interaction layer. Basically we have ran the server under a bunch of different profilers under many scenarios, found slow bits of code, and fixed them. But I'm not going to talk about profiling and performance tuning; perhaps another time. I'm going to talk about memory and scalable applications.

Every time your application creates a new Socket, Windows pulls memory from it's Nonpaged Kernel memory, which is simply physical memory that is reserved by the kernel and will never be paged out to disk. This block of memory has a finite limit and the kernel picks the limit based on the amount of phsyical RAM available to it. I don't know the exact algorithm, but with 4GB RAM it's usually somewhere around 150,000 TCP Socket connections, give or take. Want to see this in action? Simply create a loop that instantiates sockets. It will stop working eventually with a SocketException telling you there isn't enough buffer space. On top of this hard kernel level limitation, you also have to worry about how much memory each concurrent connection uses in your own application. In SoapBox we store a lot of information about each connection in memory in order to improve performance and decrease our IO operations. This includes things like the user's contact list, their last presence (available, away, busy, etc), authorization information, culture information, user directory information, etc. If we didn't hold this in memory we'd have to hit a file, database, or some other out of process persistent store for the information every time we needed it. Being IO bound is no fun. Believe me, we started out that way.

However, because of our extensive caching, SoapBox Server 2005 can only reliably handle about 20,000 simultaneous connections on the beefiest of 32 bit hardware (on 64 bit it's much, much, much higher -- I also have to admit we haven't stress tested the 2007 build on 32 bit hardware, it would probably be much higher now). It doesn't matter if you have 64GB RAM and 16 32bit processors, it we can still only handle 20,000 connections. Why, you ask? Well, it's because of the 2GB (well, really 3GB with a boot.ini switch) virtual memory limit per process in 32bit Windows. Without delving into managing your own memory your process is only allowed up to 3GB to play with. Typically, we use that up, or rather, .NET thinks we use it up, somewhere between 20,000 and 30,000 connections. Now why would I say ".NET thinks we use it up?" Story time!

A little over a year ago one of our customers kept running into a very bad situation. As evidenced by the Event Log, SoapBox Server was crashing (insert shock and awe here). It was an irregular occurance, but it did happen. However, we did no take this lightly. This customer was running about 2,500 simultaneous connections on a Dual Xeon with Hyperthreading and 4GB ram and the /3GB switch set. It was plenty of hardware for the job, and probably overkill. However, the service was still crashing. We set them up with the Debugging Tools For Windows and had them startup the process to wait for a crash (another blog we'll have to write some day). After a few tries we got a dump with some useful information in it. The result? We were out of memory, sort of.

In .NET when you call any socket operation and pass it a buffer, whether it be a send or receive, synchronous or asyncronous, it takes that buffer and pins it before giving it to the Winsock API's. Pinning, in a nutshell, is taking a .NET data structure and telling the .NET CLR memory manager not to move it, until it is explicitly un-pinned. The memory manager in the CLR is smart. As you allocate and deallocate memory it is constantly defragmenting it for you so the overall memory footprint is lower. There are quite a few really good/long/complicated articles on how this works so I won't bore you. However, pinning throws a wrench in this and the memory manager isn't quite smart enough to deal with it well (though it has gotten a lot better in 2.0). Basically, that buffer you want to put on the socket cannot move in memory (physically -- in terms of you virtual memory space) from the time the socket IO operation begins until it ends. If you look at the Winsock2 API's this is obvious, since the buffer is passed as a pointer. Anybody who's built this type of application in Winsock2 is probably saying "DUH!". I'd consider this a very leaky abstraction. Due to this behavior, it is quite easy to write a socket application in .NET that runs out of memory.

Back to the story! Not only were we out of memory, but the there was only about 200MB worth of data structures in the heap. For those of you like me that use calc.exe for all your basic math let me figure that out for you, 200MB > 3GB. Uhh, say what? How the heck were we out of memory? Well, we ran into the shortfall of pinning and memory fragmentation. The cause of this was a small number of small pinned buffers, in our case 2KB each, that were high enough in the heap to cause fragmentation spanning over 2.8GB. Where did the other 2.8GB go, you ask? Well, is was there, allocated by our process, but not being used by our code. In Son Of Strike (SoS -- a command line plug-in to the Windbg debugging tool I hope you never have to use) this showed up as free, empty, unused space! It was just sitting there waiting to be used, but we still ran out of memory. I think I mentioned earlier the memory manager in .NET isn't so smart when it comes to fragmented memory and pinning, well, this is what happens in the worst case.

Good thing for you, the answer to all your memory fragmentation and pinning woes is quite simple. Pre-allocate buffers for use by anything that will be causing pinning, and do it early on before there is a lot of memory thrash (when your application is rapidly allocating and deallocating a lot of memory). We created a simple class called a BufferPool that we use to pre-allocate a certain number of buffers. This pool can grow as need be, but it does so in large chunks and forces a garbage collection each time before the buffers are actually used. This considerably reduces the chances of fragmentation caused by pinned memory. If the pool starts off with 500 buffers, but then the 501st buffer is needed it will grow by a configurable value, typically another 500 buffers, and the induced garbage collection will cause these buffers to shift to the lowest possible point on the heap.

Interestingly enough when we found this bug we already knew about the pinning behavior of socket operations, but had only solved half of it. All of our BeginReceive calls were using the BufferPool because we knew the buffers would remain pinned until we received data from a client, but the BeginSend calls were not using the pool. We had not even considered the fact that sending a few KB of data might take long enough to pin memory, fragment the heap, and cause an OutOfMemoryException. But there is one case where they do, timeouts. The Windows TCP subsystem is very forgiving. If a client loses its connection and the server isn't explicitly told about it, the next piece of data you try to send to that client socket will end up being pinned while the TCP subsystem waits for the client to respond. It can take up to 5 minutes with the default configuration of Windows for the TCP subsystem to figure out the client isn't really there. During that entire time your buffer is pinned in memory. *poof* OutOfMemoryException.

Unfortunately, pre-allocating buffers does not completely fix the issue of running out of memory. There are also some other limits to the size of a .NET process's virtual memory space that are very complicated and I won't talk about, but basically you end up with anywhere from 1/2 to 2/3 usable virtual memory without running the risk of OutOfMemoryException. So, if you have 2GB virtual memory available (standard on a 32bit machine), you end up with about 1.3GB you can actually use reliably. Of course, this varies, and some applications will be able to use more, or maybe less. Your mileage may vary.

Don't fret, all of the issues I've talked about in here have been fixed since SoapBox Server 2005 SR1. And with the most common usage patterns people were not actually affected to begin with.

I hope this was at least marginally interesting to someone. :) Next up, I'll probably talk about limitations we discovered in the Windows Socket infrastructure, or maybe async IO, IOCP, and worker threadpools, or maybe how in the world we actually test at this scale. Only time will tell, unless Chris beats me to it.

About the Author

JD Conley is an entrepreneur and hacker, currently working away his golden handcuffs at Playdom, a subsidiary of the Walt Disney Company, since Hive7 was acquired. We make social games. The views and opinions expressed on this post are his and do not necessarily represent or reflect those of The Walt Disney Company.