Skip to content

Commit 83eeb10

Browse files
committed
CSOT: Added timeout to settings & connection string (mongodb#1173)
JAVA-4064
1 parent 5ded734 commit 83eeb10

File tree

10 files changed

+265
-21
lines changed

10 files changed

+265
-21
lines changed

driver-core/src/main/com/mongodb/ConnectionString.java

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,11 @@
122122
* <li>{@code sslInvalidHostNameAllowed=true|false}: Whether to allow invalid host names for TLS connections.</li>
123123
* <li>{@code tlsAllowInvalidHostnames=true|false}: Whether to allow invalid host names for TLS connections. Supersedes the
124124
* sslInvalidHostNameAllowed option</li>
125+
* <li>{@code timeoutMS=ms}: Time limit for the full execution of an operation.</li>
125126
* <li>{@code connectTimeoutMS=ms}: How long a connection can take to be opened before timing out.</li>
126127
* <li>{@code socketTimeoutMS=ms}: How long a receive on a socket can take before timing out.
127-
* This option is the same as {@link SocketSettings#getReadTimeout(TimeUnit)}.</li>
128+
* This option is the same as {@link SocketSettings#getReadTimeout(TimeUnit)}.
129+
* Deprecated, use {@code timeoutMS} instead.</li>
128130
* <li>{@code maxIdleTimeMS=ms}: Maximum idle time of a pooled connection. A connection that exceeds this limit will be closed</li>
129131
* <li>{@code maxLifeTimeMS=ms}: Maximum life time of a pooled connection. A connection that exceeds this limit will be closed</li>
130132
* </ul>
@@ -133,7 +135,7 @@
133135
* <li>{@code maxPoolSize=n}: The maximum number of connections in the connection pool.</li>
134136
* <li>{@code minPoolSize=n}: The minimum number of connections in the connection pool.</li>
135137
* <li>{@code waitQueueTimeoutMS=ms}: The maximum wait time in milliseconds that a thread may wait for a connection to
136-
* become available.</li>
138+
* become available. Deprecated, use {@code timeoutMS} instead.</li>
137139
* <li>{@code maxConnecting=n}: The maximum number of connections a pool may be establishing concurrently.</li>
138140
* </ul>
139141
* <p>Write concern configuration:</p>
@@ -161,7 +163,7 @@
161163
* <li>{@code wtimeoutMS=ms}
162164
* <ul>
163165
* <li>The driver adds { wtimeout : ms } to all write commands. Implies {@code safe=true}.</li>
164-
* <li>Used in combination with {@code w}</li>
166+
* <li>Used in combination with {@code w}. Deprecated, use {@code timeoutMS} instead</li>
165167
* </ul>
166168
* </li>
167169
* </ul>
@@ -280,6 +282,7 @@ public class ConnectionString {
280282
private Integer maxConnectionLifeTime;
281283
private Integer maxConnecting;
282284
private Integer connectTimeout;
285+
private Long timeout;
283286
private Integer socketTimeout;
284287
private Boolean sslEnabled;
285288
private Boolean sslInvalidHostnameAllowed;
@@ -463,6 +466,7 @@ public ConnectionString(final String connectionString, @Nullable final DnsClient
463466

464467
credential = createCredentials(combinedOptionsMaps, userName, password);
465468
warnOnUnsupportedOptions(combinedOptionsMaps);
469+
warnDeprecatedTimeouts(combinedOptionsMaps);
466470
}
467471

468472
private static final Set<String> GENERAL_OPTIONS_KEYS = new LinkedHashSet<>();
@@ -471,16 +475,18 @@ public ConnectionString(final String connectionString, @Nullable final DnsClient
471475
private static final Set<String> WRITE_CONCERN_KEYS = new HashSet<>();
472476
private static final Set<String> COMPRESSOR_KEYS = new HashSet<>();
473477
private static final Set<String> ALL_KEYS = new HashSet<>();
478+
private static final Set<String> DEPRECATED_TIMEOUT_KEYS = new HashSet<>();
474479

475480
static {
476481
GENERAL_OPTIONS_KEYS.add("minpoolsize");
477482
GENERAL_OPTIONS_KEYS.add("maxpoolsize");
483+
GENERAL_OPTIONS_KEYS.add("timeoutms");
484+
GENERAL_OPTIONS_KEYS.add("sockettimeoutms");
478485
GENERAL_OPTIONS_KEYS.add("waitqueuetimeoutms");
479486
GENERAL_OPTIONS_KEYS.add("connecttimeoutms");
480487
GENERAL_OPTIONS_KEYS.add("maxidletimems");
481488
GENERAL_OPTIONS_KEYS.add("maxlifetimems");
482489
GENERAL_OPTIONS_KEYS.add("maxconnecting");
483-
GENERAL_OPTIONS_KEYS.add("sockettimeoutms");
484490

485491
// Order matters here: Having tls after ssl means than the tls option will supersede the ssl option when both are set
486492
GENERAL_OPTIONS_KEYS.add("ssl");
@@ -536,6 +542,10 @@ public ConnectionString(final String connectionString, @Nullable final DnsClient
536542
ALL_KEYS.addAll(READ_PREFERENCE_KEYS);
537543
ALL_KEYS.addAll(WRITE_CONCERN_KEYS);
538544
ALL_KEYS.addAll(COMPRESSOR_KEYS);
545+
546+
DEPRECATED_TIMEOUT_KEYS.add("sockettimeoutms");
547+
DEPRECATED_TIMEOUT_KEYS.add("waitqueuetimeoutms");
548+
DEPRECATED_TIMEOUT_KEYS.add("wtimeoutms");
539549
}
540550

541551
// Any options contained in the connection string completely replace the corresponding options specified in TXT records,
@@ -549,15 +559,23 @@ private Map<String, List<String>> combineOptionsMaps(final Map<String, List<Stri
549559

550560

551561
private void warnOnUnsupportedOptions(final Map<String, List<String>> optionsMap) {
552-
for (final String key : optionsMap.keySet()) {
553-
if (!ALL_KEYS.contains(key)) {
554-
if (LOGGER.isWarnEnabled()) {
555-
LOGGER.warn(format("Connection string contains unsupported option '%s'.", key));
556-
}
557-
}
562+
if (LOGGER.isWarnEnabled()) {
563+
optionsMap.keySet()
564+
.stream()
565+
.filter(k -> !ALL_KEYS.contains(k))
566+
.forEach(k -> LOGGER.warn(format("Connection string contains unsupported option '%s'.", k)));
567+
}
568+
}
569+
private void warnDeprecatedTimeouts(final Map<String, List<String>> optionsMap) {
570+
if (LOGGER.isWarnEnabled()) {
571+
optionsMap.keySet()
572+
.stream()
573+
.filter(DEPRECATED_TIMEOUT_KEYS::contains)
574+
.forEach(k -> LOGGER.warn(format("Use of deprecated timeout option: '%s'. Prefer 'timeoutMS' instead.", k)));
558575
}
559576
}
560577

578+
561579
private void translateOptions(final Map<String, List<String>> optionsMap) {
562580
boolean tlsInsecureSet = false;
563581
boolean tlsAllowInvalidHostnamesSet = false;
@@ -592,6 +610,9 @@ private void translateOptions(final Map<String, List<String>> optionsMap) {
592610
case "sockettimeoutms":
593611
socketTimeout = parseInteger(value, "sockettimeoutms");
594612
break;
613+
case "timeoutms":
614+
timeout = parseLong(value, "timeoutms");
615+
break;
595616
case "tlsallowinvalidhostnames":
596617
sslInvalidHostnameAllowed = parseBoolean(value, "tlsAllowInvalidHostnames");
597618
tlsAllowInvalidHostnamesSet = true;
@@ -1100,6 +1121,15 @@ private int parseInteger(final String input, final String key) {
11001121
}
11011122
}
11021123

1124+
private long parseLong(final String input, final String key) {
1125+
try {
1126+
return Long.parseLong(input);
1127+
} catch (NumberFormatException e) {
1128+
throw new IllegalArgumentException(format("The connection string contains an invalid value for '%s'. "
1129+
+ "'%s' is not a valid long", key, input));
1130+
}
1131+
}
1132+
11031133
private List<String> parseHosts(final List<String> rawHosts) {
11041134
if (rawHosts.size() == 0){
11051135
throw new IllegalArgumentException("The connection string must contain at least one host");
@@ -1412,6 +1442,37 @@ public Integer getMaxConnecting() {
14121442
return maxConnecting;
14131443
}
14141444

1445+
/**
1446+
* The time limit for the full execution of an operation in milliseconds.
1447+
*
1448+
* <p>If set the following deprecated options will be ignored:
1449+
* {@code waitQueueTimeoutMS}, {@code socketTimeoutMS}, {@code wTimeoutMS}, {@code maxTimeMS} and {@code maxCommitTimeMS}</p>
1450+
*
1451+
* <ul>
1452+
* <li>{@code null} means that the timeout mechanism for operations will defer to using:
1453+
* <ul>
1454+
* <li>{@code waitQueueTimeoutMS}: The maximum wait time in milliseconds that a thread may wait for a connection to become
1455+
* available</li>
1456+
* <li>{@code socketTimeoutMS}: How long a send or receive on a socket can take before timing out.</li>
1457+
* <li>{@code wTimeoutMS}: How long the server will wait for the write concern to be fulfilled before timing out.</li>
1458+
* <li>{@code maxTimeMS}: The cumulative time limit for processing operations on a cursor.
1459+
* See: <a href="https://docs.mongodb.com/manual/reference/method/cursor.maxTimeMS">cursor.maxTimeMS</a>.</li>
1460+
* <li>{@code maxCommitTimeMS}: The maximum amount of time to allow a single {@code commitTransaction} command to execute.
1461+
* See: {@link TransactionOptions#getMaxCommitTime}.</li>
1462+
* </ul>
1463+
* </li>
1464+
* <li>{@code 0} means infinite timeout.</li>
1465+
* <li>{@code > 0} The time limit to use for the full execution of an operation.</li>
1466+
* </ul>
1467+
*
1468+
* @return the time limit for the full execution of an operation in milliseconds or null.
1469+
* @since 4.x
1470+
*/
1471+
@Nullable
1472+
public Long getTimeout() {
1473+
return timeout;
1474+
}
1475+
14151476
/**
14161477
* Gets the socket connect timeout specified in the connection string.
14171478
* @return the socket connect timeout
@@ -1559,6 +1620,7 @@ public boolean equals(final Object o) {
15591620
&& Objects.equals(maxConnectionLifeTime, that.maxConnectionLifeTime)
15601621
&& Objects.equals(maxConnecting, that.maxConnecting)
15611622
&& Objects.equals(connectTimeout, that.connectTimeout)
1623+
&& Objects.equals(timeout, that.timeout)
15621624
&& Objects.equals(socketTimeout, that.socketTimeout)
15631625
&& Objects.equals(sslEnabled, that.sslEnabled)
15641626
&& Objects.equals(sslInvalidHostnameAllowed, that.sslInvalidHostnameAllowed)
@@ -1577,7 +1639,7 @@ public boolean equals(final Object o) {
15771639
public int hashCode() {
15781640
return Objects.hash(credential, isSrvProtocol, hosts, database, collection, directConnection, readPreference,
15791641
writeConcern, retryWrites, retryReads, readConcern, minConnectionPoolSize, maxConnectionPoolSize, maxWaitTime,
1580-
maxConnectionIdleTime, maxConnectionLifeTime, maxConnecting, connectTimeout, socketTimeout, sslEnabled,
1642+
maxConnectionIdleTime, maxConnectionLifeTime, maxConnecting, connectTimeout, timeout, socketTimeout, sslEnabled,
15811643
sslInvalidHostnameAllowed, requiredReplicaSetName, serverSelectionTimeout, localThreshold, heartbeatFrequency,
15821644
applicationName, compressorList, uuidRepresentation, srvServiceName, srvMaxHosts);
15831645
}

driver-core/src/main/com/mongodb/MongoClientSettings.java

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@
4949
import java.util.Collections;
5050
import java.util.List;
5151
import java.util.Objects;
52+
import java.util.concurrent.TimeUnit;
5253

54+
import static com.mongodb.assertions.Assertions.isTrue;
5355
import static com.mongodb.assertions.Assertions.isTrueArgument;
5456
import static com.mongodb.assertions.Assertions.notNull;
5557
import static java.util.Arrays.asList;
@@ -111,6 +113,8 @@ public final class MongoClientSettings {
111113
private final ContextProvider contextProvider;
112114
private final DnsClient dnsClient;
113115
private final InetAddressResolver inetAddressResolver;
116+
@Nullable
117+
private final Long timeoutMS;
114118

115119
/**
116120
* Gets the default codec registry. It includes the following providers:
@@ -228,6 +232,7 @@ public static final class Builder {
228232

229233
private int heartbeatConnectTimeoutMS;
230234
private int heartbeatSocketTimeoutMS;
235+
private Long timeoutMS;
231236

232237
private ContextProvider contextProvider;
233238
private DnsClient dnsClient;
@@ -251,6 +256,7 @@ private Builder(final MongoClientSettings settings) {
251256
uuidRepresentation = settings.getUuidRepresentation();
252257
serverApi = settings.getServerApi();
253258
dnsClient = settings.getDnsClient();
259+
timeoutMS = settings.getTimeout(MILLISECONDS);
254260
inetAddressResolver = settings.getInetAddressResolver();
255261
streamFactoryFactory = settings.getStreamFactoryFactory();
256262
autoEncryptionSettings = settings.getAutoEncryptionSettings();
@@ -313,6 +319,9 @@ public Builder applyConnectionString(final ConnectionString connectionString) {
313319
if (connectionString.getWriteConcern() != null) {
314320
writeConcern = connectionString.getWriteConcern();
315321
}
322+
if (connectionString.getTimeout() != null) {
323+
timeoutMS = connectionString.getTimeout();
324+
}
316325
return this;
317326
}
318327

@@ -667,6 +676,40 @@ public Builder inetAddressResolver(@Nullable final InetAddressResolver inetAddre
667676
return this;
668677
}
669678

679+
680+
/**
681+
* Sets the time limit for the full execution of an operation.
682+
*
683+
* <ul>
684+
* <li>{@code null} means that the timeout mechanism for operations will defer to using:
685+
* <ul>
686+
* <li>{@code waitQueueTimeoutMS}: The maximum wait time in milliseconds that a thread may wait for a connection to become
687+
* available</li>
688+
* <li>{@code socketTimeoutMS}: How long a send or receive on a socket can take before timing out.</li>
689+
* <li>{@code wTimeoutMS}: How long the server will wait for the write concern to be fulfilled before timing out.</li>
690+
* <li>{@code maxTimeMS}: The cumulative time limit for processing operations on a cursor.
691+
* See: <a href="https://docs.mongodb.com/manual/reference/method/cursor.maxTimeMS">cursor.maxTimeMS</a>.</li>
692+
* <li>{@code maxCommitTimeMS}: The maximum amount of time to allow a single {@code commitTransaction} command to execute.
693+
* See: {@link TransactionOptions#getMaxCommitTime}.</li>
694+
* </ul>
695+
* </li>
696+
* <li>{@code 0} means infinite timeout.</li>
697+
* <li>{@code > 0} The time limit to use for the full execution of an operation.</li>
698+
* </ul>
699+
*
700+
* @param timeout the timeout
701+
* @param timeUnit the time unit
702+
* @return this
703+
* @since 4.x
704+
* @see #getTimeout
705+
*/
706+
public Builder timeout(final long timeout, final TimeUnit timeUnit) {
707+
isTrueArgument("timeoutMS must be >= 0", timeout >= 0);
708+
this.timeoutMS = MILLISECONDS.convert(timeout, timeUnit);
709+
return this;
710+
}
711+
712+
670713
// Package-private to provide interop with MongoClientOptions
671714
Builder heartbeatConnectTimeoutMS(final int heartbeatConnectTimeoutMS) {
672715
this.heartbeatConnectTimeoutMS = heartbeatConnectTimeoutMS;
@@ -845,6 +888,38 @@ public ServerApi getServerApi() {
845888
return serverApi;
846889
}
847890

891+
/**
892+
* The time limit for the full execution of an operation.
893+
*
894+
* <p>If set the following deprecated options will be ignored:
895+
* {@code waitQueueTimeoutMS}, {@code socketTimeoutMS}, {@code wTimeoutMS}, {@code maxTimeMS} and {@code maxCommitTimeMS}</p>
896+
*
897+
* <ul>
898+
* <li>{@code null} means that the timeout mechanism for operations will defer to using:
899+
* <ul>
900+
* <li>{@code waitQueueTimeoutMS}: The maximum wait time in milliseconds that a thread may wait for a connection to become
901+
* available</li>
902+
* <li>{@code socketTimeoutMS}: How long a send or receive on a socket can take before timing out.</li>
903+
* <li>{@code wTimeoutMS}: How long the server will wait for the write concern to be fulfilled before timing out.</li>
904+
* <li>{@code maxTimeMS}: The cumulative time limit for processing operations on a cursor.
905+
* See: <a href="https://docs.mongodb.com/manual/reference/method/cursor.maxTimeMS">cursor.maxTimeMS</a>.</li>
906+
* <li>{@code maxCommitTimeMS}: The maximum amount of time to allow a single {@code commitTransaction} command to execute.
907+
* See: {@link TransactionOptions#getMaxCommitTime}.</li>
908+
* </ul>
909+
* </li>
910+
* <li>{@code 0} means infinite timeout.</li>
911+
* <li>{@code > 0} The time limit to use for the full execution of an operation.</li>
912+
* </ul>
913+
*
914+
* @param timeUnit the time unit
915+
* @return the timeout in the given time unit
916+
* @since 4.x
917+
*/
918+
@Nullable
919+
public Long getTimeout(final TimeUnit timeUnit) {
920+
return timeoutMS == null ? null : timeUnit.convert(timeoutMS, MILLISECONDS);
921+
}
922+
848923
/**
849924
* Gets the auto-encryption settings.
850925
* <p>
@@ -995,7 +1070,8 @@ public boolean equals(final Object o) {
9951070
&& Objects.equals(autoEncryptionSettings, that.autoEncryptionSettings)
9961071
&& Objects.equals(dnsClient, that.dnsClient)
9971072
&& Objects.equals(inetAddressResolver, that.inetAddressResolver)
998-
&& Objects.equals(contextProvider, that.contextProvider);
1073+
&& Objects.equals(contextProvider, that.contextProvider)
1074+
&& Objects.equals(timeoutMS, that.timeoutMS);
9991075
}
10001076

10011077
@Override
@@ -1004,7 +1080,7 @@ public int hashCode() {
10041080
commandListeners, codecRegistry, loggerSettings, clusterSettings, socketSettings, heartbeatSocketSettings,
10051081
connectionPoolSettings, serverSettings, sslSettings, applicationName, compressorList, uuidRepresentation, serverApi,
10061082
autoEncryptionSettings, heartbeatSocketTimeoutSetExplicitly, heartbeatConnectTimeoutSetExplicitly, dnsClient,
1007-
inetAddressResolver, contextProvider);
1083+
inetAddressResolver, contextProvider, timeoutMS);
10081084
}
10091085

10101086
@Override
@@ -1034,10 +1110,12 @@ public String toString() {
10341110
+ ", dnsClient=" + dnsClient
10351111
+ ", inetAddressResolver=" + inetAddressResolver
10361112
+ ", contextProvider=" + contextProvider
1113+
+ ", timeoutMS=" + timeoutMS
10371114
+ '}';
10381115
}
10391116

10401117
private MongoClientSettings(final Builder builder) {
1118+
isTrue("timeoutMS > 0 ", builder.timeoutMS == null || builder.timeoutMS >= 0);
10411119
readPreference = builder.readPreference;
10421120
writeConcern = builder.writeConcern;
10431121
retryWrites = builder.retryWrites;
@@ -1071,5 +1149,6 @@ private MongoClientSettings(final Builder builder) {
10711149
heartbeatSocketTimeoutSetExplicitly = builder.heartbeatSocketTimeoutMS != 0;
10721150
heartbeatConnectTimeoutSetExplicitly = builder.heartbeatConnectTimeoutMS != 0;
10731151
contextProvider = builder.contextProvider;
1152+
timeoutMS = builder.timeoutMS;
10741153
}
10751154
}

0 commit comments

Comments
 (0)