Skip to content

Commit ae07ec8

Browse files
authored
Expose S3 connection max idle time as a setting (#125552)
Resolves: ES-10815
1 parent 689c7ae commit ae07ec8

File tree

6 files changed

+55
-0
lines changed

6 files changed

+55
-0
lines changed

docs/changelog/125552.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 125552
2+
summary: Expose S3 connection max idle time as a setting
3+
area: Snapshot/Restore
4+
type: enhancement
5+
issues: []

modules/repository-s3/src/internalClusterTest/java/org/elasticsearch/repositories/s3/S3BlobStoreRepositoryTests.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,13 @@ protected Settings nodeSettings(int nodeOrdinal, Settings otherSettings) {
182182
if (region != null) {
183183
builder.put(S3ClientSettings.REGION.getConcreteSettingForNamespace("test").getKey(), region);
184184
}
185+
if (randomBoolean()) {
186+
// Sometimes explicitly set connection max idle time to ensure it is configurable
187+
builder.put(
188+
S3ClientSettings.CONNECTION_MAX_IDLE_TIME_SETTING.getConcreteSettingForNamespace("test").getKey(),
189+
S3ClientSettings.Defaults.CONNECTION_MAX_IDLE_TIME
190+
);
191+
}
185192
return builder.build();
186193
}
187194

modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3ClientSettings.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,12 @@ final class S3ClientSettings {
186186
key -> Setting.boolSetting(key, false, Property.NodeScope)
187187
);
188188

189+
static final Setting.AffixSetting<TimeValue> CONNECTION_MAX_IDLE_TIME_SETTING = Setting.affixKeySetting(
190+
PREFIX,
191+
"connection_max_idle_time",
192+
key -> Setting.timeSetting(key, Defaults.CONNECTION_MAX_IDLE_TIME, Property.NodeScope)
193+
);
194+
189195
/** Credentials to authenticate with s3. */
190196
final AwsCredentials credentials;
191197

@@ -215,6 +221,11 @@ final class S3ClientSettings {
215221
/** The read timeout for the s3 client. */
216222
final int readTimeoutMillis;
217223

224+
/**
225+
* The maximum idle time (in millis) of a connection before it is discarded from the connection pool.
226+
*/
227+
final long connectionMaxIdleTimeMillis;
228+
218229
/** The maximum number of concurrent connections to use. */
219230
final int maxConnections;
220231

@@ -243,6 +254,7 @@ private S3ClientSettings(
243254
String proxyUsername,
244255
String proxyPassword,
245256
int readTimeoutMillis,
257+
long connectionMaxIdleTimeMillis,
246258
int maxConnections,
247259
int maxRetries,
248260
boolean pathStyleAccess,
@@ -259,6 +271,7 @@ private S3ClientSettings(
259271
this.proxyUsername = proxyUsername;
260272
this.proxyPassword = proxyPassword;
261273
this.readTimeoutMillis = readTimeoutMillis;
274+
this.connectionMaxIdleTimeMillis = connectionMaxIdleTimeMillis;
262275
this.maxConnections = maxConnections;
263276
this.maxRetries = maxRetries;
264277
this.pathStyleAccess = pathStyleAccess;
@@ -308,12 +321,18 @@ S3ClientSettings refine(Settings repositorySettings) {
308321
newCredentials = credentials;
309322
}
310323
final String newRegion = getRepoSettingOrDefault(REGION, normalizedSettings, region);
324+
final long newConnectionMaxIdleTimeMillis = getRepoSettingOrDefault(
325+
CONNECTION_MAX_IDLE_TIME_SETTING,
326+
normalizedSettings,
327+
TimeValue.timeValueMillis(connectionMaxIdleTimeMillis)
328+
).millis();
311329
if (Objects.equals(protocol, newProtocol)
312330
&& Objects.equals(endpoint, newEndpoint)
313331
&& Objects.equals(proxyHost, newProxyHost)
314332
&& proxyPort == newProxyPort
315333
&& proxyScheme == newProxyScheme
316334
&& newReadTimeoutMillis == readTimeoutMillis
335+
&& Objects.equals(connectionMaxIdleTimeMillis, newConnectionMaxIdleTimeMillis)
317336
&& maxConnections == newMaxConnections
318337
&& maxRetries == newMaxRetries
319338
&& Objects.equals(credentials, newCredentials)
@@ -333,6 +352,7 @@ S3ClientSettings refine(Settings repositorySettings) {
333352
proxyUsername,
334353
proxyPassword,
335354
newReadTimeoutMillis,
355+
newConnectionMaxIdleTimeMillis,
336356
newMaxConnections,
337357
newMaxRetries,
338358
newPathStyleAccess,
@@ -441,6 +461,7 @@ static S3ClientSettings getClientSettings(final Settings settings, final String
441461
proxyUsername.toString(),
442462
proxyPassword.toString(),
443463
Math.toIntExact(getConfigValue(settings, clientName, READ_TIMEOUT_SETTING).millis()),
464+
getConfigValue(settings, clientName, CONNECTION_MAX_IDLE_TIME_SETTING).millis(),
444465
getConfigValue(settings, clientName, MAX_CONNECTIONS_SETTING),
445466
getConfigValue(settings, clientName, MAX_RETRIES_SETTING),
446467
getConfigValue(settings, clientName, USE_PATH_STYLE_ACCESS),
@@ -462,6 +483,7 @@ public boolean equals(final Object o) {
462483
final S3ClientSettings that = (S3ClientSettings) o;
463484
return proxyPort == that.proxyPort
464485
&& readTimeoutMillis == that.readTimeoutMillis
486+
&& Objects.equals(connectionMaxIdleTimeMillis, that.connectionMaxIdleTimeMillis)
465487
&& maxConnections == that.maxConnections
466488
&& maxRetries == that.maxRetries
467489
&& Objects.equals(credentials, that.credentials)
@@ -488,6 +510,7 @@ public int hashCode() {
488510
proxyUsername,
489511
proxyPassword,
490512
readTimeoutMillis,
513+
connectionMaxIdleTimeMillis,
491514
maxRetries,
492515
maxConnections,
493516
disableChunkedEncoding,
@@ -510,6 +533,7 @@ private static <T> T getRepoSettingOrDefault(Setting.AffixSetting<T> setting, Se
510533

511534
static final class Defaults {
512535
static final TimeValue READ_TIMEOUT = TimeValue.timeValueSeconds(50);
536+
static final TimeValue CONNECTION_MAX_IDLE_TIME = TimeValue.timeValueSeconds(60);
513537
static final int MAX_CONNECTIONS = 50;
514538
static final int RETRY_COUNT = 3;
515539
}

modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3RepositoryPlugin.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ public List<Setting<?>> getSettings() {
152152
S3ClientSettings.UNUSED_SIGNER_OVERRIDE,
153153
S3ClientSettings.ADD_PURPOSE_CUSTOM_QUERY_PARAMETER,
154154
S3ClientSettings.REGION,
155+
S3ClientSettings.CONNECTION_MAX_IDLE_TIME_SETTING,
155156
S3Service.REPOSITORY_S3_CAS_TTL_SETTING,
156157
S3Service.REPOSITORY_S3_CAS_ANTI_CONTENTION_DELAY_SETTING,
157158
S3Repository.ACCESS_KEY_SETTING,

modules/repository-s3/src/main/java/org/elasticsearch/repositories/s3/S3Service.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ private SdkHttpClient buildHttpClient(
310310

311311
httpClientBuilder.maxConnections(clientSettings.maxConnections);
312312
httpClientBuilder.socketTimeout(Duration.ofMillis(clientSettings.readTimeoutMillis));
313+
httpClientBuilder.connectionMaxIdleTime(Duration.ofMillis(clientSettings.connectionMaxIdleTimeMillis));
313314

314315
Optional<ProxyConfiguration> proxyConfiguration = buildProxyConfiguration(clientSettings);
315316
if (proxyConfiguration.isPresent()) {

modules/repository-s3/src/test/java/org/elasticsearch/repositories/s3/S3ClientSettingsTests.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.elasticsearch.common.settings.MockSecureSettings;
1919
import org.elasticsearch.common.settings.Settings;
2020
import org.elasticsearch.common.util.concurrent.DeterministicTaskQueue;
21+
import org.elasticsearch.core.TimeValue;
2122
import org.elasticsearch.env.Environment;
2223
import org.elasticsearch.test.ClusterServiceUtils;
2324
import org.elasticsearch.test.ESTestCase;
@@ -49,6 +50,7 @@ public void testThereIsADefaultClientByDefault() {
4950
assertThat(defaultSettings.readTimeoutMillis, is(Math.toIntExact(S3ClientSettings.Defaults.READ_TIMEOUT.millis())));
5051
assertThat(defaultSettings.maxConnections, is(S3ClientSettings.Defaults.MAX_CONNECTIONS));
5152
assertThat(defaultSettings.maxRetries, is(S3ClientSettings.Defaults.RETRY_COUNT));
53+
assertThat(defaultSettings.connectionMaxIdleTimeMillis, is(S3ClientSettings.Defaults.CONNECTION_MAX_IDLE_TIME.millis()));
5254
}
5355

5456
public void testDefaultClientSettingsCanBeSet() {
@@ -206,6 +208,21 @@ public void testRegionCanBeSet() {
206208
}
207209
}
208210

211+
public void testConnectionMaxIdleTimeCanBeSet() {
212+
final TimeValue connectionMaxIdleTimeValue = randomValueOtherThan(
213+
S3ClientSettings.Defaults.CONNECTION_MAX_IDLE_TIME,
214+
ESTestCase::randomTimeValue
215+
);
216+
final Map<String, S3ClientSettings> settings = S3ClientSettings.load(
217+
Settings.builder().put("s3.client.other.connection_max_idle_time", connectionMaxIdleTimeValue).build()
218+
);
219+
assertThat(settings.get("default").connectionMaxIdleTimeMillis, is(S3ClientSettings.Defaults.CONNECTION_MAX_IDLE_TIME.millis()));
220+
assertThat(settings.get("other").connectionMaxIdleTimeMillis, is(connectionMaxIdleTimeValue.millis()));
221+
222+
// the default appears in the docs so let's make sure it doesn't change:
223+
assertEquals(TimeValue.timeValueSeconds(60), S3ClientSettings.Defaults.CONNECTION_MAX_IDLE_TIME);
224+
}
225+
209226
public void testMaxConnectionsCanBeSet() {
210227
final int maxConnections = between(1, 100);
211228
final Map<String, S3ClientSettings> settings = S3ClientSettings.load(

0 commit comments

Comments
 (0)