Skip to content
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
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,
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