Azure Table Storage and managing concurrency


In my previous post, Azure Blob Storage and managing concurrency, I wrote about storing a blob and then using either:

  • Optimistic concurrency – When performing an update, it will verify if the data has changed since the application last read that data.
  • Pessimistic concurrency – When performing an update, it will acquire a lock, preventing other processes from trying to update it.

When using Azure Table storage you only have the option of using optimistic concurrency. If pessimistic locking is needed, one approach is to assign a designated blob for each table, and try to take a lease on the blob before operating the table. I discuss this in my next post, Azure Table Storage and pessimistic concurrency using an Azure blob.

Optimistic concurrency

Every entity that is added to the storage is assigned an ETag. Every time the entity changes, the ETag will also change. It is this ETag that is used to calculate if the entity has changed since the process has last read it. The steps are:

  • Read the entity from the table storage, and grab the ETag header value.
  • Update the entity, passing in the ETag value from reading the entity from previous step.
  • The ETag passed in, matches the ETag of the current entity in the storage table. The entity is updated, and a new ETag assigned.
  • If the ETag passed in, doesn’t match the ETag of the current entity in the storage table, due to another process changes it, then an HttpStatusCode of 412 (PreconditionFailed) is returned and the file isn’t updated.

The below code (taken partly from Managing Concurrency using Azure Storage – Sample Application) shows an example of optimistic concurrency. It also shows how to ignore concurrency completely to simulate a different process updating the blob. You will need to following NuGet Packages installed for the code to work:

  • Microsoft.WindowsAzure.ConfigurationManager
  • WindowsAzure.Storage
internal void DemonstrateOptimisticConcurrencyUsingEntity()
{
    Console.WriteLine("Demo - Demonstrate optimistic concurrency using a table entity");
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
    string tableStorageReference = System.Configuration.ConfigurationManager.AppSettings["TableStorageReference"];

   CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
   CloudTable nextNumberTable = tableClient.GetTableReference(tableStorageReference);
   nextNumberTable.CreateIfNotExists();

   //Add new entity to table (Requires PartitionKey and RowsKey to work)
   NextNumberEntity originalNumber = new NextNumberEntity()
   {
        PartitionKey = "Numbers",
        RowKey = "Next",
        NumberValue = 0
   };

   TableOperation insert = TableOperation.InsertOrReplace(originalNumber);
   nextNumberTable.Execute(insert);
   Console.WriteLine("Entity added. Original ETag = {0}", originalNumber.ETag);

   //Simulate an update by different process
   NextNumberEntity updatedNumber = new NextNumberEntity()
   {
       PartitionKey = "Numbers",
       RowKey = "Next",
       NumberValue = 1
   };

   insert = TableOperation.InsertOrReplace(updatedNumber);
   nextNumberTable.Execute(insert);
   Console.WriteLine("Entity updated. Updated ETag = {0}", updatedNumber.ETag);

   //Try updating originalNumber. Etag is cached within originalNumber and passed by default.
   originalNumber.NumberValue = 2;
   insert = TableOperation.Merge(originalNumber);

   try
   {
       Console.WriteLine("Trying to update original entity");
       nextNumberTable.Execute(insert);
    }
    catch(StorageException ex)
    {
        if (ex.RequestInformation.HttpStatusCode == (int)HttpStatusCode.PreconditionFailed)
        {
            Console.WriteLine("Precondition failure as expected. Entities orignal etag does not match");
        }
        else
        {
            throw;
        }
    }

    Console.WriteLine("Press enter to exit");
    Console.ReadLine();
}

My app.config file for my console application has the following in it for the above to work.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <appSettings>
    <add key=" TableStorageReference" value="nextnumbers"/>
    <add key="StorageConnectionString" value="[Insert your own DefaultEndpointsProtocol to your Azure Storage] "/>
  </appSettings>
</configuration>

The results from running DemonstrateOptimisticConcurrencyUsingEntity()

If you want to see the table you could use visual studio. In the Server explorer, if you sign into your Azure account, under storage you should see the table you created. Mine is called NextNumbers, which I defined in my app.config “TableStorageReference”.

Within our table, we have the data we placed in from the update.

References: https://azure.microsoft.com/en-gb/blog/managing-concurrency-in-microsoft-azure-storage-2/

You can download my code from here.

Code: http://1drv.ms/1FlbDUw

Advertisements