Skip to content

Commit 372322a

Browse files
committed
Added retry on no reponse exception
1 parent 247f322 commit 372322a

File tree

3 files changed

+87
-9
lines changed

3 files changed

+87
-9
lines changed

clickhouse-http-client/src/main/java/com/clickhouse/client/http/ApacheHttpConnectionImpl.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.clickhouse.client.AbstractSocketClient;
44
import com.clickhouse.client.ClickHouseClient;
55
import com.clickhouse.client.ClickHouseConfig;
6-
import com.clickhouse.client.ClickHouseException;
76
import com.clickhouse.client.ClickHouseNode;
87
import com.clickhouse.client.ClickHouseRequest;
98
import com.clickhouse.client.ClickHouseSocketFactory;
@@ -34,6 +33,7 @@
3433
import org.apache.hc.core5.http.Header;
3534
import org.apache.hc.core5.http.HttpHost;
3635
import org.apache.hc.core5.http.HttpRequest;
36+
import org.apache.hc.core5.http.NoHttpResponseException;
3737
import org.apache.hc.core5.http.config.Registry;
3838
import org.apache.hc.core5.http.config.RegistryBuilder;
3939
import org.apache.hc.core5.http.io.SocketConfig;
@@ -45,7 +45,6 @@
4545

4646
import javax.net.ssl.SSLContext;
4747
import javax.net.ssl.SSLException;
48-
4948
import java.io.BufferedReader;
5049
import java.io.ByteArrayInputStream;
5150
import java.io.ByteArrayOutputStream;
@@ -58,7 +57,6 @@
5857
import java.net.HttpURLConnection;
5958
import java.net.InetSocketAddress;
6059
import java.net.Socket;
61-
import java.net.StandardSocketOptions;
6260
import java.nio.charset.StandardCharsets;
6361
import java.util.Collections;
6462
import java.util.List;
@@ -251,11 +249,28 @@ protected ClickHouseHttpResponse post(ClickHouseConfig config, String sql, Click
251249
ClickHouseHttpEntity postBody = new ClickHouseHttpEntity(config, contentType, contentEncoding, boundary,
252250
sql, data, tables);
253251
post.setEntity(postBody);
254-
CloseableHttpResponse response;
255-
try {
256-
response = client.execute(post);
257-
} catch (IOException e) {
258-
throw new ConnectException(ClickHouseUtils.format("HTTP request failed: %s", e.getMessage()));
252+
CloseableHttpResponse response = null;
253+
254+
for (int attempt = 0; attempt < 2; attempt++) {
255+
log.debug("HTTP request attempt {}", attempt);
256+
try {
257+
response = client.execute(post);
258+
break;
259+
} catch (NoHttpResponseException e) {
260+
log.warn("HTTP request failed: ", e.getMessage());
261+
if (attempt > 0) {
262+
throw new ConnectException(e.getMessage());
263+
} else {
264+
continue;
265+
}
266+
} catch (IOException e) {
267+
log.error("HTTP request failed", e);
268+
throw new ConnectException(e.getMessage());
269+
}
270+
}
271+
if (response == null) {
272+
// Should not happen but needed for compiler
273+
throw new ConnectException("HTTP request failed");
259274
}
260275

261276
checkResponse(config, response);
@@ -376,6 +391,8 @@ public HttpConnectionManager(Registry<ConnectionSocketFactory> socketFactory, Cl
376391

377392
ConnectionConfig connConfig = ConnectionConfig.custom()
378393
.setConnectTimeout(Timeout.of(config.getConnectionTimeout(), TimeUnit.MILLISECONDS))
394+
.setTimeToLive(20, TimeUnit.SECONDS)
395+
.setValidateAfterInactivity(10, TimeUnit.SECONDS)
379396
.build();
380397
setDefaultConnectionConfig(connConfig);
381398

clickhouse-http-client/src/main/java/com/clickhouse/client/http/config/ClickHouseHttpOption.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.clickhouse.data.ClickHouseChecker;
55

66
import java.io.Serializable;
7+
import java.net.UnknownHostException;
78

89
/**
910
* Http client options.
@@ -64,7 +65,28 @@ public enum ClickHouseHttpOption implements ClickHouseOption {
6465
* Only one role can be set at a time.
6566
*/
6667
REMEMBER_LAST_SET_ROLES("remember_last_set_roles", false,
67-
"Whether to remember last set role and send them in every next requests as query parameters.");
68+
"Whether to remember last set role and send them in every next requests as query parameters."),
69+
70+
/**
71+
* Whether to retry on failure with AsyncHttpClient. Failure includes some 'critical' IO exceptions:
72+
* <ul>
73+
* <li>{@code java.net.UnknownHostException}</li>
74+
* <li>{@code org.apache.hc.core5.http.ConnectionClosedException}</li>
75+
* <li>{@code org.apache.hc.core5.http.NoHttpResponseException}</li>
76+
* </ul>
77+
*
78+
* And next status codes:
79+
* <ul>
80+
* <li>{@code 503 Service Unavailable}</li>
81+
* </ul>
82+
*/
83+
AHC_RETRY_ON_FAILURE("ahc_retry_on_failure", true, "Whether to retry on failure with AsyncHttpClient."),
84+
85+
/**
86+
* Retry interval in milliseconds for AsyncHttpClient (if {@link #AHC_RETRY_ON_FAILURE} is enabled).
87+
*/
88+
AHC_RETRY_INTERVAL("ahc_retry_interval", 100, "Retry interval in milliseconds."),
89+
;
6890

6991
private final String key;
7092
private final Serializable defaultValue;

clickhouse-http-client/src/test/java/com/clickhouse/client/http/ApacheHttpConnectionImplTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
import java.util.concurrent.atomic.AtomicBoolean;
2525

2626
import com.github.tomakehurst.wiremock.WireMockServer;
27+
import com.github.tomakehurst.wiremock.admin.model.ScenarioState;
2728
import com.github.tomakehurst.wiremock.client.WireMock;
2829
import com.github.tomakehurst.wiremock.http.Fault;
30+
import com.github.tomakehurst.wiremock.stubbing.Scenario;
2931
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
3032
import org.testng.Assert;
3133
import org.testng.annotations.Test;
@@ -144,4 +146,41 @@ public void testFailureWhileRequest() {
144146
faultyServer.stop();
145147
}
146148
}
149+
150+
@Test(groups = {"unit"})
151+
public void testRetryOnFailure() {
152+
faultyServer = new WireMockServer(9090);
153+
faultyServer.start();
154+
try {
155+
faultyServer.addStubMapping(WireMock.post(WireMock.anyUrl())
156+
.inScenario("Retry")
157+
.whenScenarioStateIs(Scenario.STARTED)
158+
.willReturn(WireMock.aResponse().withFault(Fault.EMPTY_RESPONSE))
159+
.willSetStateTo("Failed")
160+
.build());
161+
faultyServer.addStubMapping(WireMock.post(WireMock.anyUrl())
162+
.inScenario("Retry")
163+
.whenScenarioStateIs("Failed")
164+
.willReturn(WireMock.aResponse()
165+
.withHeader("X-ClickHouse-Summary",
166+
"{ \"read_bytes\": \"10\", \"read_rows\": \"1\"}"))
167+
.build());
168+
169+
ClickHouseHttpClient httpClient = new ClickHouseHttpClient();
170+
ClickHouseConfig config = new ClickHouseConfig();
171+
httpClient.init(config);
172+
ClickHouseRequest request = httpClient.read("http://localhost:9090/").query("SELECT 1");
173+
174+
ClickHouseResponse response = null;
175+
try {
176+
response = httpClient.executeAndWait(request);
177+
} catch (ClickHouseException e) {
178+
Assert.fail("Should not throw exception", e);
179+
}
180+
Assert.assertEquals(response.getSummary().getReadBytes(), 10);
181+
Assert.assertEquals(response.getSummary().getReadRows(), 1);
182+
} finally {
183+
faultyServer.stop();
184+
}
185+
}
147186
}

0 commit comments

Comments
 (0)