Intro

In the previous post I have described how to lock blobs from any modifications and provided basic code samples. In this post I am going to show how to snapshot blobs.

Prerequisites

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

Code

It is possible to create a read-only snapshot of a blob. A snapshot provides a convenient way to back up blob data. You can use a snapshot to restore a blob to an earlier version by calling Copy Blob to overwrite a base blob with its snapshot.

Consider the following example:

var blob = container.GetBlobReference("blob.dat");
blob.Snapshot();

This code creates a new snapshot of the blob.

You can get a list of snapshots for a blob like this:

var snapshots = container.ListBlobs("blob.dat", true, BlobListingDetails.Snapshots).Cast<CloudBlob>().Where(_ => _.IsSnapshot);
foreach (var snap in snapshots)
{
    Console.WriteLine($"{snap.SnapshotQualifiedUri}");
}

You can copy a read-only snapshot to a sepparate blob. I will show how to perform this operation on a more complex example, but lets generate a big blob first:

var blob = container.GetBlockBlobReference("source.txt");
var buffer = new byte[1024 * 1024 * 4];
var r = new Random();
using (var stream = blob.OpenWrite())
{
    for (var i = 0; i < 400; i++)
    {
        r.NextBytes(buffer);
        stream.Write(buffer, 0, 1024 * 1024 * 4);
    }
}

This code will create a blob with 1.6GB of random data.

var blob = container.GetBlobReference("source.txt");
blob.Snapshot();

var oldestSnapshot = container
    .ListBlobs("source.txt", true, BlobListingDetails.Snapshots)
    .Cast<CloudBlob>()
    .Where(_ => _.IsSnapshot)
    .OrderBy(_ => _.SnapshotTime)
    .FirstOrDefault();

var newBlob = container.GetBlobReference("from-snapshot.dat");
newBlob.StartCopy(oldestSnapshot.SnapshotQualifiedUri);
newBlob.FetchAttributes();
Console.WriteLine($"{newBlob.CopyState.Status}");

This code will create a snapshot of source.txt blob and then the oldest snapshot will be copied to a sepparate blob. This process is almost instant, I belive it will take more time for giant blobs.

You can abort a copy operation like this:

var newBlob = container.GetBlobReference("from-snapshot.dat");
var copyId = newBlob.StartCopy(oldestSnapshot.SnapshotQualifiedUri);
newBlob.FetchAttributes();
newBlob.AbortCopy(copyId);

The StartCopy method return a copy ID that can be used to invoke AbortCopy method.

Another option is to revert blob to a snapshot. Consider a following example:

var blob = container.GetBlockBlobReference("small-blob.txt");
blob.UploadText("Content 1");
Console.WriteLine($"Current Blob Content: ${blob.DownloadText()}");

Console.WriteLine("Snapshoting...");
var snapshot = blob.Snapshot();
using (var sn = new StreamReader(snapshot.OpenRead()))
{
    Console.WriteLine($"Snapshot Content: {sn.ReadToEnd()}");
}

Console.WriteLine("Changing content...");
blob.UploadText("Content 2");
Console.WriteLine($"Current Blob Content: ${blob.DownloadText()}");

Console.WriteLine("Reverting to snapshot...");
blob.StartCopy(snapshot.SnapshotQualifiedUri);
Console.WriteLine($"Current Blob Content: ${blob.DownloadText()}");

This code outputs the following text:

Current Blob Content: $Content 1
Snapshoting...
Snapshot Content: Content 1
Changing content...
Current Blob Content: $Content 2
Reverting to snapshot...
Current Blob Content: $Content 1

The code creates a blob and makes a snapshot. After that, it updates the current version of a blob and then it replaces the current version of blob with the snapshot.

When you copy a snapshot to a new blob or copy the base blob to another blob, the snapshots of the original blob are not copied to the new blob. However, if you copy the snapshot to a blob that exists and has snapshots, it overwrites the base blob, but doesn’t affect the snapshots for the target blob.

If you try to delete a blob with snapshots by simply calling Delete you will get the following exception:

The remote server returned an error: (409) Conflict.

You have to explicitly specify that you want to delete a blob with all snapshots:

var blob = container.GetBlockBlobReference("small-blob.txt");
blob.Delete(DeleteSnapshotsOption.IncludeSnapshots);

or you can just delete all snapshots:

var blob = container.GetBlockBlobReference("small-blob.txt");
blob.Delete(DeleteSnapshotsOption.DeleteSnapshotsOnly);

Summary

In this post I showed how to work with snapshots, for more information visit Snapshot Blob. In the next post I am going to focus on block blobs.


;