Functional Optimistic Concurrency in C#


A few months ago Phil Haack wrote about how C# 3.0 is a gateway drug to functional programming. (Yeah, that's how long ago I started writing this blog.) I couldn't agree more. I find myself solving problems using functional rather than imperative programming quite often nowadays. It's much more elegant for many problem spaces.

Before we go any further, here's the sample app used for this article. Even if you don't like my writing, you should play with it. Yeah, you! optimistic-concurrency.zip

One problem space that fits very well with functional patterns is in developing apps that have to use optimistic concurrency to maintain data consistency at scale. Here at Hive7 we build PvP games. In such games, multiple people and background processes are often affecting the same entity at the same time. We can't use coarse grained locks or high isolation levels in MS-SQL, or the whole game would come to a halt. Here's a common scenario in a game like Knighthood:

Multiple rival lords are attacking my Kingdom at once trying to steal my most prized vassal, my wife! My wall is staffed with a heavy defense, and my hospital has a strong set of medics healing my kingdom over time. But to keep a handle on the attack I also have to continuously spend gold to heal my defensive army.

In this common use case there are a number of subtleties. First, multiple people are attacking me at once. That means they're doing damage to my defenses in real time, and at the same time. My hospital is healing my vassals over time. This occurs in a background process once every few minutes. And I'm triggering an instant heal to my defensive vassals using my gold supply. My Marketplace is also generating gold for me over time in another background process. To top it all off, this is happening across a cluster of application servers that are certain to be processing multiple requests simultaneously. Phew!

So what does all that mean? Well, basically, there are a lot of possibilities for change conflicts. And we have to deal with those conflicts to both keep a consistent data model and perform well.

There are a a number of potential strategies for managing these change conflicts in the persistent store – a few beefy Microsoft SQL Server databases in our case. We chose to go with optimistic concurrency and an abort on conflict transaction strategy. That basically means when we write data to the database we make sure we are always writing the most recent version of a row. If an application attempts to write an old version of the row, the data access layer throws an exception and aborts the transaction. Knighthood uses NHibernate so the validation is done for us automatically using a simple version number on the row. The basic algorithm is:

  1. Read data and serialize into objects (done by NHibernate)
  2. Modify objects in code
  3. Tell NHibernate to persist the changes, which does the following
    1. Increments the version number
    2. Finds all the changes and batches up insert/update calls
    3. Uses the version number in the WHERE clause of updates like: "UPDATE Table SET Col1='blah' WHERE Version=36"
    4. Checks the rows modified reported by SQL server and throws an exception if it's an unexpected number


As you can imagine, this fails regularly in a high concurrency scenario, but it succeeds orders of magnitude more often than not. It's also pretty standard for any web app nowadays.

The only problem is, to preserve consistency, an exception is thrown and the transaction is aborted when change conflicts occur. That means whatever request the application or user issued fails. We could show the user a friendly error message, but that would be a frustrating experience. Nobody likes seeing errors for non-obvious reasons. And in the case of headless software running in the background the error would just be in a log somewhere. If it's something important that needs to happen, then we have to make sure it gets done! So us imperative programmers devise a retry scheme and write a loop with an exception trap around our code. Maybe you get clever and create a class that does this which raises an event any time you need to execute your retry-able code. But, this gets pretty cumbersome. Enter functional programming!

We have a little class named DataActions that is used to simplify and consolidate this retry process and make it painless to use. I'm going to use LINQ to SQL as the example here. Here's some usage code:

DataActions.ExecuteOptimisticSubmitChanges<GameDataContext>(
    dc =>
    {
        var playerToMod = dc.Players.Where(p => p.ID == playerId).Single();
        SetRandomGold(playerToMod);
    });


As you can see it's really straight forward. Notice all the goodness going on there. We don't have to instantiate our own DataContext, manually submit the changes, or worry at all about transactions. It's all handled by the wrapper. And, you just have to provide some code to execute once the DataContext has been instantiated.

The ExecuteOptimisticSubmitChanges helper method itself is pretty simple as well:

public static void 
ExecuteOptimisticSubmitChanges<TDataContext>(Action<TDataContext> action)
    where TDataContext : DataContext, new()
{
    Retry(() =>
        {
            using (var ts = new TransactionScope())
            {
                using (var dc = new TDataContext())
                {
                    action(dc);
                    dc.SubmitChanges();
                    ts.Complete();
                }
            }
        });
}


And, finally, we have the Retry method:

public static void Retry(Action a)
{
    const int retries = 5;
    for (int i = 0; i < retries; i++)
    {
        try
        {
            a();
            break;
        }
        catch
        {
            if (i == retries - 1) throw;

            //exponential/random retry back-off.
            var rand = new Random(Guid.NewGuid().GetHashCode());
            int nextTry = rand.Next(
              (int)Math.Pow(i, 2), (int)Math.Pow(i + 1, 2) + 1);

            Thread.Sleep(nextTry);
        }
    }
}


When you string all this together you get pseudo-stacks that look like:

MyCode
  ExecuteOptimisticSubmitChanges
    Retry
      ExecuteOptimisticSubmitChanges
        MyCode

So, why should you care? The calling code is really easy to read, and you get a number of other benefits with this code. In addition to handling exceptions caused by concurrency errors, you also get retries on deadlocks, and more common Sql Connection errors.

I put together a little sample application you can play with. It uses these helpers and has a SQL Database with it. The sample simulates really high concurrency and you can watch it deal gracefully with deadlocks. Then you can change line 29 of Program.cs and execute the same concurrent code without retries enabled. It ouputs the number of failed transactions and a bunch of other interesting stuff to the console. Here's some example output:


...

Retrying after iteration 0 in 1ms
Retrying after iteration 0 in 0ms
Thread finished with 0 failures. Concurrency at 3
Retrying after iteration 1 in 3ms
Retrying after iteration 1 in 4ms
Thread finished with 0 failures. Concurrency at 2
Retrying after iteration 2 in 5ms
Thread finished with 0 failures. Concurrency at 1
Retrying after iteration 3 in 15ms
Thread finished with 0 failures. Concurrency at 0

0 total failures and 7 total retries.
All done. Hit enter to exit.

And the same test run with retries disabled:


...

Starting worker. Concurrency at 8
Thread finished with 0 failures. Concurrency at 7
Thread finished with 0 failures. Concurrency at 6
Thread finished with 1 failures. Concurrency at 5
Thread finished with 1 failures. Concurrency at 4
Thread finished with 1 failures. Concurrency at 2
Thread finished with 2 failures. Concurrency at 3
Thread finished with 0 failures. Concurrency at 1
Thread finished with 2 failures. Concurrency at 0

7 total failures and 0 total retries.
All done. Hit enter to exit.

Here's the download link again: optimistic-concurrency.zip

Let me know if you have any questions.

author: JD Conley | posted @ Friday, June 26, 2009 1:35 PM | Feedback (5)

About the author: JD Conley is the Chief Software Architect at Hive7, a Silicon Valley startup that makes massively multiplayer web games. Our top title, Knighthood, has over 4 million players and we are always working on cool new web games! Interested in making games with .NET? Drop me a line at jdc at hive7 dot com.

kick it on DotNetKicks.com

A new basket for my eggs


Hopefully after reading that title you're thinking of the old adage "Don't put all your eggs in one basket" and not something crude. Ok, I admit, either way it works for me. You're still reading.

Let me preface by saying I love what's going on at Hive7, but a guy's gotta have a side project. In fact I wrote about this phenomenon a while back. And, in my mind, that side project might as well make me some lunch money.

For the last two years or so I've been really interested in digital photos and the untapped markets that lie within. In fact, I got introduced to Hive7 while trying to sell myself to an investor to get some angel funding in the space. I'm not a pro photographer wannabe or anything like that. I just think digital photos are a great medium for sharing life with friends and family. I have built Friend Photosaver for Facebook (a screen saver using Facebook photos), Photo Feeds for Facebook (automatic photo RSS creator for Facebook), and Photozap (a tool to download Facebook photos as a zip file).

Those applications are all pretty cool, but didn't really strike me (or anyone else) as especially compelling. But, they did lead me down the path of building something that I think is pretty interesting.

Everyone has a digital camera or cell phone camera. When you go to a social gathering of any sort there are usually tens to hundreds of photos taken. Think of weddings, birthdays, graduations, family bbq's, night clubs. . . . What happens to these photos? Someone copies them to their computer, or uploads them to a photo sharing web site. They send out links or maybe share the photos through a social network's tagging or posting features or some such. That's all fine and good, but I think there's more to be had.

Enter pixur.me. Quoting the about page:

Pixur.me is a different kind of online photo sharing service. Our mission is to focus on the person receiving photos, rather than the one taking them. There are a lot of great services where you can organize your own photos and share them with people, but we think that's only half of the equation.

Can you find all the cute pictures of your kids from your last family vacation? Or how about all the photos from your wedding that your guests took? Could your mother find those same photos?

You could if your family was using pixur.me! What if all the photos that everyone took at that last vacation or your wedding were in one spot? Even though Aunt Sue uses Flickr, and you use Facebook, and your mother uses Picasa. That's pixur.me. Create a Stream and see for yourself! Once your stream is created anyone can add photos to it, regardless of where they are stored online.

That's it. Another basket awaiting some eggs. Give it a spin and let me know what you think. Of course, it's not very interesting if you just use it by yourself. Create a stream and give out the link at your next gathering. Or maybe start a stream that your extended family can add photos to so grandma can see them all in one spot.

Oh yeah, I almost forgot this is a technical blog. This project started out as a technology experiment so it's built on Windows Azure and ASP.NET MVC. Very cool stuff. I'll have to write more about them later...

author: JD Conley | posted @ Tuesday, June 02, 2009 11:46 PM | Feedback (0)

About the author: JD Conley is the Chief Software Architect at Hive7, a Silicon Valley startup that makes massively multiplayer web games. Our top title, Knighthood, has over 4 million players and we are always working on cool new web games! Interested in making games with .NET? Drop me a line at jdc at hive7 dot com.

kick it on DotNetKicks.com

ioDrive, Changing the Way You Code


Introduction

In my lifetime there have been very few technologies that have created a paradigm shift in the software industry – I was born just after the spinning magnetic hard drive was created. Off the top of my head I can think of: the Internet (thanks Al!), optical disks, Windows, and parallel computing. From each of these technologies entirely new software industries were born and development methodology drastically changed. We're at the beginning of another such change, this time in data storage.

Contents



Random Story for Context

At Hive7 we make web based social games for platforms like Facebook and Myspace. We're a tiny startup, but producing a successful game on these platforms means we're writing code to deal with millions of monthly users, and thousands of simultaneous users pounding away at our games. Because our games are web based they're basically written like you'd write any other web application. They're stateless, with multiple RDBMS back end servers for most of the data storage. Game state is pretty small so we don't really store that much data per user. We don't have Google sized problems to solve or anything. Our main problem is with speed.

When you're surfing the web you want it to be fast but can live with a page taking a few second to load here and there. When you're playing a game, on the other hand, you want instant gratification. A full second is just way too long to wait to see the results of your action. Your character's life might be on the line!

To accomplish this speed in our games we currently buy high end commodity hardware for our database servers, and have a huge cluster of memcached that we tap into. It works. But, properly implementing caching is complex. And those DB servers are big 3U power hungry monsters! Here's a typical disk configuration of one of our DB servers:



Each of those drives are 15k RPM 72 GB SAS (or whatever the fastest is at the time of build). And the RAID controllers are very high end with loads of cache. And here's the kicker! We can only use about 25% of the capacity of these arrays before the database write load gets too high and performance starts to suffer. They cost us about $10k a piece. Sure, there are much more complex architectures we could use to gain performance. Or we could spend a few hundred grand and pick up a good SAN of some sort. Or we could drop some coin for Samsung SSD's. But, those options are a bit out of the price we want to spend for our hardware, not to mention the necessary rack space and power requirements.

Enter the ioDrive. With read/write speeds that are very close to the 24 SSD monster that Samsung recently touted, at a way lower price, I have a hard time imagining choosing the 24 drive option. Maybe if you had massive storage requirements, but for pure performance you can't beat the ioDrive price/performance ratio right now. I don't remember if I'm allowed to comment on pricing, but you can contact a sales rep at Fusion-io for more info.

Last month we picked up one of these bad boys for testing. In summary, "WOW!" I spent a few hours this week putting the ioDrive through the ringer and comparing it to a couple different disk configurations in our datacenter. My main goal was to see if this is a viable option to help us consolidate databases and/or speed up existing servers.

The Configuration

ioDrive System (my workstation)

  • Windows Server 2008 x64 Standard Edition
  • 4 CPU Cores
  • 6 GB Ram
  • 80 GB ioDrive
  • Log and Data files on same drive

Fast Disk System

  • Windows Server 2008 x64 Standard Edition
  • 8 CPU Cores
  • 8 GB Ram
  • 16 15k RPM 72 GB SAS Drives (visualization above)
  • Log and Data files on different arrays

Big and Slow Disk System

  • Windows Server 2008 x64 Standard Edition
  • 4 CPU Cores
  • 8 GB Ram
  • 12 7200 RPM 500 GB SATA Drives
  • Log and Data files on different arrays
Test Configuration

For this test I used SQLIOSim with two five minute test runs. We were really only interested in simulating database workloads. If you want a more comprehensive set of tests check out Tom's Hardware. I should also mention that this was obviously not a test of equals. Both disk based systems have a clear RAM advantage and the fast disk system has a clear CPU advantage. The hardware chipsets and CPU's are also slightly different, but they're the same generation of Intel chips. In any case, when you see the results you'll see how this had a negligible effect. We're talking orders of magnitude differences in performance here...

I ran two different configurations through SQLIOSim. One was the "Default" configuration that ships with the tool. It represents a pretty typical load on a SQL Server disk system for a general use SQL server. The other was one I created called "Write Heavy Memory Constrained". The write heavy one was designed to simulate the usage in a typical game, where, due to caching, we have way more writes than reads to a database. Also, the write heavy one is much more parallel. It uses 100 simulated simultaneous random access users where the default one has only 8. And, with the write heavy one there is no chance the entire data set can be cached in memory. It puts a serious strain on the disk subsystem.

I took the output from SQLIOSim and imported it into Excel to do some analysis. I was primarily concerned with two metrics: IO Duration and IO Operation count. These two things tell me all I need to know. First, how long does it take the device to perform IO on average, and how many can it get done in the given time period.

Test Results

Write Heavy Memory Constrained Workload
MetricioDriveSlow DisksFast Disks
Total IO Operations 10,625,381 1,309,673 3,260,725
Total IO Time (ms) 17,625,337 1,730,147,612 356,839,912
Cumulative Avg IO Duration (ms) 1.66 1,321.05 109.44




Wow, 100x faster IO's on average!



Over 20x less time spent doing IO operations!



And over 3x more operations performed. This would have been way higher, but the ioDrive system was CPU constrained, taking 100% CPU. Looks like we'll be loading up at least 8 cores in any database servers we build with these cards!

Default Workload
MetricioDriveSlow DisksFast Disks
Total IO Operations 690,753 287,180 456,300
Total IO Time (ms) 3,616,903 231,859,576 93,991,055
Cumulative Avg IO Duration (ms) 5.24 807.37 205.99




40x faster on average in this workload! Looks like the bulk operations and larger IO's present in this workload narrowed the gap a bit.



This time, a little under 30x less time spent doing IO operations!



Only 1.5x more total operations this round. This time we weren't CPU constrained, and I didn't take the time to dig in to the "why" on this one. Based on the raw data I would guess this is caused by IO blocking a lot more often for ioDrive than the fast RAID system. This probably has to do with the caching system in the RAID cards under this mixed write workload. You'll notice if you look at the raw report, that the ioDrive has no read or write cache at the device level. It doesn't really need it.

In case you want to see the raw data or the SQLIOSim configuration files, you can download the package here: ioDrive Test Results

Conclusion

Wow! ioDrive is going to be scary fast in a database server, especially when it comes to tiny random write IO's, parallelism, and memory constraints. I think we'll be seeing a lot of new interesting software development and system architectures due to this type of technology. The industry is changing. You no longer need either tons of cache (or cash) or tons of RAM to get great performance out of your data store. We're talking 100x better performance than our fast commodity arrays. I think it's safe to say we'll be using these devices in production in the near future. Since this device is currently plugged into my workstation, maybe I'll post another review about how it's improving my development productivity so you can convince your boss to buy you one. :)

author: JD Conley | posted @ Thursday, March 12, 2009 12:30 PM | Feedback (20)

About the author: JD Conley is the Chief Software Architect at Hive7, a Silicon Valley startup that makes massively multiplayer web games. Our top title, Knighthood, has over 4 million players and we are always working on cool new web games! Interested in making games with .NET? Drop me a line at jdc at hive7 dot com.

kick it on DotNetKicks.com

You might be a great hacker if you...


A number of years ago I was doing some mentoring at a California state agency that shall remain nameless. I got my butt up in time to be into their office at 8am. (Ok, I'll be honest, usually I got up in time. I was late on a few occasions.) I led them down the path of learning ASP.NET from scratch. Together we built a great product that is still in use today on a highly trafficked web site. Some time late in the mentoring project a student came up to me and asked the strangest question. He wanted to know how I learned everything I was teaching them. He wanted to take the same classes.



The guy was an amazing engineer. He was methodical, had great documentation, dotted all his i's and crossed all this t's. But he wasn't a great hacker. He was a bit slow, and didn't have much creativity. It was around that time I started paying more attention to traits of great hackers, before Paul coined the term. The bastard! At that time I was really just looking for people that learned quickly and could get things done faster than the rest. Some day maybe I'll figure out how to be all witty and important and coin terms.

So, here we go, in no particular order – except the first one, which is the obvious transition from my enticing story above:

  • started off as a script kiddie

    A typical scenario looks something like the following, though could occur in any area (not just video games):

    1. Play Quake 2
    2. Get stupidly good at Quake 2
    3. Get bored with Quake 2
    4. Figure out how to cheat by writing scripts to rocket jump or speed hack
    5. Realize what you just did was coding, and it's fun and amazingly rewarding
    6. Change all direction in life (yes, even when you're 12 years old) so you can do more coding
  • often forget to shave

    "Wait a minute, you mean people do their own laundry?" Yes, you are exceptionally lazy. That's ok. You have more important things to do in life than worry about how you smell/look.

  • have ever worked on a project for 24 hours straight

    "Hold on! You don't work without sleep until a problem is solved?" And no, last minute procrastination doesn't count, and neither do production outages. Everyone's been there. I'm talking about all night sessions working to solve a problem that could have been done over a few normal working days.

  • instantly quiet a room when you speak

    You don't talk much. You spend most of your time listening to others. You find idle chit chat to be boring and have quantitatively determined it's a waste of time. Maybe it's because you don't have much to say. But, what I think is more likely is that you only say things that are relevant and important. Thus, when you speak, the room listens. A room full of strangers doesn't count. They won't know you only say important things until you've trained them that way.

  • have ever gone to visit a friend and proceeded to ignore them because you must finish that stupid puzzle they had on their coffee table before putting it down

    See photo at top of post.

  • get asked to help debug other people's code

    There's a certain amount of pride a developer has over their code. No matter how logical it is to call someone in for help, it's always the last thing we do. If you're the guy people call for help, you're on the right track.

  • are naturally good at video games

    Ever pick up a game, and within minutes beat or come really close to beating, someone who's been playing it for months? This is a sure fire sign of your analytical and problem solving skills. Come to think of it, I think I'm going to start adding this to my interview process.

  • use every operating system in existence

    Sure, you think Windows sucks, but you use it because you play games on it and deep inside you know it doesn't really suck much worse than the competition. You know Linux is the best (DUH!) but you play with FreeBSD. You have OSX running on your laptop because those big icons and MacBooks are sexy. But really, it's more about curiosity than anything.

  • make a habit of picking up a new technology over the weekend

    Lego Mindstorms, anyone? Oooh, how about Adobe AIR or Microsoft Azure or iPhone development. You catch my drift.

  • are extremely critical of everything

    You find fault in everything from your takeout food to web sites to world economic systems. The world is an imperfect mess that needs to be cleaned up. And, of course, you could do it with a weekend and your new favorite development platform (that you haven't used yet)!

This is all I could come up with in the time I set aside for this blog post. So what do you all think? What am I missing? I'll update the post with your ideas as they come in, if they don't suck.

author: JD Conley | posted @ Monday, February 09, 2009 7:14 PM | Feedback (5)

About the author: JD Conley is the Chief Software Architect at Hive7, a Silicon Valley startup that makes massively multiplayer web games. Our top title, Knighthood, has over 4 million players and we are always working on cool new web games! Interested in making games with .NET? Drop me a line at jdc at hive7 dot com.

kick it on DotNetKicks.com

Abstracting Away Azure: How to Run Outside of the Cloud


I had a lot of fun over our holiday break this December working on prototype projects for up and coming technologies. One of those projects dealt with Windows Azure, or, the Azure Services Platform. Azure is basically a cloud application hosting environment put together by Microsoft. The idea is, you build your web apps in .NET and publish them to the nebulous cloud. Once in the cloud they scale and perform well and you don't have to deal with any of the headaches of managing things at the OS/System level.

But with the recent economic news out of Redmond I've been wondering about the future of its more experimental CTP/Alpha/Omega/Whatever-They-Call-It projects such as Azure. If you're not familiar with the project, I suggest you venture on over and check it out now.

Unlike other cloud hosting platforms out there, with Azure you don't have to maintain the operating system. Not only do you get the benefits of cloud computing, but you don't even need a system administrator to run the thing. Of course, the fact that you don't have control of the operating system has its drawbacks.

With Azure you can't run unmanaged code, you're stuck in Medium trust, and you can only build a port 80/443 HTTP application. If you want to run memcached or Velocity or streaming media codecs, well, you can't. If you want to host a game server that communicates with UDP or some non-http protocol, you can't do that either. But, for most custom web applications, everything you need is there. They host a "database" for you, a queue service, you can run background services, and you even get a shared logging service.

All of the services they provide seem to work as advertised and are promised to be extremely scalable. But, one thing they don't talk about (and I can't say I blame them) is how you might run your applications if they're not hosted in the cloud. In our company this just isn't acceptable. If we put out a game and our hosting provider ceases to exist, or no longer meets our needs, we had better be able to move to a new hosting provider! So, I'll give you some tips based on my experiences building prototype Azure applications on how you can easily easily design your applications to run outside of the cloud.

The Main Azure Features
  • Table storage
  • Queue services
  • Blob storage
  • Logging
  • Background services (Worker Role)
Table/Queue/Blob

Abstracting away tables, queues, and blobs is fairly simple but takes a bit of up front planning. You do basically the same thing you'd do if you were building an application on a large team that is designed to work with any data storage back end. At a high level:

In order to maintain the abstraction it's very important that your UI and background services don't interact directly with the Azure services. First off, use DTO entities. If all else fails and your new back end storage isn't compatible with Azure, you can always fall back to re-writing the layer that talks to it and you don't have to change any of your UI code. Do not expose the PartitionKey and RowKey values on your DTO entities. Leave the partitioning scheme as an implementation detail of your Service/Model layer. It will change if you have to move your data into Amazon's SimpleDB, for example. Since Azure Table Storage uses the ADO.NET Entity Framework at the core, there actually isn't much you need to do to the entities in order to make them portable to other Table-like storage systems. Also, the Blob and Queue storage services are quite simple and abstracting their interface is a matter of tens of lines of code.

Create interfaces for the layer that the UI communicates with and use a dependency injection (DI) framework such as StructureMap or Castle to inject your implementations that communicate with Azure.

I use StructureMap on a day to day basis, and I was dissapointed that it didn't work out of the box. I had to make a couple modifications to the source to get it to run under medium trust. First, you need to add an AllowPartiallyTrustedCallersAttribute to the assembly and then remove the security assertion that's asserting the right to read the machine name (you don't have access to the machine name in medium trust). You can download my updated version here (patch and binary): StructureMap-2.5-PartialTrust.zip

That's it. With your UI not talking directly to the Azure services you'll have an extra layer of code to maintain, but you'll be thankful if you ever need to pull it out of the cloud.

Logging

For all my non-Azure projects I use log4net for logging. It's a simple, flexible, open-source logging engine. You might want to use Enterprise Framework. Whatever. Just like with the storage engines the key to being able to move off of the Azure logging service some day is to not use it in your applications directly. I wrote a little Appender plugin for log4net that writes logs to the Azure RoleManager if the app is loaded into the Azure context. Most of the code is mapping the multitude of log4net log levels to the Azure event log names. Here's the code:

public class AzureRoleManagerAppender
    : AppenderSkeleton
{
    public AzureRoleManagerAppender()
    {
    }

    public AzureRoleManagerAppender(ILayout layout)
    {
        Layout = layout;
    }

    protected override void Append(log4net.Core.LoggingEvent loggingEvent)
    {
        if (null == Layout)
            Layout = new log4net.Layout.SimpleLayout();

        var sb = new StringBuilder();
        using (var sr = new StringWriter(sb))
        {
            Layout.Format(sr, loggingEvent);
            sr.Flush();

            if (RoleManager.IsRoleManagerRunning)
                RoleManager.WriteToLog(GetEventLogName(loggingEvent), sb.ToString());
            else
                System.Diagnostics.Trace.Write(sb.ToString(), GetEventLogName(loggingEvent));
        }
    }

    protected virtual string GetEventLogName(LoggingEvent loggingEvent)
    {
        if (loggingEvent.Level == Level.Alert)
            return "Critical";
        else if (loggingEvent.Level == Level.Critical)
            return "Critical";
        else if (loggingEvent.Level == Level.Debug)
            return "Verbose";
        else if (loggingEvent.Level == Level.Emergency)
            return "Critical";
        else if (loggingEvent.Level == Level.Error)
            return "Error";
        else if (loggingEvent.Level == Level.Fatal)
            return "Critical";
        else if (loggingEvent.Level == Level.Fine)
            return "Information";
        else if (loggingEvent.Level == Level.Finer)
            return "Information";
        else if (loggingEvent.Level == Level.Finest)
            return "Information";
        else if (loggingEvent.Level == Level.Info)
            return "Information";
        else if (loggingEvent.Level == Level.Notice)
            return "Information";
        else if (loggingEvent.Level == Level.Severe)
            return "Critical";
        else if (loggingEvent.Level == Level.Trace)
            return "Verbose";
        else if (loggingEvent.Level == Level.Verbose)
            return "Verbose";
        else if (loggingEvent.Level == Level.Warn)
            return "Warning";
        else
            return "Information";
    }
}


Then you just configure log4net as usual, and go on your merry way. Write your logs to log4net rather than to the Azure log manager.

<log4net>
  <appender name="azure" type="AzureRoleManagerAppender,MyAssembly">
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%logger - %message" />
    </layout>
  </appender>

  <root>
    <level value="ALL" />
    <appender-ref ref="azure" />
  </root>
</log4net>


private ILog _log = LogManager.GetLogger(typeof(WorkerRole));

...

_log.Info("Starting worker process");


Background Services

Background services (Worker Roles) are basically Windows Services. The key difference, though, is in the behavior of the Start method. In Windows Service land you're expected to exit the Start method when the service has started. In Azure, the Start method is more like a Main and when it exits Azure assumes your service has completed its task and is restarted. I'd just write all your code in your RoleEntryPoint and not worry about any abstraction for the Worker Role. It's simple enough to just refactor and move to a Windows Service model if need be. But, just like in your UI, don't communicate directly with Azure back end services like Table, Queue, and Blob storage.

So there you have it. The basics of abstracting away Azure. I don't think Microsoft plans on canceling this project any time soon, but if they do (or you want to host elsewhere) you'll be ready! I, for one, am really excited about the future potential of Azure and we may even use it here, but we will be designing our applications so they can easily be ported to a different platform just in case.

author: JD Conley | posted @ Monday, February 02, 2009 4:27 PM | Feedback (4)

About the author: JD Conley is the Chief Software Architect at Hive7, a Silicon Valley startup that makes massively multiplayer web games. Our top title, Knighthood, has over 4 million players and we are always working on cool new web games! Interested in making games with .NET? Drop me a line at jdc at hive7 dot com.

kick it on DotNetKicks.com