Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add perf tests for Blob storage scenarios. #17897

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions sdk/storage/Azure.Storage.Blobs/perf/Azure.Storage.Blobs.Perf.slnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"solution": {
"path": "..\\..\\Azure.Storage.sln",
"projects": [
"..\\..\\common\\Perf\\Azure.Test.Perf\\Azure.Test.Perf.csproj",
"..\\core\\Azure.Core.TestFramework\\src\\Azure.Core.TestFramework.csproj",
"Azure.Storage.Common\\src\\Azure.Storage.Common.csproj",
"Azure.Storage.Blobs\\src\\Azure.Storage.Blobs.csproj",
"Azure.Storage.Blobs\\perf\\Azure.Storage.Blobs.Perf\\Azure.Storage.Blobs.Perf.csproj",
"Azure.Storage.Blobs\\perf\\Microsoft.Azure.Storage.Blob.Perf\\Microsoft.Azure.Storage.Blob.Perf.csproj"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\src\Azure.Storage.Blobs.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\..\..\..\common\Perf\Azure.Test.Perf\Azure.Test.Perf.csproj" />
<ProjectReference Include="$(MSBuildThisFileDirectory)..\..\..\..\core\Azure.Core.TestFramework\src\Azure.Core.TestFramework.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Azure.Storage.Blobs.Specialized;
using Azure.Test.Perf;

namespace Azure.Storage.Blobs.Perf
{
public abstract class BlobTest<TOptions> : ContainerTest<TOptions> where TOptions : SizeOptions
{
protected BlobClient BlobClient { get; private set; }
protected BlockBlobClient BlockBlobClient { get; private set; }

public BlobTest(TOptions options) : base(options)
{
var blobName = $"Azure.Storage.Blobs.Perf.BlobTest-{Guid.NewGuid()}";

BlobClient = BlobContainerClient.GetBlobClient(blobName);
BlockBlobClient = BlobContainerClient.GetBlockBlobClient(blobName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Threading.Tasks;
using Azure.Test.Perf;

namespace Azure.Storage.Blobs.Perf
{
public abstract class ContainerTest<TOptions> : ServiceTest<TOptions> where TOptions : PerfOptions
{
// See https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata for
// restrictions on blob containers and blob storage naming.
protected static string ContainerName { get; } = $"ContainerTest-{Guid.NewGuid()}".ToLowerInvariant();

protected BlobContainerClient BlobContainerClient { get; private set; }

public ContainerTest(TOptions options) : base(options)
{
BlobContainerClient = BlobServiceClient.GetBlobContainerClient(ContainerName);
}

public override async Task GlobalSetupAsync()
{
await base.GlobalSetupAsync();
await BlobContainerClient.CreateAsync();
}

public override async Task GlobalCleanupAsync()
{
await BlobContainerClient.DeleteAsync();
await base.GlobalCleanupAsync();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Net.Http;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Core.Pipeline;
using Azure.Test.Perf;

namespace Azure.Storage.Blobs.Perf
{
public static class PerfStressTransport
{
public static HttpPipelineTransport Create(PerfOptions options)
{
HttpClient httpClient;
if (options.Insecure)
{
httpClient = new HttpClient(new HttpClientHandler()
{
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
});
}
else
{
httpClient = new HttpClient();
}

var httpClientTransport = new HttpClientTransport(httpClient);

if (!string.IsNullOrEmpty(options.Host))
{
return new ChangeUriTransport(httpClientTransport, options.Host, options.Port);
}
else
{
return httpClientTransport;
}
}

private class ChangeUriTransport : HttpPipelineTransport
{
private readonly HttpPipelineTransport _transport;
private readonly string _host;
private readonly int? _port;

public ChangeUriTransport(HttpPipelineTransport transport, string host, int? port)
{
_transport = transport;
_host = host;
_port = port;
}

public override Request CreateRequest()
{
return _transport.CreateRequest();
}

public override void Process(HttpMessage message)
{
ChangeUri(message);
_transport.Process(message);
}

public override ValueTask ProcessAsync(HttpMessage message)
{
ChangeUri(message);
return _transport.ProcessAsync(message);
}

private void ChangeUri(HttpMessage message)
{
// Ensure Host header is only set once, since the same HttpMessage will be reused on retries
if (!message.Request.Headers.Contains("Host"))
{
message.Request.Headers.Add("Host", message.Request.Uri.Host);
}

message.Request.Uri.Host = _host;
if (_port.HasValue)
{
message.Request.Uri.Port = _port.Value;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using Azure.Core.TestFramework;

namespace Azure.Storage.Blobs.Perf
{
/// <summary>
/// Represents the ambient environment in which the test suite is being run, offering access to information such as environment variables.
/// </summary>
internal sealed class PerfTestEnvironment : TestEnvironment
{
/// <summary>
/// The shared instance of the <see cref="PerfTestEnvironment"/> to be used during test runs.
/// </summary>
public static PerfTestEnvironment Instance { get; } = new PerfTestEnvironment();

/// <summary>
/// The storage account endpoint suffix of the cloud to use for testing.
/// </summary>
public new string StorageEndpointSuffix => base.StorageEndpointSuffix ?? "core.windows.net";

/// <summary>
/// The name of the Blob storage account to test against.
/// </summary>
/// <value>The Blob storage account name, read from the "AZURE_STORAGE_ACCOUNT_NAME" environment variable.</value>
public string BlobStorageAccountName => GetVariable("AZURE_STORAGE_ACCOUNT_NAME");

/// <summary>
/// The shared access key of the Blob storage account to test against.
/// </summary>
/// <value>The Blob storage account key, read from the "AZURE_STORAGE_ACCOUNT_KEY" environment variable.</value>
public string BlobStorageAccountKey => GetVariable("AZURE_STORAGE_ACCOUNT_KEY");

/// <summary>
/// The connection string for accessing the Blob storage account used for testing.
/// </summary>
public string BlobStorageConnectionString { get; }

/// <summary>
/// Initializes a new instance of the <see cref="PerfTestEnvironment"/> class.
/// </summary>
public PerfTestEnvironment()
{
BlobStorageConnectionString = $"DefaultEndpointsProtocol={Uri.UriSchemeHttps};AccountName={BlobStorageAccountName};AccountKey={BlobStorageAccountKey};EndpointSuffix={StorageEndpointSuffix}";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Test.Perf;

namespace Azure.Storage.Blobs.Perf
{
public abstract class ServiceTest<TOptions> : PerfTest<TOptions> where TOptions : PerfOptions
{
protected BlobServiceClient BlobServiceClient { get; private set; }

public ServiceTest(TOptions options) : base(options)
{
var blobClientOptions = new BlobClientOptions()
{
Transport = PerfStressTransport.Create(options)
};

BlobServiceClient = new BlobServiceClient(PerfTestEnvironment.Instance.BlobStorageConnectionString, blobClientOptions);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Test.Perf;
using CommandLine;

namespace Azure.Storage.Blobs.Perf
{
public class StorageTransferOptionsOptions : SizeOptions
{
private int? _maximumTransferLength;
private int? _maximumConcurrency;

[Option('l', "maximumTransferLength")]
public int? MaximumTransferLength
{
get => _maximumTransferLength;
set
{
_maximumTransferLength = value;
UpdateStorageTransferOptions();
}
}

[Option('t', "MaximumConcurrency")]
public int? MaximumConcurrency
{
get => _maximumConcurrency;
set
{
_maximumConcurrency = value;
UpdateStorageTransferOptions();
}
}

public StorageTransferOptions StorageTransferOptions { get; private set; }

private void UpdateStorageTransferOptions()
{
StorageTransferOptions = new StorageTransferOptions()
{
MaximumConcurrency = MaximumConcurrency,
MaximumTransferLength = MaximumTransferLength
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.Reflection;
using Azure.Test.Perf;

await PerfProgram.Main(Assembly.GetEntryAssembly(), args);
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Azure.Test.Perf;

namespace Azure.Storage.Blobs.Perf.Scenarios
{
/// <summary>
/// The performance test scenario focused on downloading blobs from the Azure blobs storage.
/// </summary>
/// <seealso cref="Azure.Test.Perf.PerfTest{StorageTransferOptionsOptions}" />
public sealed class DownloadBlob : ContainerTest<StorageTransferOptionsOptions>
{
private readonly BlobClient _blobClient;

public DownloadBlob(StorageTransferOptionsOptions options) : base(options)
{
_blobClient = BlobContainerClient.GetBlobClient("Azure.Storage.Blobs.Perf.Scenarios.DownloadBlob");
}

public override async Task GlobalSetupAsync()
{
await base.GlobalSetupAsync();

using Stream stream = RandomStream.Create(Options.Size);

// No need to delete file in GlobalCleanup(), since ContainerTest.GlobalCleanup() deletes the whole container
await _blobClient.UploadAsync(stream);
}

public override void Run(CancellationToken cancellationToken)
{
_blobClient.DownloadTo(Stream.Null, transferOptions: Options.StorageTransferOptions, cancellationToken: cancellationToken);
}

public override async Task RunAsync(CancellationToken cancellationToken)
{
await _blobClient.DownloadToAsync(Stream.Null, transferOptions: Options.StorageTransferOptions, cancellationToken: cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Azure.Test.Perf;

namespace Azure.Storage.Blobs.Perf.Scenarios
{
/// <summary>
/// The performance test scenario focused on uploading blobs to the Azure blobs storage.
/// </summary>
/// <seealso cref="Azure.Test.Perf.PerfTest{SizeOptions}" />
public sealed class UploadBlob : BlobTest<StorageTransferOptionsOptions>
{
private readonly Stream _stream;

public UploadBlob(StorageTransferOptionsOptions options) : base (options)
{
_stream = RandomStream.Create(options.Size);
}

public override void Dispose(bool disposing)
{
_stream.Dispose();
base.Dispose(disposing);
}

public override void Run(CancellationToken cancellationToken)
{
_stream.Seek(0, SeekOrigin.Begin);
BlobClient.Upload(_stream, transferOptions: Options.StorageTransferOptions, cancellationToken: cancellationToken);
}

public override async Task RunAsync(CancellationToken cancellationToken)
{
_stream.Seek(0, SeekOrigin.Begin);
await BlobClient.UploadAsync(_stream, transferOptions: Options.StorageTransferOptions, cancellationToken: cancellationToken);
}
}
}
Loading