Thu, 08 Apr 2010

Leasing Windows Azure Blobs Using the Storage Client Library

One of the unsung heroes of Windows Azure storage is the ability to acquire leases on blobs. This feature can help you solve thorny concurrency challenges, perform leader election, serialize edits, and much more. Look for more discussion of leases over on the Windows Azure Storage Team blog in the coming weeks.

A question came up on the Windows Azure forum yesterday about how to use this blob lease functionality from the storage client library that ships with the Windows Azure SDK. Unfortunately, methods to acquire, renew, break, and release leases is not yet included in the top-level Microsoft.WindowsAzure.StorageClient namespace. This makes it a bit hard to figure out how to use leases. In this post, I’ll show you what’s available in the storage client library to manage leases, and I’ll share some code to help you get going.

Using the Protocol namespace

The lease operations can be found in the Microsoft.WindowsAzure.StorageClient.Protocol namespace, which provides lower-level helpers to interact with the storage REST API. In that namespace, there’s a method called BlobRequest.Lease(), which can help you construct a web request to perform lease operations.

Here’s a simple method which attempts to acquire a new lease on a blob and returns the acquired lease ID. (For convenience, I’ve made this an extension method of CloudBlob, which allows for syntax like myBlob.AcquireLease().)

public static string AcquireLease(this CloudBlob blob)
{
    var creds = blob.ServiceClient.Credentials;
    var transformedUri = new Uri(creds.TransformUri(blob.Uri.ToString()));
    var req = BlobRequest.Lease(transformedUri,
        90, // timeout (in seconds)
        LeaseAction.Acquire, // as opposed to "break" "release" or "renew"
        null); // name of the existing lease, if any
    blob.ServiceClient.Credentials.SignRequest(req);
    using (var response = req.GetResponse())
    {
        return response.Headers["x-ms-lease-id"];
    }
}

The call to BlobRequest.Lease() gives me an HttpWebRequest which I can then execute. To make sure I’m using the correct URL and authorization, I’m using TransformUri() and SignRequest(). The former updates the URL with a Shared Access Signature (if needed), and the latter constructs the correct Authorization header (if needed). Doing both ensures that no matter which kind of access I’m using, I have a properly authorized HttpWebRequest.

Finally I execute the web request and read the x-ms-lease-id header to get back the newly-acquired lease (which will be in GUID format).

Using the lease once acquired

Unfortunately, because the methods in Microsoft.WindowsAzure.StorageClient don’t support leases yet, you’ll need to create your own convenience functions using the Protocol namespace to perform write operations on leased blobs. As an example, here’s an extension method I created to mimic the UploadText() method:

// NOTE: This method doesn't do everything that the regular UploadText does.
// Notably, it doesn't update the BlobProperties of the blob (with the new
// ETag and LastModifiedTimeUtc). It also, like all the methods in this file,
// doesn't apply any retry logic. Use this at your own risk!
public static void UploadText(this CloudBlob blob, string text, string leaseId)
{
    string url = blob.Uri.ToString();
    if (blob.ServiceClient.Credentials.NeedsTransformUri)
    {
        url = blob.ServiceClient.Credentials.TransformUri(url);
    }
    var req = BlobRequest.Put(new Uri(url), 90, new BlobProperties(), BlobType.BlockBlob, leaseId, 0);
    using (var writer = new StreamWriter(req.GetRequestStream()))
    {
        writer.Write(text);
    }
    blob.ServiceClient.Credentials.SignRequest(req);
    req.GetResponse().Close();
}

The rest of the lease methods

The rest of the lease methods were trivial to implement after writing AcquireLease. I made a single private method that handles all the other operations, and then extension methods that are easy to call:

private static void DoLeaseOperation(CloudBlob blob, string leaseId, LeaseAction action)
{
    var creds = blob.ServiceClient.Credentials;
    var transformedUri = new Uri(creds.TransformUri(blob.Uri.ToString()));
    var req = BlobRequest.Lease(transformedUri, 90, action, leaseId);
    creds.SignRequest(req);
    req.GetResponse().Close();
}

public static void ReleaseLease(this CloudBlob blob, string leaseId)
{
    DoLeaseOperation(blob, leaseId, LeaseAction.Release);
}

public static void RenewLease(this CloudBlob blob, string leaseId)
{
    DoLeaseOperation(blob, leaseId, LeaseAction.Renew);
}

public static void BreakLease(this CloudBlob blob)
{
    DoLeaseOperation(blob, null, LeaseAction.Break);
}

Simple usage

Here’s sample code that acquires a lease on a blob, modifies the contents of that blob, and then releases the lease:

var account = CloudStorageAccount.DevelopmentStorageAccount;
var blob = account.CreateCloudBlobClient().GetBlobReference("container/blob");
var leaseId = blob.AcquireLease();
blob.UploadText("new content", leaseId);
blob.ReleaseLease(leaseId);

Included in the sample code is a longer example that exercises all the methods and shows the semantics of blob leases.

Download the sample code

You can download the code in two forms:

  1. LeaseBlobExtensions.cs – just the extension methods, in the smarx.LeaseBlobExtensions namespace
  2. LeaseBlobTest_code.zip – VS 2010 solution with the extension methods and a console application that tests them out

Give it a try, and let me know what you think!