RetryToken: expose attempts, scope and status
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 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.
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.
Can you share a code snippet showing how you create the custom retry strategy, so I can run it locally?
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.
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();
}
}
Any update on this?