Example #1
0
        public async Task CanDiffPagesBetweenSnapshots()
        {
            // Arrange
            var snapshot1Client = InstrumentClient(new PageBlobClient(snapshot1SASUri, GetOptions()));
            var snapshot2Client = InstrumentClient(new PageBlobClient(snapshot2SASUri, GetOptions()));

            // Act
            PageRangesInfo pageRangesInfo = await snapshot2Client.GetManagedDiskPageRangesDiffAsync(previousSnapshotUri : snapshot1SASUri);

            // Assert
            Assert.IsNotNull(pageRangesInfo.LastModified);
            Assert.IsNotNull(pageRangesInfo.ETag);
            CollectionAssert.IsNotEmpty(pageRangesInfo.ClearRanges);
            CollectionAssert.IsNotEmpty(pageRangesInfo.PageRanges);

            // Assert page diff
            var pageRange = pageRangesInfo.PageRanges.First();
            var range1    = await DownloadRange(snapshot1Client, pageRange);

            var range2 = await DownloadRange(snapshot2Client, pageRange);

            Assert.AreNotEqual(range1, range2);

            // Assert page clean
            var cleanRange = pageRangesInfo.ClearRanges.First();

            range2 = await DownloadRange(snapshot2Client, cleanRange);

            foreach (byte b in range2)
            {
                Assert.AreEqual(0, b);
            }
        }
        /// <summary>
        /// This method copies the changes since the last snapshot to the target base blob
        /// </summary>
        /// <param name="backupStorageAccountBlobClient">An instance of BlobClient which represents the storage account where the base blob is stored.</param>
        /// <param name="targetContainerName">The name of container in the target storage account where the base blob is stored</param>
        /// <param name="targetBaseBlobName">The name of the base blob used for storing the backups in the target storage account </param>
        /// <param name="lastSnapshotSASUri">The SAS URI of the last incremental snapshot</param>
        /// <param name="currentSnapshotSASUri">The SAS URI of the current snapshot</param>
        /// <returns></returns>
        private static async Task CopyChangesSinceLastSnapshotToBaseBlob(PageBlobClient targetBaseBlob, string lastSnapshotSASUri, string currentSnapshotSASUri)
        {
            PageBlobClient snapshot = new PageBlobClient(new Uri(currentSnapshotSASUri));

            //Get the changes since the last incremental snapshots of the managed disk.
            //GetManagedDiskDiffAsync is a new method introduced to get the changes since the last snapshot
            PageRangesInfo pageRangesInfo = await snapshot.GetManagedDiskPageRangesDiffAsync(null, null, new Uri(lastSnapshotSASUri), null, CancellationToken.None);

            foreach (var range in pageRangesInfo.ClearRanges)
            {
                await targetBaseBlob.ClearPagesAsync(range);
            }

            foreach (var range in pageRangesInfo.PageRanges)
            {
                Int64 rangeSize = (Int64)(range.Length);

                // Chop a range into 4MB chunchs
                for (Int64 subOffset = 0; subOffset < rangeSize; subOffset += FourMegabyteAsBytes)
                {
                    int subRangeSize = (int)Math.Min(rangeSize - subOffset, FourMegabyteAsBytes);

                    HttpRange sourceRange = new HttpRange(subOffset, subRangeSize);

                    //When you use WritePagesAsync by passing the SAS URI of the source snapshot, the SDK uses Put Page From URL rest API: https://docs.microsoft.com/en-us/rest/api/storageservices/put-page-from-url
                    //When this API is invoked, the Storage service reads the data from source and copies the data to the target blob without requiring clients to buffer the data.
                    await targetBaseBlob.UploadPagesFromUriAsync(new Uri(currentSnapshotSASUri), sourceRange, sourceRange, null, null, null, CancellationToken.None);
                }
            }

            await targetBaseBlob.CreateSnapshotAsync();
        }
        /// <summary>
        /// This method copies the first incremental snapshot as a base blob in the target region
        /// </summary>
        /// <param name="backupStorageAccountBlobClient">An instance of BlobClient which represents the storage account where the base blob is stored.</param>
        /// <param name="targetContainerName">The name of container in the target storage account where the base blob is stored</param>
        /// <param name="targetBaseBlobName">The name of the base blob used for storing the backups in the target storage account </param>
        /// <param name="sourceSnapshotSASUri">The SAS URI of the source snapshot</param>
        /// <returns></returns>
        private static async Task CopyFirstSnapshotToBackupStorageAccount(PageBlobClient targetBaseBlob, string sourceSnapshotSASUri)
        {
            PageBlobClient sourceSnapshot = new PageBlobClient(new Uri(sourceSnapshotSASUri));

            //Get the size of the source snapshot
            var snapshotProperties = await sourceSnapshot.GetPropertiesAsync();

            long sourceSnapshotSize = snapshotProperties.Value.ContentLength;

            //Create the target base blob with the same size as the source snapshot
            await targetBaseBlob.CreateAsync(sourceSnapshotSize);

            //Snapshots are stored as page blobs under the hood
            //Get all the valid page ranges from the source snapshot.
            //Learn more about page blobs and page ranges:
            //https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blob-pageblob-overview
            ///https://blogs.msdn.microsoft.com/windowsazurestorage/2012/03/26/getting-the-page-ranges-of-a-large-page-blob-in-segments/
            ///https://docs.microsoft.com/en-us/rest/api/storageservices/get-page-ranges
            PageRangesInfo pageRanges = await sourceSnapshot.GetPageRangesAsync();

            await WritePageRanges(sourceSnapshotSASUri, targetBaseBlob, pageRanges);

            await targetBaseBlob.CreateSnapshotAsync();
        }
        /// <summary>
        /// This method writes the page ranges from the source snapshot in the source region to the target base blob in the target region
        /// </summary>
        /// <param name="sourceSnapshotSASUri">The SAS URI of the source snapshot</param>
        /// <param name="targetBaseBlob">An instance of CloudPageBlob which represents the target base blob</param>
        /// <param name="pageRanges">Page ranges on the source snapshots that have changed since the last snapshot</param>
        /// <returns></returns>
        private static async Task WritePageRanges(string sourceSnapshotSASUri, PageBlobClient targetBaseBlob, PageRangesInfo pageRangeInfo)
        {
            foreach (HttpRange range in pageRangeInfo.PageRanges)
            {
                Int64 rangeSize = (Int64)(range.Length);

                // Chop a range into 4MB chunchs
                for (Int64 subOffset = 0; subOffset < rangeSize; subOffset += FourMegabyteAsBytes)
                {
                    int subRangeSize = (int)Math.Min(rangeSize - subOffset, FourMegabyteAsBytes);

                    HttpRange sourceRange = new HttpRange(subOffset, subRangeSize);

                    await targetBaseBlob.UploadPagesFromUriAsync(new Uri(sourceSnapshotSASUri), sourceRange, sourceRange, null, null, null, CancellationToken.None);
                }
            }
        }