aws-sdk-java-v2 icon indicating copy to clipboard operation
aws-sdk-java-v2 copied to clipboard

RetryToken: expose attempts, scope and status

Open elruwen opened this issue 11 months ago • 6 comments

Describe the feature

Hi!

I want the RetryToken class to expose the fields attempt count, scope and status so I can get visibility when a retry is happening.

Cheers Ruwen

Use Case

I am currently migrating from the SDK v1. In the SDK v1 I got custom retry condition which extends PredefinedRetryPolicies.SDKDefaultRetryCondition.

I do that in order to log retries:

    public boolean shouldRetry(AmazonWebServiceRequest originalRequest, AmazonClientException exception, int retriesAttempted) {
        boolean retry = super.shouldRetry(originalRequest, exception, retriesAttempted);
        String source = originalRequest.getClass().getSimpleName();
        LOGGER.warn("Retrying: {}, Retry Attempt: {}, Source: {}, Exception: {}", retry, retriesAttempted, source, exception.getMessage());
        return retry;
    }

In the past that has been quite helpful and so I want to keep that behaviour.

Currently the RetryToken doesn't expose any fields. If I log toString() on the retry token, then the log message is way to verbose.

Proposed Solution

Expose the fields:

  • attempts
  • state
  • scope

Other Information

No response

Acknowledgements

  • [x] I may be able to implement this feature request
  • [ ] This feature might incur a breaking change

AWS Java SDK version used

2.29.52

JDK version used

17

Operating System and version

Debian stable

elruwen avatar Jan 23 '25 01:01 elruwen

@elruwen sorry for the long silence.

The attempts will show up as suppressed exceptions if WARN logging is used, like:

Caused by: software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: s3.not-a-real-region.amazonaws.com (SDK Attempt Count: 4)
	at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:130)
	at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:95)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper.retryPolicyDisallowedRetryException(RetryableStageHelper.java:168)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.execute(RetryableStage.java:73)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.RetryableStage.execute(RetryableStage.java:36)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.StreamManagingStage.execute(StreamManagingStage.java:53)
	at software.amazon.awssdk.core.internal.http.StreamManagingStage.execute(StreamManagingStage.java:35)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.executeWithTimer(ApiCallTimeoutTrackingStage.java:82)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.execute(ApiCallTimeoutTrackingStage.java:62)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallTimeoutTrackingStage.execute(ApiCallTimeoutTrackingStage.java:43)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallMetricCollectionStage.execute(ApiCallMetricCollectionStage.java:50)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ApiCallMetricCollectionStage.execute(ApiCallMetricCollectionStage.java:32)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.RequestPipelineBuilder$ComposingRequestPipelineStage.execute(RequestPipelineBuilder.java:206)
	at software.amazon.awssdk.core.internal.http.pipeline.stages.ExecutionFailureExceptionReportingStage.execute(ExecutionFailureExceptionReportingStage.java:37)
	... 38 more
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 1 failure: Unable to execute HTTP request: s3.not-a-real-region.amazonaws.com: nodename nor servname provided, or not known
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 2 failure: Unable to execute HTTP request: s3.not-a-real-region.amazonaws.com
	Suppressed: software.amazon.awssdk.core.exception.SdkClientException: Request attempt 3 failure: Unable to execute HTTP request: s3.not-a-real-region.amazonaws.com

DEBUG-level logging will also show the retry attempt number with additional info - example:

2025-04-29 13:31:28,156 [main] DEBUG software.amazon.awssdk.retries.LegacyRetryStrategy:85 - Request attempt 3 token acquired (backoff: 35ms, cost: 5, capacity: 490/500)
2025-04-29 13:31:28,156 [main] DEBUG software.amazon.awssdk.request:96 - Retryable error detected. Will retry in 35ms. Request attempt number 2
software.amazon.awssdk.core.exception.SdkClientException: Unable to execute HTTP request: s3.not-a-real-region.amazonaws.com
	at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:130)
	at software.amazon.awssdk.core.exception.SdkClientException.create(SdkClientException.java:47)
	...

I'm not quite sure what "state" and "scope" mean in this context, but maybe they are covered in the DEBUG logs?

Let us know if this is what you're looking for.

debora-ito avatar Apr 29 '25 20:04 debora-ito

Hi @debora-ito,

which class is producing those logs?

I got a custom retry strategy and nothing is logged. In the BaseRetryStrategy I can see only debug logs. I don't want to enable debug logs since it is way to verbose.

About the fields: They are all in the class software.amazon.awssdk.retries.internal.DefaultRetryToken but the API is only exposing the interface RetryToken.

elruwen avatar Apr 30 '25 04:04 elruwen

Can you share a code snippet showing how you create the custom retry strategy, so I can run it locally?

debora-ito avatar Apr 30 '25 16:04 debora-ito

It looks like this issue has not been active for more than five days. In the absence of more information, we will be closing this issue soon. If you find that this is still a problem, please add a comment to prevent automatic closure, or if the issue is already closed please feel free to reopen it.

github-actions[bot] avatar May 10 '25 18:05 github-actions[bot]

Sorry for the delay. I hope that is enough, otherwise I will make you a maven project.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.retries.api.AcquireInitialTokenRequest;
import software.amazon.awssdk.retries.api.AcquireInitialTokenResponse;
import software.amazon.awssdk.retries.api.RecordSuccessRequest;
import software.amazon.awssdk.retries.api.RecordSuccessResponse;
import software.amazon.awssdk.retries.api.RefreshRetryTokenRequest;
import software.amazon.awssdk.retries.api.RefreshRetryTokenResponse;
import software.amazon.awssdk.retries.api.RetryStrategy;


public class LoggingRetryStrategy implements RetryStrategy {
    private static final Logger LOGGER = LoggerFactory.getLogger(LoggingRetryStrategy.class);

    private final RetryStrategy delegate;

    public LoggingRetryStrategy(RetryStrategy delegate) {
        this.delegate = delegate;
    }

    @Override
    public AcquireInitialTokenResponse acquireInitialToken(AcquireInitialTokenRequest request) {
        return delegate.acquireInitialToken(request);
    }

    @Override
    public RefreshRetryTokenResponse refreshRetryToken(RefreshRetryTokenRequest request) {
        // TODO this quite verbose at the moment...
        // see https://github.com/aws/aws-sdk-java-v2/issues/5818
        LOGGER.warn("Retrying: {} Exception: {}", request.token(), request.failure().getMessage());
        return delegate.refreshRetryToken(request);
    }

    @Override
    public RecordSuccessResponse recordSuccess(RecordSuccessRequest request) {
        return delegate.recordSuccess(request);
    }

    @Override
    public int maxAttempts() {
        return delegate.maxAttempts();
    }

    @Override
    public boolean useClientDefaults() {
        return delegate.useClientDefaults();
    }

    @Override
    public Builder<?, ?> toBuilder() {
        return delegate.toBuilder();
    }
}

elruwen avatar May 13 '25 06:05 elruwen

Any update on this?

elruwen avatar Jun 10 '25 04:06 elruwen