From f90ab8a9b58b9b2050aedd4faf4394604517d5dd Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Mon, 25 Oct 2021 11:48:55 -0700 Subject: [PATCH] Add Storage Performance tests for DownloadTo API (#2987) Adding 3 new tests for Storage Performance The next tests are expected to find out the overhead between each layer of the Azure SDK client while performing a download operation. The current performance test is measuring the client + azure core pipeline + http transport. The next tests removes authentication policy and then each layer up to the HTTP transport adapter directly. DownloadBlobSas Generates a SaS token to download a blob. This way the authentication policy "sharedKey" won't run in the pipeline. DownloadBlobWithPipelineOnly Use the SaS token as well but send the request without the BlobClient. Instead it uses the Azure Core Pipeline directly DownloadBlobWithTransportOnly Same as before, the SaS token is used to download the blob but consuming only the libcurl-transport adapter. This option skips the HttpPipeline. The only elements used from Azure Core are the HttpRequest and the Azure::Context Note porting tests from the .NET equivalent: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/storage/Azure.Storage.Blobs/perf/Azure.Storage.Blobs.Perf/Scenarios/DownloadSasUriBlobClient.cs https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/storage/Azure.Storage.Blobs/perf/Azure.Storage.Blobs.Perf/Scenarios/DownloadSasUriHttpClient.cs https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/storage/Azure.Storage.Blobs/perf/Azure.Storage.Blobs.Perf/Scenarios/DownloadSasUriHttpPipeline.cs --- sdk/core/perf/CMakeLists.txt | 5 + .../test/perf/CMakeLists.txt | 7 ++ .../storage/blobs/test/blob_base_test.hpp | 29 ++++- .../blobs/test/download_blob_from_sas.hpp | 95 ++++++++++++++ .../test/download_blob_pipeline_only.hpp | 117 ++++++++++++++++++ .../test/download_blob_transport_only.hpp | 116 +++++++++++++++++ .../src/azure_storage_blobs_perf_test.cpp | 22 +++- 7 files changed, 385 insertions(+), 6 deletions(-) create mode 100644 sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_from_sas.hpp create mode 100644 sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_pipeline_only.hpp create mode 100644 sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_transport_only.hpp diff --git a/sdk/core/perf/CMakeLists.txt b/sdk/core/perf/CMakeLists.txt index 4ae4155be..db23c8f12 100644 --- a/sdk/core/perf/CMakeLists.txt +++ b/sdk/core/perf/CMakeLists.txt @@ -6,6 +6,11 @@ project(azure-perf LANGUAGES CXX) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake-modules") + +include(AzureTransportAdapters) set( AZURE_PERFORMANCE_HEADER diff --git a/sdk/storage/azure-storage-blobs/test/perf/CMakeLists.txt b/sdk/storage/azure-storage-blobs/test/perf/CMakeLists.txt index 3a61e2574..49fbb4672 100644 --- a/sdk/storage/azure-storage-blobs/test/perf/CMakeLists.txt +++ b/sdk/storage/azure-storage-blobs/test/perf/CMakeLists.txt @@ -7,10 +7,17 @@ project(azure-storage-blobs-perf LANGUAGES CXX) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED True) +if(BUILD_TRANSPORT_CURL) + set(DOWNLOAD_WITH_LIBCURL inc/azure/storage/blobs/test/download_blob_transport_only.hpp) +endif() + set( AZURE_STORAGE_BLOBS_PERF_TEST_HEADER inc/azure/storage/blobs/test/blob_base_test.hpp + inc/azure/storage/blobs/test/download_blob_from_sas.hpp + inc/azure/storage/blobs/test/download_blob_pipeline_only.hpp inc/azure/storage/blobs/test/download_blob_test.hpp + ${DOWNLOAD_WITH_LIBCURL} inc/azure/storage/blobs/test/list_blob_test.hpp inc/azure/storage/blobs/test/upload_blob_test.hpp ) diff --git a/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/blob_base_test.hpp b/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/blob_base_test.hpp index 7a506ff7e..93437b4d3 100644 --- a/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/blob_base_test.hpp +++ b/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/blob_base_test.hpp @@ -25,13 +25,35 @@ namespace Azure { namespace Storage { namespace Blobs { namespace Test { * */ class BlobsTest : public Azure::Perf::PerfTest { + private: + std::shared_ptr m_keyCredential; + protected: std::string m_containerName; std::string m_blobName; std::string m_connectionString; + std::unique_ptr m_serviceClient; std::unique_ptr m_containerClient; std::unique_ptr m_blobClient; + std::string GetSasToken() + { + // Generate SaS Token + auto sasStartsOn = std::chrono::system_clock::now() - std::chrono::minutes(5); + auto sasExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(60); + + Sas::BlobSasBuilder blobSasBuilder; + blobSasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp; + blobSasBuilder.StartsOn = sasStartsOn; + blobSasBuilder.ExpiresOn = sasExpiresOn; + blobSasBuilder.BlobContainerName = m_containerName; + blobSasBuilder.BlobName = m_blobName; + blobSasBuilder.Resource = Sas::BlobSasResource::Blob; + blobSasBuilder.SetPermissions(Sas::BlobSasPermissions::All); + + return blobSasBuilder.GenerateSasToken(*m_keyCredential); + } + public: /** * @brief Creat the container client @@ -48,12 +70,15 @@ namespace Azure { namespace Storage { namespace Blobs { namespace Test { m_blobName = "blob" + Azure::Core::Uuid::CreateUuid().ToString(); // Create client, container and blobClient + m_serviceClient = std::make_unique( + Azure::Storage::Blobs::BlobServiceClient::CreateFromConnectionString(m_connectionString)); m_containerClient = std::make_unique( - Azure::Storage::Blobs::BlobContainerClient::CreateFromConnectionString( - m_connectionString, m_containerName)); + m_serviceClient->GetBlobContainerClient(m_containerName)); m_containerClient->CreateIfNotExists(); m_blobClient = std::make_unique( m_containerClient->GetBlockBlobClient(m_blobName)); + + m_keyCredential = _internal::ParseConnectionString(m_connectionString).KeyCredential; } void Cleanup() override { m_containerClient->DeleteIfExists(); } diff --git a/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_from_sas.hpp b/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_from_sas.hpp new file mode 100644 index 000000000..b96a19bc5 --- /dev/null +++ b/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_from_sas.hpp @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Test the performance of downloading a block blob using SaS token. + * + */ + +#pragma once + +#include +#include + +#include "azure/storage/blobs/test/blob_base_test.hpp" + +#include +#include +#include +#include + +namespace Azure { namespace Storage { namespace Blobs { namespace Test { + + /** + * @brief A test to measure downloading a blob using SaS token. + * + */ + class DownloadBlobSas : public Azure::Storage::Blobs::Test::BlobsTest { + private: + std::unique_ptr> m_downloadBuffer; + std::unique_ptr m_blobClientSas; + + public: + /** + * @brief Construct a new DownloadBlobSas test. + * + * @param options The test options. + */ + DownloadBlobSas(Azure::Perf::TestOptions options) : BlobsTest(options) {} + + /** + * @brief The size to upload on setup is defined by a mandatory parameter. + * + */ + void Setup() override + { + // Call base to create blob client + BlobsTest::Setup(); + + long size = m_options.GetMandatoryOption("Size"); + + m_downloadBuffer = std::make_unique>(size); + + auto rawData = std::make_unique>(size); + auto content = Azure::Core::IO::MemoryBodyStream(*rawData); + m_blobClient->Upload(content); + + m_blobClientSas = std::make_unique( + m_blobClient->GetUrl() + GetSasToken()); + } + + /** + * @brief Define the test + * + */ + void Run(Azure::Core::Context const&) override + { + m_blobClientSas->DownloadTo(m_downloadBuffer->data(), m_downloadBuffer->size()); + } + + /** + * @brief Define the test options for the test. + * + * @return The list of test options. + */ + std::vector GetTestOptions() override + { + // TODO: Merge with base options + return {{"Size", {"--size"}, "Size of payload (in bytes)", 1, true}}; + } + + /** + * @brief Get the static Test Metadata for the test. + * + * @return Azure::Perf::TestMetadata describing the test. + */ + static Azure::Perf::TestMetadata GetTestMetadata() + { + return {"DownloadBlobSas", "Download a blob.", [](Azure::Perf::TestOptions options) { + return std::make_unique(options); + }}; + } + }; + +}}}} // namespace Azure::Storage::Blobs::Test diff --git a/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_pipeline_only.hpp b/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_pipeline_only.hpp new file mode 100644 index 000000000..3ba318a21 --- /dev/null +++ b/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_pipeline_only.hpp @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Test the performance of downloading a block blob using SaS token and with the http core + * pipeline directly. + * + */ + +#pragma once + +#include +#include +#include + +#include "azure/storage/blobs/test/blob_base_test.hpp" + +#include +#include +#include +#include + +namespace Azure { namespace Storage { namespace Blobs { namespace Test { + + /** + * @brief A test to measure downloading a blob using SaS token and with the http core pipeline + * directly. + * + */ + class DownloadBlobWithPipelineOnly : public Azure::Storage::Blobs::Test::BlobsTest { + private: + std::unique_ptr> m_downloadBuffer; + std::unique_ptr m_pipeline; + std::unique_ptr m_request; + + public: + /** + * @brief Construct a new DownloadBlobWithPipelineOnly test. + * + * @param options The test options. + */ + DownloadBlobWithPipelineOnly(Azure::Perf::TestOptions options) : BlobsTest(options) {} + + /** + * @brief The size to upload on setup is defined by a mandatory parameter. + * + */ + void Setup() override + { + // Call base to create blob client + BlobsTest::Setup(); + + long size = m_options.GetMandatoryOption("Size"); + bool bufferResponse = m_options.GetMandatoryOption("Buffer"); + + m_downloadBuffer = std::make_unique>(size); + + auto rawData = std::make_unique>(size); + auto content = Azure::Core::IO::MemoryBodyStream(*rawData); + m_blobClient->Upload(content); + + auto requestUrl = m_blobClient->GetUrl() + GetSasToken(); + + m_request = std::make_unique( + Azure::Core::Http::HttpMethod::Get, Azure::Core::Url(requestUrl), bufferResponse); + + Azure::Core::_internal::ClientOptions options; + std::vector> perRetry; + std::vector> perOperation; + m_pipeline = std::make_unique( + options, "perfTest", "x.x", std::move(perRetry), std::move(perOperation)); + } + + /** + * @brief Define the test + * + */ + void Run(Azure::Core::Context const& context) override + { + // Transport policy resolved the buffer option. If buffer is ON on the request, the response + // will contain the payload directly. When it is OFF, the response will contain the stream to + // the network. + auto response = m_pipeline->Send(*m_request, context); + } + + /** + * @brief Define the test options for the test. + * + * @return The list of test options. + */ + std::vector GetTestOptions() override + { + // TODO: Merge with base options + return { + {"Size", {"--size"}, "Size of payload (in bytes)", 1, true}, + {"Buffer", {"--buffer"}, "Whether to buffer the response", 1, true}}; + } + + /** + * @brief Get the static Test Metadata for the test. + * + * @return Azure::Perf::TestMetadata describing the test. + */ + static Azure::Perf::TestMetadata GetTestMetadata() + { + return { + "DownloadBlobWithPipelineOnly", + "Download a blob using the curl transport adapter directly. No SDK layer.", + [](Azure::Perf::TestOptions options) { + return std::make_unique( + options); + }}; + } + }; + +}}}} // namespace Azure::Storage::Blobs::Test diff --git a/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_transport_only.hpp b/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_transport_only.hpp new file mode 100644 index 000000000..e576b1d1c --- /dev/null +++ b/sdk/storage/azure-storage-blobs/test/perf/inc/azure/storage/blobs/test/download_blob_transport_only.hpp @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief Test the performance of downloading a block blob using SaS token and with transport + * adapter directly. + * + */ + +#pragma once + +#include +#include +#include + +#include "azure/storage/blobs/test/blob_base_test.hpp" + +#include +#include +#include +#include + +namespace Azure { namespace Storage { namespace Blobs { namespace Test { + + /** + * @brief A test to measure downloading a blob using SaS token and with transport adapter + * directly. + * + */ + class DownloadBlobWithTransportOnly : public Azure::Storage::Blobs::Test::BlobsTest { + private: + std::unique_ptr> m_downloadBuffer; + std::unique_ptr m_curlTransport; + bool m_bufferResponse = false; + std::unique_ptr m_request; + + public: + /** + * @brief Construct a new DownloadBlobWithTransportOnly test. + * + * @param options The test options. + */ + DownloadBlobWithTransportOnly(Azure::Perf::TestOptions options) : BlobsTest(options) {} + + /** + * @brief The size to upload on setup is defined by a mandatory parameter. + * + */ + void Setup() override + { + // Call base to create blob client + BlobsTest::Setup(); + + long size = m_options.GetMandatoryOption("Size"); + m_bufferResponse = m_options.GetMandatoryOption("Buffer"); + + m_downloadBuffer = std::make_unique>(size); + + auto rawData = std::make_unique>(size); + auto content = Azure::Core::IO::MemoryBodyStream(*rawData); + m_blobClient->Upload(content); + + auto requestUrl = m_blobClient->GetUrl() + GetSasToken(); + + m_curlTransport = std::make_unique(); + m_request = std::make_unique( + Azure::Core::Http::HttpMethod::Get, Azure::Core::Url(requestUrl), m_bufferResponse); + } + + /** + * @brief Define the test + * + */ + void Run(Azure::Core::Context const& context) override + { + auto response = m_curlTransport->Send(*m_request, context); + + if (m_bufferResponse) + { + // if test request the response stream to be read completely. + *m_downloadBuffer = response->ExtractBodyStream()->ReadToEnd(); + } + } + + /** + * @brief Define the test options for the test. + * + * @return The list of test options. + */ + std::vector GetTestOptions() override + { + // TODO: Merge with base options + return { + {"Size", {"--size"}, "Size of payload (in bytes)", 1, true}, + {"Buffer", {"--buffer"}, "Whether to buffer the response", 1, true}}; + } + + /** + * @brief Get the static Test Metadata for the test. + * + * @return Azure::Perf::TestMetadata describing the test. + */ + static Azure::Perf::TestMetadata GetTestMetadata() + { + return { + "DownloadBlobWithTransportOnly", + "Download a blob using the curl transport adapter directly. No SDK layer.", + [](Azure::Perf::TestOptions options) { + return std::make_unique( + options); + }}; + } + }; + +}}}} // namespace Azure::Storage::Blobs::Test diff --git a/sdk/storage/azure-storage-blobs/test/perf/src/azure_storage_blobs_perf_test.cpp b/sdk/storage/azure-storage-blobs/test/perf/src/azure_storage_blobs_perf_test.cpp index cdd8219d0..04fd4018e 100644 --- a/sdk/storage/azure-storage-blobs/test/perf/src/azure_storage_blobs_perf_test.cpp +++ b/sdk/storage/azure-storage-blobs/test/perf/src/azure_storage_blobs_perf_test.cpp @@ -3,7 +3,14 @@ #include +#include "azure/storage/blobs/test/download_blob_from_sas.hpp" +#include "azure/storage/blobs/test/download_blob_pipeline_only.hpp" #include "azure/storage/blobs/test/download_blob_test.hpp" + +#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER) +#include "azure/storage/blobs/test/download_blob_transport_only.hpp" +#endif + #include "azure/storage/blobs/test/list_blob_test.hpp" #include "azure/storage/blobs/test/upload_blob_test.hpp" @@ -11,10 +18,17 @@ int main(int argc, char** argv) { // Create the test list - std::vector tests{ - Azure::Storage::Blobs::Test::DownloadBlob::GetTestMetadata(), - Azure::Storage::Blobs::Test::UploadBlob::GetTestMetadata(), - Azure::Storage::Blobs::Test::ListBlob::GetTestMetadata()}; + std::vector tests + { + Azure::Storage::Blobs::Test::DownloadBlob::GetTestMetadata(), + Azure::Storage::Blobs::Test::UploadBlob::GetTestMetadata(), + Azure::Storage::Blobs::Test::ListBlob::GetTestMetadata(), + Azure::Storage::Blobs::Test::DownloadBlobSas::GetTestMetadata(), +#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER) + Azure::Storage::Blobs::Test::DownloadBlobWithTransportOnly::GetTestMetadata(), +#endif + Azure::Storage::Blobs::Test::DownloadBlobWithPipelineOnly::GetTestMetadata() + }; Azure::Perf::Program::Run(Azure::Core::Context::ApplicationContext, tests, argc, argv);