Thu, 09 Aug 2012

Simple Scheduling in Windows Azure

Nate Totten recently wrote a nice post called Task Scheduling with Windows Azure Web Sites Using a Cron Job Service, in which he described how to use a third-party cron service to call your web application on a regular schedule. This is a great solution when your code can only run in response to an incoming web request, as is the case in Windows Azure Web Sites, Heroku, or any (other) shared hoster. Today, Google App Engine has a built-in mechanism like this, but I remember people advocating the third-party cron service approach on GAE before this feature was added.

The downside of this approach is that you've added a dependency on a third-party service. Nate wrote:

When uptime is critical or you are running large processes I generally recommend using a Worker Role. It is pretty easy to setup a single worker role to run scheduled tasks.

Task Scheduling with Windows Azure Web Sites Using a Cron Job Service

I too have recommended using a worker role (or a web role, really) for task scheduling, but I built a more complex solution, focusing on scaling out across multiple role instances and scheduling tasks at arbitrary times (preempting existing tasks). Inspired by the simplicity of Nate's approach for Web Sites, I wondered how my approach could be simplified for people who wanted to use a worker role with multiple instances to run something on a simple recurring schedule. For example, suppose I want my application to send me a status report every 15 minutes. I'm running two role instances, but I only want to get one copy of the email.

Below is what I came up with. (Just install-package smarx.WazStorageExtensions first.)

public override Run()
{
    var blob = CloudStorageAccount
        .Parse(RoleEnvironment.GetConfigurationSettingValue("DataConnectionString"))
        .CreateCloudBlobClient().GetContainerReference("leases")
        .GetBlobReference(RoleEnvironment.DeploymentId + ".lease");

    AutoRenewLease.DoEvery(blob, TimeSpan.FromMinutes(15), () => {
        Trace.WriteLine("Doing stuff at {0:R}", DateTimeOffset.UtcNow);
    });
}

Note that I'm using the current deployment ID as part of the blob name. I did that in case I'm running the same code in multiple places (production and staging, or even locally). I want my scheduled task to run in each deployment.

Note that DoEvery never returns. If you want to run this in its own thread, you might want to wrap it like so: new Thread(() => AutoRenewLease...).Start().

The Code

This is part of my smarx.WazStorageExtensions NuGet package, the full source of which is available on GitHub.

This new method can be found in AutoRenewLease.cs.