Skip to content

Commit c68a554

Browse files
authored
feat: Added configurable timeouts for fetchSegments and dispatch events ODP calls (#494)
## Summary Added configurable timeouts for fetchSegments and dispatch events ODP calls. The default timeout is 10s. ## Test plan - Manually tested thoroughly - All test pass ## Issues [FSSDK-8689](https://jira.sso.episerver.net/browse/FSSDK-8689)
1 parent 42039e2 commit c68a554

File tree

4 files changed

+62
-10
lines changed

4 files changed

+62
-10
lines changed

core-httpclient-impl/src/main/java/com/optimizely/ab/HttpClientUtils.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323
*/
2424
public final class HttpClientUtils {
2525

26-
private static final int CONNECTION_TIMEOUT_MS = 10000;
27-
private static final int CONNECTION_REQUEST_TIMEOUT_MS = 5000;
28-
private static final int SOCKET_TIMEOUT_MS = 10000;
26+
public static final int CONNECTION_TIMEOUT_MS = 10000;
27+
public static final int CONNECTION_REQUEST_TIMEOUT_MS = 5000;
28+
public static final int SOCKET_TIMEOUT_MS = 10000;
29+
30+
private static RequestConfig requestConfigWithTimeout;
2931

3032
private HttpClientUtils() {
3133
}
@@ -36,6 +38,17 @@ private HttpClientUtils() {
3638
.setSocketTimeout(SOCKET_TIMEOUT_MS)
3739
.build();
3840

41+
public static RequestConfig getDefaultRequestConfigWithTimeout(int timeoutMillis) {
42+
if (requestConfigWithTimeout == null) {
43+
requestConfigWithTimeout = RequestConfig.custom()
44+
.setConnectTimeout(timeoutMillis)
45+
.setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT_MS)
46+
.setSocketTimeout(SOCKET_TIMEOUT_MS)
47+
.build();
48+
}
49+
return requestConfigWithTimeout;
50+
}
51+
3952
public static OptimizelyHttpClient getDefaultHttpClient() {
4053
return OptimizelyHttpClient.builder().build();
4154
}

core-httpclient-impl/src/main/java/com/optimizely/ab/OptimizelyHttpClient.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
*
3-
* Copyright 2019, Optimizely
3+
* Copyright 2019, 2022 Optimizely
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -25,6 +25,8 @@
2525
import org.apache.http.impl.client.HttpClientBuilder;
2626
import org.apache.http.impl.client.HttpClients;
2727
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
2830

2931
import java.io.Closeable;
3032
import java.io.IOException;
@@ -38,6 +40,8 @@
3840
*/
3941
public class OptimizelyHttpClient implements Closeable {
4042

43+
private static final Logger logger = LoggerFactory.getLogger(OptimizelyHttpClient.class);
44+
4145
private final CloseableHttpClient httpClient;
4246

4347
OptimizelyHttpClient(CloseableHttpClient httpClient) {
@@ -78,6 +82,7 @@ public static class Builder {
7882
// force-close the connection after this idle time (with 0, eviction is disabled by default)
7983
long evictConnectionIdleTimePeriod = 0;
8084
TimeUnit evictConnectionIdleTimeUnit = TimeUnit.MILLISECONDS;
85+
private int timeoutMillis = HttpClientUtils.CONNECTION_TIMEOUT_MS;
8186

8287
private Builder() {
8388

@@ -103,6 +108,11 @@ public Builder withEvictIdleConnections(long maxIdleTime, TimeUnit maxIdleTimeUn
103108
this.evictConnectionIdleTimeUnit = maxIdleTimeUnit;
104109
return this;
105110
}
111+
112+
public Builder setTimeoutMillis(int timeoutMillis) {
113+
this.timeoutMillis = timeoutMillis;
114+
return this;
115+
}
106116

107117
public OptimizelyHttpClient build() {
108118
PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager();
@@ -111,11 +121,13 @@ public OptimizelyHttpClient build() {
111121
poolingHttpClientConnectionManager.setValidateAfterInactivity(validateAfterInactivity);
112122

113123
HttpClientBuilder builder = HttpClients.custom()
114-
.setDefaultRequestConfig(HttpClientUtils.DEFAULT_REQUEST_CONFIG)
124+
.setDefaultRequestConfig(HttpClientUtils.getDefaultRequestConfigWithTimeout(timeoutMillis))
115125
.setConnectionManager(poolingHttpClientConnectionManager)
116126
.disableCookieManagement()
117127
.useSystemProperties();
118128

129+
logger.debug("Creating HttpClient with timeout: " + timeoutMillis);
130+
119131
if (evictConnectionIdleTimePeriod > 0) {
120132
builder.evictIdleConnections(evictConnectionIdleTimePeriod, evictConnectionIdleTimeUnit);
121133
}

core-httpclient-impl/src/main/java/com/optimizely/ab/odp/DefaultODPApiManager.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,27 @@
3333
public class DefaultODPApiManager implements ODPApiManager {
3434
private static final Logger logger = LoggerFactory.getLogger(DefaultODPApiManager.class);
3535

36-
private final OptimizelyHttpClient httpClient;
36+
private final OptimizelyHttpClient httpClientSegments;
37+
private final OptimizelyHttpClient httpClientEvents;
3738

3839
public DefaultODPApiManager() {
3940
this(OptimizelyHttpClient.builder().build());
4041
}
4142

43+
public DefaultODPApiManager(int segmentFetchTimeoutMillis, int eventDispatchTimeoutMillis) {
44+
httpClientSegments = OptimizelyHttpClient.builder().setTimeoutMillis(segmentFetchTimeoutMillis).build();
45+
if (segmentFetchTimeoutMillis == eventDispatchTimeoutMillis) {
46+
// If the timeouts are same, single httpClient can be used for both.
47+
httpClientEvents = httpClientSegments;
48+
} else {
49+
httpClientEvents = OptimizelyHttpClient.builder().setTimeoutMillis(eventDispatchTimeoutMillis).build();
50+
}
51+
}
52+
4253
@VisibleForTesting
4354
DefaultODPApiManager(OptimizelyHttpClient httpClient) {
44-
this.httpClient = httpClient;
55+
this.httpClientSegments = httpClient;
56+
this.httpClientEvents = httpClient;
4557
}
4658

4759
@VisibleForTesting
@@ -150,7 +162,7 @@ public String fetchQualifiedSegments(String apiKey, String apiEndpoint, String u
150162

151163
CloseableHttpResponse response = null;
152164
try {
153-
response = httpClient.execute(request);
165+
response = httpClientSegments.execute(request);
154166
} catch (IOException e) {
155167
logger.error("Error retrieving response from ODP service", e);
156168
return null;
@@ -211,7 +223,7 @@ public Integer sendEvents(String apiKey, String apiEndpoint, String eventPayload
211223

212224
CloseableHttpResponse response = null;
213225
try {
214-
response = httpClient.execute(request);
226+
response = httpClientEvents.execute(request);
215227
} catch (IOException e) {
216228
logger.error("Error retrieving response from event request", e);
217229
return 0;

core-httpclient-impl/src/test/java/com/optimizely/ab/odp/DefaultODPApiManagerTest.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.junit.Test;
2929
import org.mockito.ArgumentCaptor;
3030

31-
import java.util.ArrayList;
3231
import java.util.Arrays;
3332
import java.util.HashSet;
3433

@@ -129,4 +128,20 @@ public void eventDispatchFailStatus() throws Exception {
129128
apiManager.sendEvents("testKey", "testEndpoint", "[]]");
130129
logbackVerifier.expectMessage(Level.ERROR, "ODP event send failed (Response code: 400, null)");
131130
}
131+
132+
@Test
133+
public void apiTimeouts() {
134+
// Default timeout is 10 seconds
135+
new DefaultODPApiManager();
136+
logbackVerifier.expectMessage(Level.DEBUG, "Creating HttpClient with timeout: 10000", 1);
137+
138+
// Same timeouts result in single httpclient
139+
new DefaultODPApiManager(2222, 2222);
140+
logbackVerifier.expectMessage(Level.DEBUG, "Creating HttpClient with timeout: 2222", 1);
141+
142+
// Different timeouts result in different HttpClients
143+
new DefaultODPApiManager(3333, 4444);
144+
logbackVerifier.expectMessage(Level.DEBUG, "Creating HttpClient with timeout: 3333", 1);
145+
logbackVerifier.expectMessage(Level.DEBUG, "Creating HttpClient with timeout: 4444", 1);
146+
}
132147
}

0 commit comments

Comments
 (0)