Skip to content

Commit 77daa2f

Browse files
authored
Merge pull request #1820 from marklogic/feature/retry-listener
MLE-23230 Trying out retry interceptor
2 parents f21157b + 75819de commit 77daa2f

File tree

2 files changed

+91
-5
lines changed

2 files changed

+91
-5
lines changed

marklogic-client-api/src/main/java/com/marklogic/client/impl/okhttp/OkHttpUtil.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,7 @@
77
import com.marklogic.client.impl.HTTPKerberosAuthInterceptor;
88
import com.marklogic.client.impl.HTTPSamlAuthInterceptor;
99
import com.marklogic.client.impl.SSLUtil;
10-
import okhttp3.ConnectionPool;
11-
import okhttp3.CookieJar;
12-
import okhttp3.Dns;
13-
import okhttp3.Interceptor;
14-
import okhttp3.OkHttpClient;
10+
import okhttp3.*;
1511

1612
import javax.net.SocketFactory;
1713
import javax.net.ssl.HostnameVerifier;
@@ -82,6 +78,9 @@ public static OkHttpClient.Builder newOkHttpClientBuilder(String host, DatabaseC
8278
OkHttpUtil.configureSocketFactory(clientBuilder, sslContext, trustManager);
8379
OkHttpUtil.configureHostnameVerifier(clientBuilder, sslVerifier);
8480

81+
// Trying this out for all calls initially to see how the regression test piplines do.
82+
clientBuilder.addInterceptor(new RetryInterceptor(3, 1000, 2, 8000));
83+
8584
return clientBuilder;
8685
}
8786

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (c) 2010-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3+
*/
4+
package com.marklogic.client.impl.okhttp;
5+
6+
import okhttp3.Interceptor;
7+
import okhttp3.Request;
8+
import okhttp3.Response;
9+
import org.slf4j.Logger;
10+
11+
import java.io.IOException;
12+
import java.net.ConnectException;
13+
import java.net.SocketTimeoutException;
14+
import java.net.UnknownHostException;
15+
16+
/**
17+
* OkHttp interceptor that retries requests on certain connection failures,
18+
* which can be helpful when MarkLogic is temporarily unavailable during restarts.
19+
*/
20+
class RetryInterceptor implements Interceptor {
21+
22+
private final static Logger logger = org.slf4j.LoggerFactory.getLogger(RetryInterceptor.class);
23+
24+
private final int maxRetries;
25+
private final long initialDelayMs;
26+
private final double backoffMultiplier;
27+
private final long maxDelayMs;
28+
29+
RetryInterceptor(int maxRetries, long initialDelayMs, double backoffMultiplier, long maxDelayMs) {
30+
this.maxRetries = maxRetries;
31+
this.initialDelayMs = initialDelayMs;
32+
this.backoffMultiplier = backoffMultiplier;
33+
this.maxDelayMs = maxDelayMs;
34+
}
35+
36+
@Override
37+
public Response intercept(Chain chain) throws IOException {
38+
Request request = chain.request();
39+
IOException lastException = null;
40+
41+
for (int attempt = 0; attempt <= maxRetries; attempt++) {
42+
try {
43+
return chain.proceed(request);
44+
} catch (IOException e) {
45+
lastException = e;
46+
47+
if (attempt == maxRetries || !isRetryableException(e)) {
48+
logger.warn("Not retryable: {}; {}", e.getClass(), e.getMessage());
49+
throw e;
50+
}
51+
52+
long delay = calculateDelay(attempt);
53+
logger.warn("Request to {} failed (attempt {}/{}): {}. Retrying in {}ms",
54+
request.url(), attempt + 1, maxRetries, e.getMessage(), delay);
55+
56+
sleep(delay);
57+
}
58+
}
59+
60+
throw lastException;
61+
}
62+
63+
private boolean isRetryableException(IOException e) {
64+
return e instanceof ConnectException ||
65+
e instanceof SocketTimeoutException ||
66+
e instanceof UnknownHostException ||
67+
(e.getMessage() != null && (
68+
e.getMessage().contains("Failed to connect") ||
69+
e.getMessage().contains("unexpected end of stream") ||
70+
e.getMessage().contains("Connection reset") ||
71+
e.getMessage().contains("Read timed out")
72+
));
73+
}
74+
75+
private long calculateDelay(int attempt) {
76+
long delay = (long) (initialDelayMs * Math.pow(backoffMultiplier, attempt));
77+
return Math.min(delay, maxDelayMs);
78+
}
79+
80+
private void sleep(long delay) {
81+
try {
82+
Thread.sleep(delay);
83+
} catch (InterruptedException ie) {
84+
logger.warn("Ignoring InterruptedException while sleeping for retry delay: {}", ie.getMessage());
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)