Skip to content

Commit a4a7ff9

Browse files
committed
add s3 service performance tests
1 parent 128ba19 commit a4a7ff9

File tree

5 files changed

+332
-0
lines changed

5 files changed

+332
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
#pragma once
7+
8+
#include <aws/core/Aws.h>
9+
#include <aws/core/utils/StringUtils.h>
10+
#include <aws/core/utils/UUID.h>
11+
#include <aws/core/utils/memory/stl/AWSString.h>
12+
13+
#include <algorithm>
14+
#include <cstddef>
15+
#include <cstdlib>
16+
#include <iostream>
17+
#include <random>
18+
#include <string>
19+
20+
namespace PerformanceTest {
21+
namespace Utils {
22+
/**
23+
* Generate a random string of specified length.
24+
* @param length The desired length of the string
25+
* @return A random string containing lowercase letters and digits
26+
*/
27+
static inline Aws::String RandomString(size_t length) {
28+
auto randchar = []() -> char {
29+
const char charset[] =
30+
"0123456789"
31+
"abcdefghijklmnopqrstuvwxyz";
32+
const size_t max_index = (sizeof(charset) - 1);
33+
return charset[rand() % max_index];
34+
};
35+
Aws::String str(length, 0);
36+
std::generate_n(str.begin(), length, randchar);
37+
return str;
38+
}
39+
40+
/**
41+
* Log error messages to stderr.
42+
* @param service The name of the AWS service
43+
* @param operation The name of the operation that failed
44+
* @param message The error message to log
45+
*/
46+
static inline void LogError(const Aws::String& service, const Aws::String& operation, const Aws::String& message) {
47+
std::cerr << "[ERROR] " << service << ":" << operation << " failed: " << message << '\n';
48+
}
49+
50+
/**
51+
* Generate a unique identifier using UUID.
52+
* @return A 10-character lowercase UUID substring
53+
*/
54+
static inline Aws::String GenerateUniqueId() {
55+
Aws::String const rawUUID = Aws::Utils::UUID::RandomUUID();
56+
return Aws::Utils::StringUtils::ToLower(rawUUID.c_str()).substr(0, 10);
57+
}
58+
59+
/**
60+
* Get the current SDK version as a formatted string.
61+
* @return SDK version in the format "major.minor.patch" (e.g., "1.11.0")
62+
*/
63+
static inline Aws::String GetSDKVersionString() {
64+
Aws::SDKOptions::SDKVersion const version;
65+
return Aws::Utils::StringUtils::to_string(static_cast<int>(version.major)) + "." +
66+
Aws::Utils::StringUtils::to_string(static_cast<int>(version.minor)) + "." +
67+
Aws::Utils::StringUtils::to_string(static_cast<int>(version.patch));
68+
}
69+
70+
} // namespace Utils
71+
} // namespace PerformanceTest
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
#pragma once
7+
8+
#include <aws/core/utils/memory/stl/AWSString.h>
9+
#include <aws/s3/S3Client.h>
10+
11+
#include <cstddef>
12+
13+
namespace PerformanceTest {
14+
namespace Services {
15+
namespace S3 {
16+
/**
17+
* Configuration for S3 performance test cases.
18+
*/
19+
struct TestCase {
20+
Aws::String sizeLabel;
21+
size_t sizeBytes;
22+
Aws::String bucketTypeLabel;
23+
};
24+
25+
/**
26+
* Runs S3 performance test by creating a bucket, performing PutObject and GetObject operations,
27+
* then cleaning up resources.
28+
* @param s3 S3 client instance to use for operations
29+
* @param config Test configuration containing object size and bucket type parameters
30+
* @param availabilityZoneId Availability zone ID for S3 Express buckets
31+
* @param iterations Number of put/get operations to perform
32+
*/
33+
void RunTest(Aws::S3::S3Client& s3, const TestCase& config, const Aws::String& availabilityZoneId, int iterations = 3);
34+
35+
/**
36+
* Create S3 bucket for testing.
37+
* @param s3 S3 client instance
38+
* @param config Test configuration
39+
* @param availabilityZoneId Availability zone ID for S3 Express buckets
40+
* @return Bucket name if successful, empty string if failed
41+
*/
42+
Aws::String SetupBucket(Aws::S3::S3Client& s3, const TestCase& config, const Aws::String& availabilityZoneId);
43+
44+
/**
45+
* Run PutObject and GetObject operations.
46+
* @param s3 S3 client instance
47+
* @param bucketName Name of the bucket to use
48+
* @param config Test configuration
49+
* @param iterations Number of operations to perform
50+
*/
51+
void RunOperations(Aws::S3::S3Client& s3, const Aws::String& bucketName, const TestCase& config, int iterations);
52+
53+
/**
54+
* Clean up S3 resources (objects and bucket).
55+
* @param s3 S3 client instance
56+
* @param bucketName Name of the bucket to clean up
57+
* @param iterations Number of objects to delete
58+
*/
59+
void CleanupResources(Aws::S3::S3Client& s3, const Aws::String& bucketName, int iterations);
60+
} // namespace S3
61+
} // namespace Services
62+
} // namespace PerformanceTest
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
#pragma once
7+
8+
#include <aws/core/utils/memory/stl/AWSSet.h>
9+
#include <aws/core/utils/memory/stl/AWSString.h>
10+
#include <aws/core/utils/memory/stl/AWSVector.h>
11+
12+
namespace PerformanceTest {
13+
namespace Services {
14+
namespace S3 {
15+
namespace TestConfig {
16+
const Aws::Set<Aws::String> TestOperations = {"PutObject", "GetObject"};
17+
18+
const Aws::Vector<TestCase> TestMatrix = {{"8KB", 8 * 1024, "s3-standard"}, {"64KB", 64 * 1024, "s3-standard"},
19+
{"1MB", 1024 * 1024, "s3-standard"}, {"8KB", 8 * 1024, "s3-express"},
20+
{"64KB", 64 * 1024, "s3-express"}, {"1MB", 1024 * 1024, "s3-express"}};
21+
22+
const Aws::String OutputFilename = "s3-performance-test-results.json";
23+
} // namespace TestConfig
24+
} // namespace S3
25+
} // namespace Services
26+
} // namespace PerformanceTest
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
#include <aws/core/utils/StringUtils.h>
7+
#include <aws/core/utils/memory/stl/AWSAllocator.h>
8+
#include <aws/core/utils/memory/stl/AWSString.h>
9+
#include <aws/core/utils/memory/stl/AWSStringStream.h>
10+
#include <aws/s3/S3Client.h>
11+
#include <aws/s3/model/BucketInfo.h>
12+
#include <aws/s3/model/BucketType.h>
13+
#include <aws/s3/model/CreateBucketConfiguration.h>
14+
#include <aws/s3/model/CreateBucketRequest.h>
15+
#include <aws/s3/model/DataRedundancy.h>
16+
#include <aws/s3/model/DeleteBucketRequest.h>
17+
#include <aws/s3/model/DeleteObjectRequest.h>
18+
#include <aws/s3/model/GetObjectRequest.h>
19+
#include <aws/s3/model/LocationType.h>
20+
#include <aws/s3/model/PutObjectRequest.h>
21+
#include <performance-tests/Utils.h>
22+
#include <performance-tests/services/s3/S3PerformanceTest.h>
23+
24+
void PerformanceTest::Services::S3::RunTest(Aws::S3::S3Client& s3, const TestCase& config, const Aws::String& availabilityZoneId,
25+
int iterations) {
26+
auto bucketName = SetupBucket(s3, config, availabilityZoneId);
27+
if (bucketName.empty()) {
28+
return;
29+
}
30+
31+
RunOperations(s3, bucketName, config, iterations);
32+
CleanupResources(s3, bucketName, iterations);
33+
}
34+
35+
Aws::String PerformanceTest::Services::S3::SetupBucket(Aws::S3::S3Client& s3, const TestCase& config,
36+
const Aws::String& availabilityZoneId) {
37+
Aws::String bucketName;
38+
Aws::S3::Model::CreateBucketRequest cbr;
39+
Aws::String const bucketId = PerformanceTest::Utils::GenerateUniqueId();
40+
41+
if (config.bucketTypeLabel == "s3-express") {
42+
bucketName = "perf-express-" + bucketId + "--" + availabilityZoneId + "--x-s3";
43+
cbr.SetBucket(bucketName);
44+
Aws::S3::Model::CreateBucketConfiguration bucketConfig;
45+
bucketConfig.SetLocation(
46+
Aws::S3::Model::LocationInfo().WithType(Aws::S3::Model::LocationType::AvailabilityZone).WithName(availabilityZoneId));
47+
48+
bucketConfig.SetBucket(Aws::S3::Model::BucketInfo()
49+
.WithType(Aws::S3::Model::BucketType::Directory)
50+
.WithDataRedundancy(Aws::S3::Model::DataRedundancy::SingleAvailabilityZone));
51+
52+
cbr.SetCreateBucketConfiguration(bucketConfig);
53+
} else {
54+
bucketName = "perf-standard-" + bucketId;
55+
cbr.SetBucket(bucketName);
56+
}
57+
58+
auto createOutcome = s3.CreateBucket(cbr);
59+
if (!createOutcome.IsSuccess()) {
60+
PerformanceTest::Utils::LogError("S3", "CreateBucket", createOutcome.GetError().GetMessage());
61+
return "";
62+
}
63+
64+
return bucketName;
65+
}
66+
67+
void PerformanceTest::Services::S3::RunOperations(Aws::S3::S3Client& s3, const Aws::String& bucketName, const TestCase& config,
68+
int iterations) {
69+
const auto randomPayload = PerformanceTest::Utils::RandomString(config.sizeBytes);
70+
71+
// Run PutObject multiple times
72+
for (int i = 0; i < iterations; i++) {
73+
auto stream = Aws::MakeShared<Aws::StringStream>("PerfStream");
74+
*stream << randomPayload;
75+
76+
Aws::S3::Model::PutObjectRequest por;
77+
por.WithBucket(bucketName).WithKey("test-object-" + Aws::Utils::StringUtils::to_string(i)).SetBody(stream);
78+
por.SetAdditionalCustomHeaderValue("test-dimension-size", config.sizeLabel);
79+
por.SetAdditionalCustomHeaderValue("test-dimension-bucket-type", config.bucketTypeLabel);
80+
auto putOutcome = s3.PutObject(por);
81+
if (!putOutcome.IsSuccess()) {
82+
PerformanceTest::Utils::LogError("S3", "PutObject", putOutcome.GetError().GetMessage());
83+
}
84+
}
85+
86+
// Run GetObject multiple times
87+
for (int i = 0; i < iterations; i++) {
88+
Aws::S3::Model::GetObjectRequest gor;
89+
gor.WithBucket(bucketName).WithKey("test-object-" + Aws::Utils::StringUtils::to_string(i));
90+
gor.SetAdditionalCustomHeaderValue("test-dimension-size", config.sizeLabel);
91+
gor.SetAdditionalCustomHeaderValue("test-dimension-bucket-type", config.bucketTypeLabel);
92+
auto getOutcome = s3.GetObject(gor);
93+
if (!getOutcome.IsSuccess()) {
94+
PerformanceTest::Utils::LogError("S3", "GetObject", getOutcome.GetError().GetMessage());
95+
}
96+
}
97+
}
98+
99+
void PerformanceTest::Services::S3::CleanupResources(Aws::S3::S3Client& s3, const Aws::String& bucketName, int iterations) {
100+
for (int i = 0; i < iterations; i++) {
101+
auto deleteObjectOutcome = s3.DeleteObject(
102+
Aws::S3::Model::DeleteObjectRequest().WithBucket(bucketName).WithKey("test-object-" + Aws::Utils::StringUtils::to_string(i)));
103+
if (!deleteObjectOutcome.IsSuccess()) {
104+
PerformanceTest::Utils::LogError("S3", "DeleteObject", deleteObjectOutcome.GetError().GetMessage());
105+
}
106+
}
107+
108+
auto deleteBucketOutcome = s3.DeleteBucket(Aws::S3::Model::DeleteBucketRequest().WithBucket(bucketName));
109+
if (!deleteBucketOutcome.IsSuccess()) {
110+
PerformanceTest::Utils::LogError("S3", "DeleteBucket", deleteBucketOutcome.GetError().GetMessage());
111+
}
112+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
#include <aws/core/Aws.h>
7+
#include <aws/core/client/ClientConfiguration.h>
8+
#include <aws/core/monitoring/MonitoringFactory.h>
9+
#include <aws/core/utils/memory/AWSMemory.h>
10+
#include <aws/core/utils/memory/stl/AWSString.h>
11+
#include <aws/s3/S3Client.h>
12+
#include <performance-tests/Utils.h>
13+
#include <performance-tests/reporting/JsonReportingMetrics.h>
14+
#include <performance-tests/services/s3/S3PerformanceTest.h>
15+
#include <performance-tests/services/s3/S3TestConfig.h>
16+
17+
#include <string>
18+
19+
int main(int argc, char** argv) {
20+
Aws::String region = "us-east-1";
21+
Aws::String availabilityZoneId = "use1-az4";
22+
Aws::String commitId = "unknown";
23+
int iterations = 10;
24+
25+
for (int i = 1; i < argc; ++i) {
26+
Aws::String const arg = argv[i];
27+
if (arg == "--region" && i + 1 < argc) {
28+
region = argv[++i];
29+
} else if (arg == "--az-id" && i + 1 < argc) {
30+
availabilityZoneId = argv[++i];
31+
} else if (arg == "--iterations" && i + 1 < argc) {
32+
iterations = std::stoi(argv[++i]);
33+
} else if (arg == "--commit-id" && i + 1 < argc) {
34+
commitId = argv[++i];
35+
}
36+
}
37+
38+
Aws::SDKOptions options;
39+
Aws::String const versionStr = PerformanceTest::Utils::GetSDKVersionString();
40+
41+
options.monitoringOptions.customizedMonitoringFactory_create_fn = {[&]() -> Aws::UniquePtr<Aws::Monitoring::MonitoringFactory> {
42+
return Aws::MakeUnique<PerformanceTest::Reporting::JsonReportingMetricsFactory>(
43+
"JsonReportingMetricsFactory", PerformanceTest::Services::S3::TestConfig::TestOperations, "cpp1", versionStr, commitId,
44+
PerformanceTest::Services::S3::TestConfig::OutputFilename);
45+
}};
46+
47+
Aws::InitAPI(options);
48+
49+
{
50+
Aws::Client::ClientConfiguration cfg;
51+
cfg.region = region;
52+
53+
Aws::S3::S3Client s3(cfg);
54+
for (const auto& config : PerformanceTest::Services::S3::TestConfig::TestMatrix) {
55+
PerformanceTest::Services::S3::RunTest(s3, config, availabilityZoneId, iterations);
56+
}
57+
}
58+
59+
Aws::ShutdownAPI(options);
60+
return 0;
61+
}

0 commit comments

Comments
 (0)