Tutorial: Build a highly available application with Blob storage

This tutorial is part one of a series. In it, you learn how to make your application data highly available in Azure.

When you've completed this tutorial, you will have a console application that uploads and retrieves a blob from a read-access geo-redundant (RA-GRS) storage account.

RA-GRS works by replicating transactions from a primary region to a secondary region. This replication process guarantees that the data in the secondary region is eventually consistent. The application uses the Circuit Breaker pattern to determine which endpoint to connect to, automatically switching between endpoints as failures and recoveries are simulated.

If you don't have an Azure subscription, create a free account before you begin.

In part one of the series, you learn how to:

  • Create a storage account
  • Set the connection string
  • Run the console application

Prerequisites

To complete this tutorial:

Sign in to the Azure portal

Sign in to the Azure portal.

Create a storage account

A storage account provides a unique namespace to store and access your Azure Storage data objects.

Follow these steps to create a read-access geo-redundant storage account:

  1. Select the Create a resource button found on the upper left-hand corner of the Azure portal.

  2. Select Storage from the New page.

  3. Select Storage account - blob, file, table, queue under Featured.

  4. Fill out the storage account form with the following information, as shown in the following image and select Create:

    Setting       Suggested value Description 
    Name mystorageaccount A unique value for your storage account
    Deployment model Resource Manager Resource Manager contains the latest features.
    Account kind StorageV2 For details on the types of accounts, see types of storage accounts
    Performance Standard Standard is sufficient for the example scenario.
    Replication Read-access geo-redundant storage (RA-GRS) This is necessary for the sample to work.
    Subscription your subscription For details about your subscriptions, see Subscriptions.
    ResourceGroup myResourceGroup For valid resource group names, see Naming rules and restrictions.
    Location East US Choose a location.

create storage account

Download the sample

Download the sample project and extract (unzip) the storage-dotnet-circuit-breaker-pattern-ha-apps-using-ra-grs.zip file. You can also use git to download a copy of the application to your development environment. The sample project contains a console application.

git clone https://github.com/Azure-Samples/storage-dotnet-circuit-breaker-pattern-ha-apps-using-ra-grs.git

Configure the sample

In the application, you must provide the connection string for your storage account. You can store this connection string within an environment variable on the local machine running the application. Follow one of the examples below depending on your Operating System to create the environment variable.

In the Azure portal, navigate to your storage account. Select Access keys under Settings in your storage account. Copy the connection string from the primary or secondary key. Run one of the following commands based on your operating system, replacing <yourconnectionstring> with your actual connection string. This command saves an environment variable to the local machine. In Windows, the environment variable is not available until you reload the Command Prompt or shell you are using.

Linux

export storageconnectionstring=<yourconnectionstring>

Windows

setx storageconnectionstring "<yourconnectionstring>"

Run the console application

In Visual Studio, press F5 or select Start to begin debugging the application. Visual studio automatically restores missing NuGet packages if configured, visit Installing and reinstalling packages with package restore to learn more.

A console window launches and the application begins running. The application uploads the HelloWorld.png image from the solution to the storage account. The application checks to ensure the image has replicated to the secondary RA-GRS endpoint. It then begins downloading the image up to 999 times. Each read is represented by a P or an S. Where P represents the primary endpoint and S represents the secondary endpoint.

Console app running

In the sample code, the RunCircuitBreakerAsync task in the Program.cs file is used to download an image from the storage account using the DownloadToFileAsync method. Prior to the download, an OperationContext is defined. The operation context defines event handlers, that fire when a download completes successfully or if a download fails and is retrying.

Understand the sample code

Retry event handler

The OperationContextRetrying event handler is called when the download of the image fails and is set to retry. If the maximum number of retries defined in the application are reached, the LocationMode of the request is changed to SecondaryOnly. This setting forces the application to attempt to download the image from the secondary endpoint. This configuration reduces the time taken to request the image as the primary endpoint is not retried indefinitely.

private static void OperationContextRetrying(object sender, RequestEventArgs e)
{
    retryCount++;
    Console.WriteLine("Retrying event because of failure reading the primary. RetryCount = " + retryCount);

    // Check if we have had more than n retries in which case switch to secondary.
    if (retryCount >= retryThreshold)
    {

        // Check to see if we can fail over to secondary.
        if (blobClient.DefaultRequestOptions.LocationMode != LocationMode.SecondaryOnly)
        {
            blobClient.DefaultRequestOptions.LocationMode = LocationMode.SecondaryOnly;
            retryCount = 0;
        }
        else
        {
            throw new ApplicationException("Both primary and secondary are unreachable. Check your application's network connection. ");
        }
    }
}

Request completed event handler

The OperationContextRequestCompleted event handler is called when the download of the image is successful. If the application is using the secondary endpoint, the application continues to use this endpoint up to 20 times. After 20 times, the application sets the LocationMode back to PrimaryThenSecondary and retries the primary endpoint. If a request is successful, the application continues to read from the primary endpoint.

private static void OperationContextRequestCompleted(object sender, RequestEventArgs e)
{
    if (blobClient.DefaultRequestOptions.LocationMode == LocationMode.SecondaryOnly)
    {
        // You're reading the secondary. Let it read the secondary [secondaryThreshold] times,
        //    then switch back to the primary and see if it's available now.
        secondaryReadCount++;
        if (secondaryReadCount >= secondaryThreshold)
        {
            blobClient.DefaultRequestOptions.LocationMode = LocationMode.PrimaryThenSecondary;
            secondaryReadCount = 0;
        }
    }
}

Next steps

In part one of the series, you learned about making an application highly available with RA-GRS storage accounts.

Advance to part two of the series to learn how to simulate a failure and force your application to use the secondary RA-GRS endpoint.