Skip to content

Commit 885192e

Browse files
John Viegasjoviegas
John Viegas
authored andcommitted
Fix for Issue #1684 Changes in handleTimeoutCausedException in TimeoutExceptionHandlingStage
1 parent ab86d79 commit 885192e

File tree

4 files changed

+116
-1
lines changed

4 files changed

+116
-1
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"contributor": "",
4+
"type": "bugfix",
5+
"description": "Fix for [#1684](https://github.com/aws/aws-sdk-java-v2/issues/1684) Some of the Retry attempts which failed due to the API TimeOuts did not successfully retried but ended up with AbortedException."
6+
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/TimeoutExceptionHandlingStage.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import software.amazon.awssdk.core.client.config.SdkClientOption;
2626
import software.amazon.awssdk.core.exception.AbortedException;
2727
import software.amazon.awssdk.core.exception.ApiCallAttemptTimeoutException;
28+
import software.amazon.awssdk.core.exception.SdkClientException;
2829
import software.amazon.awssdk.core.exception.SdkInterruptedException;
2930
import software.amazon.awssdk.core.internal.http.HttpClientDependencies;
3031
import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
@@ -90,7 +91,8 @@ public Response<OutputT> execute(SdkHttpFullRequest request, RequestExecutionCon
9091
*/
9192
private Exception translatePipelineException(RequestExecutionContext context, Exception e) {
9293
if (e instanceof InterruptedException || e instanceof IOException ||
93-
e instanceof AbortedException || Thread.currentThread().isInterrupted()) {
94+
e instanceof AbortedException || Thread.currentThread().isInterrupted()
95+
|| (e instanceof SdkClientException && isCausedByApiCallAttemptTimeout(context))) {
9496
return handleTimeoutCausedException(context, e);
9597
}
9698
return e;

core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/TimeoutExceptionHandlingStageTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
3535
import software.amazon.awssdk.core.exception.AbortedException;
3636
import software.amazon.awssdk.core.exception.ApiCallAttemptTimeoutException;
37+
import software.amazon.awssdk.core.exception.SdkClientException;
3738
import software.amazon.awssdk.core.http.NoopTestRequest;
3839
import software.amazon.awssdk.core.internal.http.HttpClientDependencies;
3940
import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
@@ -164,6 +165,33 @@ public void interruptFlagWasSet_causedByApiCallTimeout_shouldThrowInterruptExcep
164165
verifyInterruptStatusPreserved();
165166
}
166167

168+
@Test
169+
public void apiCallAttemptTimeoutException_causedBySdkClientException_as_apiCallAttemptTimeoutTask_Caused_SdkClientException() throws Exception {
170+
when(apiCallTimeoutTask.hasExecuted()).thenReturn(false);
171+
when(apiCallAttemptTimeoutTask.hasExecuted()).thenReturn(true);
172+
when(requestPipeline.execute(any(), any())).thenThrow(SdkClientException.create(""));
173+
verifyExceptionThrown(ApiCallAttemptTimeoutException.class);
174+
}
175+
176+
@Test
177+
public void interruptedException_causedByApiCallAttemptTimeoutTask() throws Exception {
178+
when(apiCallTimeoutTask.hasExecuted()).thenReturn(true);
179+
when(apiCallAttemptTimeoutTask.hasExecuted()).thenReturn(true);
180+
when(requestPipeline.execute(any(), any())).thenThrow(SdkClientException.class);
181+
verifyExceptionThrown(InterruptedException.class);
182+
}
183+
184+
185+
@Test
186+
public void abortedException_causedByApiCallAttemptTimeoutTask_shouldNotPropagate() throws Exception {
187+
when(apiCallTimeoutTask.hasExecuted()).thenReturn(false);
188+
when(apiCallAttemptTimeoutTask.hasExecuted()).thenReturn(true);
189+
when(requestPipeline.execute(any(), any())).thenThrow(AbortedException.class);
190+
verifyExceptionThrown(ApiCallAttemptTimeoutException.class);
191+
}
192+
193+
194+
167195

168196
private void verifyInterruptStatusPreserved() {
169197
assertThat(Thread.currentThread().isInterrupted()).isTrue();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.dynamodb;
17+
18+
import org.junit.Test;
19+
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
20+
import software.amazon.awssdk.core.exception.ApiCallAttemptTimeoutException;
21+
import software.amazon.awssdk.core.retry.RetryPolicy;
22+
import software.amazon.awssdk.core.retry.conditions.OrRetryCondition;
23+
import software.amazon.awssdk.core.retry.conditions.RetryCondition;
24+
import software.amazon.awssdk.testutils.service.AwsTestBase;
25+
26+
import java.time.Duration;
27+
import java.util.concurrent.atomic.AtomicInteger;
28+
29+
import static org.junit.Assert.assertEquals;
30+
import static org.junit.Assert.assertTrue;
31+
32+
33+
/**
34+
* Simple test to check all the retries are made when all the API calls timeouts.
35+
*/
36+
public class DynamoDbJavaClientRetryOnTimeoutIntegrationTest extends AwsTestBase {
37+
38+
private static DynamoDbClient ddb;
39+
private final Integer RETRY_ATTEMPTS = 3;
40+
41+
public static void setupWithRetryPolicy(RetryPolicy retryPolicy, Integer attemptTimeOutMillis) throws Exception {
42+
ddb = DynamoDbClient.builder()
43+
.overrideConfiguration(ClientOverrideConfiguration.builder()
44+
.retryPolicy(retryPolicy)
45+
.apiCallAttemptTimeout(Duration.ofMillis(attemptTimeOutMillis)) //forces really quick api call timeout
46+
.build())
47+
.build();
48+
49+
}
50+
51+
public static RetryPolicy createRetryPolicyWithCounter(AtomicInteger counter, Integer numOfAttempts) {
52+
final RetryPolicy retryPolicy = RetryPolicy.defaultRetryPolicy().toBuilder()
53+
.numRetries(numOfAttempts)
54+
.retryCondition(OrRetryCondition.create(
55+
context -> {
56+
counter.incrementAndGet();
57+
return false;
58+
}, RetryCondition.defaultRetryCondition())).build();
59+
60+
return retryPolicy;
61+
62+
}
63+
64+
@Test
65+
public void testRetryAttemptsOnTimeOut() throws Exception {
66+
AtomicInteger atomicInteger = new AtomicInteger(0);
67+
Boolean apiTimeOutExceptionOccurred = Boolean.FALSE;
68+
setupWithRetryPolicy(createRetryPolicyWithCounter(atomicInteger, RETRY_ATTEMPTS), 2);
69+
try {
70+
71+
ddb.listTables();
72+
} catch (ApiCallAttemptTimeoutException e) {
73+
apiTimeOutExceptionOccurred = true;
74+
}
75+
assertEquals(RETRY_ATTEMPTS.intValue(), atomicInteger.get());
76+
assertTrue(apiTimeOutExceptionOccurred);
77+
}
78+
79+
}

0 commit comments

Comments
 (0)