Intro

In the previous post I described common operations that can be performed on all types of blobs. In this post I am going to continue with blobs leasing, it is another operation that is common for all blob types.

Prerequisites

Refer to previous posts to get started with Azure Blob Service.

Code

You can block a blob for some time, this process called leasing. This operation establishes and manages a lock on a blob for write and delete operations. The lock duration can be 15 to 60 seconds, or can be infinite. Consider the following example:

var blob = container.GetBlobReference("blob.dat");
blob.AcquireLease(TimeSpan.FromSeconds(30), null);
blob.Delete();

This code fails with the following exception:

(412) There is currently a lease on the blob and no lease ID was specified in the request.

It means that it is impossible to perform write or delete operations on a blob that is currently leased without a lease ID. So you have to use the following code:

var blob = container.GetBlobReference("blob.dat");
var leaseId = blob.AcquireLease(TimeSpan.FromSeconds(15), null);
blob.Delete(AccessCondition.GenerateLeaseCondition(leaseId));

This code deletes a leased blob. When a lease is active, the lease ID must be included in the request for any non-read operations.

Consider a more complex example:

var blob = container.GetBlobReference("blob.dat");
Console.WriteLine("AcquireLease for 15 seconds.");
var leaseId = blob.AcquireLease(TimeSpan.FromSeconds(15), null);
try
{
    Console.WriteLine("Trying to delete blob...");
    blob.Delete();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Console.WriteLine("Waiting for 20 seconds for lease to expire...");
Thread.Sleep(TimeSpan.FromSeconds(20));

Console.WriteLine("Renew lease");
blob.RenewLease(AccessCondition.GenerateLeaseCondition(leaseId));

var newLeaseId = Guid.NewGuid().ToString();
Console.WriteLine($"Changing lease id from '{leaseId}' to '{newLeaseId}'");
blob.ChangeLease(newLeaseId, AccessCondition.GenerateLeaseCondition(leaseId));

Console.WriteLine("Break lease");
blob.BreakLease(TimeSpan.FromSeconds(10), AccessCondition.GenerateLeaseCondition(newLeaseId));
try
{
    Console.WriteLine("Trying to delete blob...");
    blob.Delete();
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Console.WriteLine("Release lease");
blob.ReleaseLease(AccessCondition.GenerateLeaseCondition(newLeaseId));

Console.WriteLine("Trying to delete blob...");
blob.Delete();
Console.WriteLine("Success!");

This code ouputs the following text:

AcquireLease for 15 seconds.
Trying to delete blob...
The remote server returned an error: (412) There is currently a lease on the blob and no lease ID was specified in the request..
Waiting for 20 seconds for lease to expire...
Renew lease
Changing lease id from 'bdefabea-181a-4f24-9a54-c85a65ec2596' to '5e25a88b-55f3-4046-a9dc-e082eb246f3b'
Break lease
Trying to delete blob...
The remote server returned an error: (412) There is currently a lease on the blob and no lease ID was specified in the request..
Release lease
Trying to delete blob...
Success!

This code first acquires a lease for 15 seconds by calling AcquireLease, then it tries to Delete a leased blob and fails. It is the expected behaviour, because we didn't specify lease ID. Then we wait for 20 seconds for a lease to expire and then renew the lease for additional 15 seconds. When you renew a lease, the clock restarts. You can’t change the duration of the lease when you renew it, it will use the duration of the original lease. After that we change the current lease ID to a new one by calling ChangeLease, and then we break the lease to show that BreakLease doesn't releases the lease. Finally we call ReleaseLease to completely release lease and delete the blob.

There is a way to check if blob is leased or not. Consider the following simple example:

var blob = container.GetBlobReferenceFromServer("blob.dat");
Console.WriteLine("Initial: ");
Console.WriteLine($"LeaseDuration: ${blob.Properties.LeaseDuration}");
Console.WriteLine($"LeaseStatus: ${blob.Properties.LeaseStatus}");
Console.WriteLine($"LeaseState: ${blob.Properties.LeaseState}");

var leaseId = blob.AcquireLease(TimeSpan.FromSeconds(15), null);
blob.FetchAttributes();
Console.WriteLine("AcquireLease: ");
Console.WriteLine($"LeaseDuration: ${blob.Properties.LeaseDuration}");
Console.WriteLine($"LeaseStatus: ${blob.Properties.LeaseStatus}");
Console.WriteLine($"LeaseState: ${blob.Properties.LeaseState}");

blob.RenewLease(AccessCondition.GenerateLeaseCondition(leaseId));
blob.FetchAttributes();
Console.WriteLine("RenewLease: ");
Console.WriteLine($"LeaseDuration: ${blob.Properties.LeaseDuration}");
Console.WriteLine($"LeaseStatus: ${blob.Properties.LeaseStatus}");
Console.WriteLine($"LeaseState: ${blob.Properties.LeaseState}");

blob.BreakLease(TimeSpan.FromSeconds(1), AccessCondition.GenerateLeaseCondition(leaseId));
blob.FetchAttributes();
Console.WriteLine("BreakLease: ");
Console.WriteLine($"LeaseDuration: ${blob.Properties.LeaseDuration}");
Console.WriteLine($"LeaseStatus: ${blob.Properties.LeaseStatus}");
Console.WriteLine($"LeaseState: ${blob.Properties.LeaseState}");

Thread.Sleep(TimeSpan.FromSeconds(5));
blob.FetchAttributes();
Console.WriteLine("Broken: ");
Console.WriteLine($"LeaseDuration: ${blob.Properties.LeaseDuration}");
Console.WriteLine($"LeaseStatus: ${blob.Properties.LeaseStatus}");
Console.WriteLine($"LeaseState: ${blob.Properties.LeaseState}");

blob.ReleaseLease(AccessCondition.GenerateLeaseCondition(leaseId));
blob.FetchAttributes();
Console.WriteLine("ReleaseLease: ");
Console.WriteLine($"LeaseDuration: ${blob.Properties.LeaseDuration}");
Console.WriteLine($"LeaseStatus: ${blob.Properties.LeaseStatus}");
Console.WriteLine($"LeaseState: ${blob.Properties.LeaseState}");

This code outputs the following text:

Initial:
LeaseDuration: $Unspecified
LeaseStatus: $Unlocked
LeaseState: $Available

AcquireLease:
LeaseDuration: $Fixed
LeaseStatus: $Locked
LeaseState: $Leased

RenewLease:
LeaseDuration: $Fixed
LeaseStatus: $Locked
LeaseState: $Leased

BreakLease:
LeaseDuration: $Unspecified
LeaseStatus: $Unlocked
LeaseState: $Breaking

Broken:
LeaseDuration: $Unspecified
LeaseStatus: $Unlocked
LeaseState: $Broken

ReleaseLease:
LeaseDuration: $Unspecified
LeaseStatus: $Unlocked
LeaseState: $Available

This simple example shows how to get information about current lease state of a blob. Make sure you call FetchAttributes before checking properties, otherwise you will get outdated info.

Keep in mind that you can also lease container, this operation establishes and manages a lock on a container for delete operations. But you can still update/delete individual blobs inside a container.

Summary

In this post I described the mechanism of blobs leasing. To get find out more about leasing blobs visit Lease Blob.


;