Azure worker roles and recurring tasks

In our application we have multiple web front ends communicating with one or more worker roles. This allows our worker roles to take the brunt of expensive operations. For example, some (very infrequent) actions require up to about 500 emails being sent (via Amazon SES). Before Azure (BA), we did everything on the web server - it wasn't common but sometimes users reported over a minute from clicking the button to the result being displayed. Our move to using workers responding to queue messages has massively sped this sort of thing up.

Most actions on the worker are triggered by queue messages but we do have several things that just need to happen at set intervals (we use blob leases to prevent multiple workers doing the same thing). While implementing the "ExclusiveJobHost" and "BlobLease" classes, I noticed a common pattern that I ended up pulling out:

public class RecurringAction
{
    private ManualResetEvent exitSignal = new ManualResetEvent(false);

    private TimeSpan interval;

    private readonly Action behaviour;

    private Task runningTask;

    public RecurringAction(TimeSpan interval, Action behaviour)
    {
        this.interval = interval;
        this.behaviour = behaviour;
    }

    public void Start()
    {
        // Already running
        if (runningTask != null) return;
        exitSignal.Reset();
        runningTask = Task.Run(
            () => 
                {
                    while (!exitSignal.WaitOne(interval, true))
                    {
                        behaviour();
                    }
                    runningTask = null;
                });

    public void Stop()
    {
        exitSignal.Set();
    }
}

This allows you to Start and Stop an action e.g. from a worker role's RoleEntryPoint Run and OnStop respectively. I'm sure the usage of the class is obvious but here's an example anyway

class Program
{
    static void Main()
    {
        var a = new RecurringAction(TimeSpan.FromSeconds(1), () => Console.Write("."));
        a.Start();
        Thread.Sleep(10000);
        Console.WriteLine("\nCalling stop");
        a.Stop();
        Console.WriteLine("Stop called. Press any key to exit");
        Console.ReadKey();
    }
}

Which results in:


When I have some more time I'll share the blob lease approach. The basic idea is to acquire a lease for an amount of time (t), then at regular intervals (less than t) try to renew that lease.

Comments

Popular posts from this blog

Trimming strings in action parameters in ASP.Net Web API

Full text search in Entity Framework 6 using command interception

Composing Expressions in C#