Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 6 additions & 0 deletions .changes/next-release/bugfix-AWSSDKforJavav2-a6b451e.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Add business metrics tracking for precomputed checksum headers in requests."
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, Re
result = legacyChecksum(request, context);
}

recordChecksumBusinessMetrics(context.executionAttributes());
recordChecksumBusinessMetrics(request, context.executionAttributes());

return result;
}
Expand Down Expand Up @@ -343,7 +343,7 @@ private PayloadChecksumStore getPayloadChecksumStore(ExecutionAttributes executi
return executionAttributes.getAttribute(CHECKSUM_STORE);
}

private void recordChecksumBusinessMetrics(ExecutionAttributes executionAttributes) {
private void recordChecksumBusinessMetrics(SdkHttpFullRequest.Builder request, ExecutionAttributes executionAttributes) {
BusinessMetricCollection businessMetrics =
executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS);

Expand All @@ -360,10 +360,16 @@ private void recordChecksumBusinessMetrics(ExecutionAttributes executionAttribut
.ifPresent(businessMetrics::addMetric);

ChecksumSpecs checksumSpecs = executionAttributes.getAttribute(RESOLVED_CHECKSUM_SPECS);
ChecksumAlgorithm algorithm = resolveChecksumAlgorithm(checksumSpecs);
BusinessMetricsUtils.resolveChecksumAlgorithmFeatureIds(algorithm, request)
.forEach(businessMetrics::addMetric);
}

private static ChecksumAlgorithm resolveChecksumAlgorithm(ChecksumSpecs checksumSpecs) {
if (checksumSpecs != null && checksumSpecs.algorithmV2() != null) {
BusinessMetricsUtils.resolveChecksumAlgorithmMetric(checksumSpecs.algorithmV2())
.ifPresent(businessMetrics::addMetric);
return checksumSpecs.algorithmV2();
}
return null;
}

static final class ChecksumCalculatingStreamProvider implements ContentStreamProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@

package software.amazon.awssdk.core.internal.useragent;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.checksums.DefaultChecksumAlgorithm;
import software.amazon.awssdk.checksums.spi.ChecksumAlgorithm;
Expand All @@ -24,6 +26,7 @@
import software.amazon.awssdk.core.retry.RetryMode;
import software.amazon.awssdk.core.retry.RetryPolicy;
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.retries.AdaptiveRetryStrategy;
import software.amazon.awssdk.retries.LegacyRetryStrategy;
import software.amazon.awssdk.retries.StandardRetryStrategy;
Expand Down Expand Up @@ -90,7 +93,22 @@ public static Optional<String> resolveResponseChecksumValidationMetric(
}
}

public static Optional<String> resolveChecksumAlgorithmMetric(ChecksumAlgorithm algorithm) {
public static Set<String> resolveChecksumAlgorithmFeatureIds(ChecksumAlgorithm algorithm,
SdkHttpFullRequest.Builder request) {
Set<String> ids = new HashSet<>(8);
request.forEachHeader((header, values) -> {
String id = headerToChecksumFeatureId(header);
if (id != null) {
ids.add(id);
}
});

resolveChecksumAlgorithmMetric(algorithm).ifPresent(ids::add);

return ids;
}

private static Optional<String> resolveChecksumAlgorithmMetric(ChecksumAlgorithm algorithm) {
if (algorithm == null) {
return Optional.empty();
}
Expand Down Expand Up @@ -135,4 +153,32 @@ public static Optional<String> resolveChecksumAlgorithmMetric(ChecksumAlgorithm
return Optional.empty();
}

// pkg private for testing
static String headerToChecksumFeatureId(String h) {
switch (h) {
case "x-amz-checksum-crc32":
return BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_CRC32.value();
case "x-amz-checksum-crc32c":
return BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_CRC32C.value();
case "x-amz-checksum-crc64nvme":
return BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_CRC64.value();
case "x-amz-checksum-sha256":
return BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_SHA256.value();
case "x-amz-checksum-sha512":
return BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_SHA512.value();
case "x-amz-checksum-sha1":
return BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_SHA1.value();
case "x-amz-checksum-md5":
return BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_MD5.value();
case "x-amz-checksum-xxhash64":
return BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_XXHASH64.value();
case "x-amz-checksum-xxhash3":
return BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_XXHASH3.value();
case "x-amz-checksum-xxhash128":
return BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_XXHASH128.value();
default:
return null;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,40 @@
* permissions and limitations under the License.
*/

package software.amazon.awssdk.core.internal.useragent.businessmetrics;
package software.amazon.awssdk.core.internal.useragent;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Arrays;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import software.amazon.awssdk.checksums.DefaultChecksumAlgorithm;
import software.amazon.awssdk.checksums.spi.ChecksumAlgorithm;
import software.amazon.awssdk.core.internal.retry.SdkDefaultRetryStrategy;
import software.amazon.awssdk.core.internal.useragent.BusinessMetricsUtils;
import software.amazon.awssdk.core.retry.RetryMode;
import software.amazon.awssdk.core.retry.RetryPolicy;
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.retries.api.RetryStrategy;

class BusinessMetricsUtilsTest {
private SdkHttpFullRequest.Builder testRequest;

@BeforeEach
void setup() {
testRequest = SdkHttpFullRequest.builder();
}

@ParameterizedTest(name = "{index} - {0}")
@MethodSource("inputValues")
@MethodSource("retryModeMetricInput")
void when_retryModeMetric_isResolvedFromInput_correctMetricIsReturned(String description, RetryPolicy retryPolicy,

Check warning on line 48 in core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtilsTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "RetryPolicy"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ1LhOQFPU4l3pg_Jj0a&open=AZ1LhOQFPU4l3pg_Jj0a&pullRequest=6838
RetryStrategy retryStrategy, String expected) {
RetryStrategy retryStrategy, String expected) {
Optional<String> retryModeMetric = BusinessMetricsUtils.resolveRetryMode(retryPolicy, retryStrategy);

if (expected != null) {
Expand All @@ -44,17 +56,54 @@
}
}

private static Stream<Arguments> inputValues() {
@ParameterizedTest(name = "{0} = {1}")
@MethodSource("checksumFeatureIdInput")
void when_checksumFeatureId_isResolvedFromHeader_correctMetricIsReturned(BusinessMetricFeatureId id, String header) {
assertThat(BusinessMetricsUtils.headerToChecksumFeatureId(header)).isEqualTo(id.value());
}

@Test
void when_checksumFeatureId_isResolvedFromHeader_unknownIsMappedToNull() {
assertThat(BusinessMetricsUtils.headerToChecksumFeatureId("x-amz-checksum-1234567")).isNull();
}

@Test
void when_checksumFeatureIds_areResolvedFromAlgorithmAndHeaders_allAlgorithmFeatureIdsReturned() {
ChecksumAlgorithm algorithm = DefaultChecksumAlgorithm.XXHASH128;
testRequest.putHeader("x-amz-checksum-crc32", "my-checksum");

assertThat(BusinessMetricsUtils.resolveChecksumAlgorithmFeatureIds(algorithm, testRequest))
.containsExactly(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_XXHASH128.value(),
BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_CRC32.value());
}

@Test
void when_checksumFeatureIds_areResolvedFromAlgorithmAndHeaders_andTheyResoveToTheSameId_idsAreDeduped() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: andTheyResolveToTheSameId

ChecksumAlgorithm algorithm = DefaultChecksumAlgorithm.CRC32;
testRequest.putHeader("x-amz-checksum-crc32", "my-checksum");

assertThat(BusinessMetricsUtils.resolveChecksumAlgorithmFeatureIds(algorithm, testRequest))
.containsExactly(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_CRC32.value());
}

@Test
void when_checksumFeatureIds_areResolvedFromAlgorithmAndHeaders_headerIsUnknown_ignored() {
testRequest.putHeader("x-amz-checksum-foo", "my-checksum");

assertThat(BusinessMetricsUtils.resolveChecksumAlgorithmFeatureIds(null, testRequest)).isEmpty();
}

private static Stream<Arguments> retryModeMetricInput() {
return Stream.of(
Arguments.of("No retry input returns empty", null, null, null),
Arguments.of("Retry policy for legacy mode returns legacy",
RetryPolicy.forRetryMode(RetryMode.LEGACY), null,

Check warning on line 100 in core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtilsTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "RetryPolicy"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ1LhOQFPU4l3pg_Jj0b&open=AZ1LhOQFPU4l3pg_Jj0b&pullRequest=6838
BusinessMetricFeatureId.RETRY_MODE_LEGACY.value()),
Arguments.of("Retry policy for standard mode returns standard",
RetryPolicy.forRetryMode(RetryMode.STANDARD), null,

Check warning on line 103 in core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtilsTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "RetryPolicy"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ1LhOQFPU4l3pg_Jj0c&open=AZ1LhOQFPU4l3pg_Jj0c&pullRequest=6838
BusinessMetricFeatureId.RETRY_MODE_STANDARD.value()),
Arguments.of("Retry policy for adaptive mode returns adaptive",
RetryPolicy.forRetryMode(RetryMode.ADAPTIVE), null,

Check warning on line 106 in core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtilsTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "RetryPolicy"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ1LhOQFPU4l3pg_Jj0d&open=AZ1LhOQFPU4l3pg_Jj0d&pullRequest=6838

Check warning on line 106 in core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtilsTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "ADAPTIVE"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ1LhOQFPU4l3pg_Jj0e&open=AZ1LhOQFPU4l3pg_Jj0e&pullRequest=6838
BusinessMetricFeatureId.RETRY_MODE_ADAPTIVE.value()),
Arguments.of("Retry strategy for legacy mode returns legacy", null,
SdkDefaultRetryStrategy.forRetryMode(RetryMode.LEGACY),
Expand All @@ -66,9 +115,24 @@
SdkDefaultRetryStrategy.forRetryMode(RetryMode.ADAPTIVE_V2),
BusinessMetricFeatureId.RETRY_MODE_ADAPTIVE.value()),
Arguments.of("Retry policy overrides retry strategy",
RetryPolicy.forRetryMode(RetryMode.LEGACY),

Check warning on line 118 in core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtilsTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this use of "RetryPolicy"; it is deprecated.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ1LhOQFPU4l3pg_Jj0f&open=AZ1LhOQFPU4l3pg_Jj0f&pullRequest=6838
SdkDefaultRetryStrategy.forRetryMode(RetryMode.ADAPTIVE_V2),
BusinessMetricFeatureId.RETRY_MODE_LEGACY.value())
);
}

private static Stream<Arguments> checksumFeatureIdInput() {
return Arrays.stream(BusinessMetricFeatureId.values())
.filter(id -> id.name().startsWith("FLEXIBLE_CHECKSUMS_REQ_")
&& !id.name().startsWith("FLEXIBLE_CHECKSUMS_REQ_WHEN"))
.map(id -> {
String name = id.name();
String algorithm = name.substring(23).toLowerCase(Locale.US);
// CRC64 is special >_<
if ("crc64".equals(algorithm)) {
algorithm = "crc64nvme";
}
return Arguments.of(id, "x-amz-checksum-" + algorithm);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,11 @@
"shape":"ChecksumAlgorithm",
"location":"header",
"locationName":"x-amz-sdk-checksum-algorithm"
},
"ChecksumCrc32":{
"shape":"String",
"location":"header",
"locationName":"x-amz-checksum-crc32"
}
},
"payload":"Body"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,39 @@
mockHttpClient.stubNextResponse(mockResponse());
}

@Test
void when_precomputedChecksumProvided_correctMetricIsAdded() {
ProtocolRestJsonClient client = ProtocolRestJsonClient.builder()
.region(Region.US_WEST_2)
.credentialsProvider(CREDENTIALS_PROVIDER)
.httpClient(mockHttpClient)
.build();

client.putOperationWithChecksum(r -> r.checksumCrc32("checksum"),
RequestBody.fromString("test content"));

String userAgent = getUserAgentFromLastRequest();
assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(FLEXIBLE_CHECKSUMS_REQ_CRC32.value()));
}

@Test
void when_precomputedChecksum_and_checksumAlgProvided_correctMetricsAreAdded() {
ProtocolRestJsonClient client = ProtocolRestJsonClient.builder()
.region(Region.US_WEST_2)
.credentialsProvider(CREDENTIALS_PROVIDER)
.httpClient(mockHttpClient)
.build();

client.putOperationWithChecksum(r -> r.checksumCrc32("checksum")
.checksumAlgorithm(ChecksumAlgorithm.CRC32_C),
RequestBody.fromString("test content"));

String userAgent = getUserAgentFromLastRequest();

assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(FLEXIBLE_CHECKSUMS_REQ_CRC32.value()));

Check warning on line 102 in test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/FlexibleChecksumBusinessMetricTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Join these multiple assertions subject to one assertion chain.

See more on https://sonarcloud.io/project/issues?id=aws_aws-sdk-java-v2&issues=AZ1LhOXCPU4l3pg_Jj1g&open=AZ1LhOXCPU4l3pg_Jj1g&pullRequest=6838
assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(FLEXIBLE_CHECKSUMS_REQ_CRC32C.value()));
}

@Test
void when_noChecksumConfigurationIsSet_defaultConfigMetricsAreAdded() {
ProtocolRestJsonClient client = ProtocolRestJsonClient.builder()
Expand Down
Loading