Skip to content

Commit 7d670cc

Browse files
committed
Updated after Zoe's comments
1 parent f0ce7cb commit 7d670cc

File tree

2 files changed

+33
-81
lines changed

2 files changed

+33
-81
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"type": "bugfix",
3-
"category": "Apache HTTP Client",
3+
"category": "AWS SDK for Java v2",
44
"contributor": "",
55
"description": "Fixed issue with leased connection leaks when threads executing HTTP connections with Apache HttpClient were interrupted while the connection was in progress."
66
}

test/protocol-tests/src/test/java/software/amazon/awssdk/protocol/tests/connection/SyncClientConnectionInterruptionTest.java

Lines changed: 32 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,16 @@
2525
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
2626
import java.net.URI;
2727
import java.time.Duration;
28+
import java.util.List;
2829
import java.util.Optional;
2930
import java.util.concurrent.ExecutorService;
3031
import java.util.concurrent.Executors;
3132
import java.util.concurrent.Future;
3233
import java.util.concurrent.atomic.AtomicLong;
33-
import java.util.stream.Stream;
34+
import org.junit.jupiter.api.AfterAll;
35+
import org.junit.jupiter.api.AfterEach;
3436
import org.junit.jupiter.api.BeforeEach;
3537
import org.junit.jupiter.api.Test;
36-
import org.junit.jupiter.params.ParameterizedTest;
37-
import org.junit.jupiter.params.provider.Arguments;
38-
import org.junit.jupiter.params.provider.MethodSource;
3938
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
4039
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
4140
import software.amazon.awssdk.core.exception.AbortedException;
@@ -57,26 +56,32 @@ class SyncClientConnectionInterruptionTest {
5756
public static final String SAMPLE_BODY = "{\"StringMember"
5857
+ "\":\"resultString\"}";
5958
private final WireMockServer mockServer = new WireMockServer(new WireMockConfiguration()
60-
.bindAddress("localhost")
61-
.dynamicPort());
59+
.bindAddress("localhost").dynamicPort());
60+
61+
private static final ExecutorService executorService = Executors.newCachedThreadPool();
62+
6263
@BeforeEach
6364
public void setup() {
6465
mockServer.start();
6566
stubPostRequest(".*", aResponse(), "{}");
6667
}
6768

69+
@AfterAll
70+
public static void cleanUp(){
71+
executorService.shutdownNow();
72+
}
73+
6874
@Test
6975
void connectionPoolsGetsReusedWhenInterruptedWith_1_MaxConnection() throws Exception {
70-
Integer LONG_DELAY = 1500;
76+
Integer responseDelay = 1500;
7177

7278
String urlRegex = "/2016-03-11/allTypes";
73-
stubPostRequest(urlRegex, aResponse().withFixedDelay(LONG_DELAY), SAMPLE_BODY);
79+
stubPostRequest(urlRegex, aResponse().withFixedDelay(responseDelay), SAMPLE_BODY);
7480
SdkHttpClient httpClient = ApacheHttpClient.builder().maxConnections(1).build();
75-
ProtocolRestJsonClient client = getClient(httpClient, Duration.ofMillis(2L * LONG_DELAY)).build();
81+
ProtocolRestJsonClient client = getClient(httpClient, Duration.ofMillis(2L * responseDelay)).build();
7682

77-
ExecutorService executorService = Executors.newFixedThreadPool(5);
7883
Future<?> toBeInterruptedFuture = executorService.submit(() -> client.allTypes());
79-
unInterruptedSleep(LONG_DELAY - LONG_DELAY / 5);
84+
unInterruptedSleep(responseDelay - responseDelay / 5);
8085
toBeInterruptedFuture.cancel(true);
8186
// Make sure thread start the Http connections
8287
unInterruptedSleep(50);
@@ -85,56 +90,27 @@ void connectionPoolsGetsReusedWhenInterruptedWith_1_MaxConnection() throws Excep
8590
executorService.shutdownNow();
8691
}
8792

88-
@Test
89-
void connectionPoolsGetsReusedWhenInterruptedWith_Multiple_MaxConnection() throws Exception {
90-
Integer LONG_DELAY = 1000;
91-
Integer VERY_VERY_LONG_DELAY = LONG_DELAY * 5;
92-
stubPostRequest("/2016-03-11/allTypes", aResponse().withFixedDelay(LONG_DELAY), SAMPLE_BODY);
93-
stubPostRequest("/2016-03-11/JsonValuesOperation", aResponse().withFixedDelay(VERY_VERY_LONG_DELAY), SAMPLE_BODY);
94-
SdkHttpClient httpClient = ApacheHttpClient.builder().maxConnections(3).build();
95-
Duration timeOutDuration = Duration.ofMillis(2L * LONG_DELAY);
96-
ProtocolRestJsonClient client = getClient(httpClient, timeOutDuration).build();
97-
98-
ExecutorService executorService = Executors.newFixedThreadPool(5);
99-
Future<?> toBeInterruptedFuture0 = executorService.submit(() -> client.allTypes());
100-
Future<?> toBeInterruptedFuture1 = executorService.submit(() -> client.allTypes());
101-
Future<?> toBeInterruptedFuture2 = executorService.submit(() -> client.allTypes());
102-
unInterruptedSleep(50);
103-
executorService.submit(() -> client.jsonValuesOperation());
104-
unInterruptedSleep(LONG_DELAY / 2);
105-
toBeInterruptedFuture0.cancel(true);
106-
toBeInterruptedFuture1.cancel(true);
107-
toBeInterruptedFuture2.cancel(true);
108-
unInterruptedSleep(LONG_DELAY / 2);
109-
// Make sure thread start the Http connections
110-
AllTypesResponse allTypesResponse = client.allTypes();
111-
assertThat(allTypesResponse.stringMember()).isEqualTo("resultString");
112-
executorService.shutdownNow();
113-
}
114-
11593
@Test
11694
void interruptionWhenWaitingForLease_AbortsImmediately() throws InterruptedException {
117-
Integer LONG_DELAY = 5000;
95+
Integer responseDelay = 50000;
11896
ExceptionInThreadRun exceptionInThreadRun = new ExceptionInThreadRun();
119-
AtomicLong leaseWaitingTime = new AtomicLong(LONG_DELAY);
120-
stubPostRequest("/2016-03-11/allTypes", aResponse().withFixedDelay(LONG_DELAY), SAMPLE_BODY);
97+
AtomicLong leaseWaitingTime = new AtomicLong(responseDelay);
98+
stubPostRequest("/2016-03-11/allTypes", aResponse().withFixedDelay(responseDelay), SAMPLE_BODY);
12199
SdkHttpClient httpClient = ApacheHttpClient.builder().maxConnections(1).build();
122-
ProtocolRestJsonClient client = getClient(httpClient, Duration.ofMillis(2L * LONG_DELAY)).build();
123-
ExecutorService executorService = Executors.newFixedThreadPool(5);
100+
ProtocolRestJsonClient client = getClient(httpClient, Duration.ofMillis(2L * responseDelay)).build();
124101
executorService.submit(() -> client.allTypes());
125-
unInterruptedSleep(100);
102+
// 1 Sec sleep to make sure Thread 1 is picked for executing Http connection
103+
unInterruptedSleep(1000);
126104
Thread leaseWaitingThread = new Thread(() -> {
127105

128106
try {
129107
client.allTypes(l -> l.overrideConfiguration(
130108
b -> b
131-
.apiCallAttemptTimeout(Duration.ofSeconds(10))
132109
.addMetricPublisher(new MetricPublisher() {
133110
@Override
134111
public void publish(MetricCollection metricCollection) {
135-
System.out.println(metricCollection);
136112
Optional<MetricRecord<?>> apiCallDuration =
137-
metricCollection.stream().filter(o -> "ApiCallDuration" .equals(o.metric().name())).findAny();
113+
metricCollection.stream().filter(o -> "ApiCallDuration".equals(o.metric().name())).findAny();
138114
leaseWaitingTime.set(Duration.parse(apiCallDuration.get().value().toString()).toMillis());
139115
}
140116

@@ -151,11 +127,13 @@ public void close() {
151127
});
152128

153129
leaseWaitingThread.start();
154-
unInterruptedSleep(100);
130+
// 1 sec sleep to make sure Http connection execution is initialized for Thread 2 , in this case it will wait for lease
131+
// and immediately terminate on interrupt
132+
unInterruptedSleep(1000);
155133
leaseWaitingThread.interrupt();
156134
leaseWaitingThread.join();
157-
assertThat(leaseWaitingTime.get()).isNotEqualTo(LONG_DELAY.longValue());
158-
assertThat(leaseWaitingTime.get()).isLessThan(LONG_DELAY.longValue());
135+
assertThat(leaseWaitingTime.get()).isNotEqualTo(responseDelay.longValue());
136+
assertThat(leaseWaitingTime.get()).isLessThan(responseDelay.longValue());
159137
assertThat(exceptionInThreadRun.getException()).isInstanceOf(AbortedException.class);
160138
client.close();
161139
}
@@ -169,54 +147,28 @@ public void close() {
169147
@Test
170148
void interruptionDueToApiTimeOut_followed_byInterruptCausesOnlyTimeOutException() throws InterruptedException {
171149
SdkHttpClient httpClient = ApacheHttpClient.create();
172-
Integer SERVER_RESPONSE_DELAY = 3000;
173-
stubPostRequest("/2016-03-11/allTypes", aResponse().withFixedDelay(SERVER_RESPONSE_DELAY), SAMPLE_BODY);
150+
Integer responseDelay = 3000;
151+
stubPostRequest("/2016-03-11/allTypes", aResponse().withFixedDelay(responseDelay), SAMPLE_BODY);
174152
ExceptionInThreadRun exception = new ExceptionInThreadRun();
175153
ProtocolRestJsonClient client =
176154
getClient(httpClient, Duration.ofMillis(10)).overrideConfiguration(o -> o.retryPolicy(RetryPolicy.none())).build();
177155
unInterruptedSleep(100);
178156
// We need to creat a separate thread to interrupt it externally.
179157
Thread leaseWaitingThread = new Thread(() -> {
180158
try {
181-
client.allTypes(l -> l.overrideConfiguration(b -> b.apiCallAttemptTimeout(Duration.ofMillis(SERVER_RESPONSE_DELAY / 3))));
159+
client.allTypes(l -> l.overrideConfiguration(b -> b.apiCallAttemptTimeout(Duration.ofMillis(responseDelay / 3))));
182160
} catch (Exception e) {
183161
exception.setException(e);
184162
}
185163
});
186164
leaseWaitingThread.start();
187-
unInterruptedSleep(SERVER_RESPONSE_DELAY - SERVER_RESPONSE_DELAY / 10);
165+
unInterruptedSleep(responseDelay - responseDelay / 10);
188166
leaseWaitingThread.interrupt();
189167
leaseWaitingThread.join();
190168
assertThat(exception.getException()).isInstanceOf(ApiCallAttemptTimeoutException.class);
191169
client.close();
192170
}
193171

194-
195-
@Test
196-
void sdkClientInterrupted_while_connectionIsInProgress() throws InterruptedException {
197-
SdkHttpClient httpClient = ApacheHttpClient.create();
198-
Integer SERVER_RESPONSE_DELAY = 3000;
199-
stubPostRequest("/2016-03-11/allTypes", aResponse().withFixedDelay(SERVER_RESPONSE_DELAY), SAMPLE_BODY);
200-
ExceptionInThreadRun exception = new ExceptionInThreadRun();
201-
ProtocolRestJsonClient client =
202-
getClient(httpClient, Duration.ofMillis(10)).overrideConfiguration(o -> o.retryPolicy(RetryPolicy.none())).build();
203-
unInterruptedSleep(100);
204-
// We need to creat a separate thread to interrupt it externally.
205-
Thread leaseWaitingThread = new Thread(() -> {
206-
try {
207-
client.allTypes(l -> l.overrideConfiguration(b -> b.apiCallAttemptTimeout(Duration.ofMillis(SERVER_RESPONSE_DELAY * 3))));
208-
} catch (Exception e) {
209-
exception.setException(e);
210-
}
211-
});
212-
leaseWaitingThread.start();
213-
unInterruptedSleep(SERVER_RESPONSE_DELAY - SERVER_RESPONSE_DELAY / 10);
214-
leaseWaitingThread.interrupt();
215-
leaseWaitingThread.join();
216-
assertThat(exception.getException()).isInstanceOf(AbortedException.class);
217-
client.close();
218-
}
219-
220172
private class ExceptionInThreadRun {
221173
private Exception exception;
222174
public Exception getException() {

0 commit comments

Comments
 (0)