diff --git a/README.md b/README.md
index 7d6de4dd0dc..587a248ccc7 100644
--- a/README.md
+++ b/README.md
@@ -54,6 +54,17 @@ almost always be binary compatible with prior minor releases from the same major
Patch 5.x.y increments (such as 5.0.0 -> 5.0.1, 5.1.1 -> 5.1.2, etc) will occur for bug fixes only and will always be binary compatible
with prior patch releases of the same minor release branch.
+#### @Alpha
+
+APIs marked with the `@Alpha` annotation are in the early stages of development, subject to incompatible changes,
+or even removal, in a future release and may lack some intended features. An APIs bearing `@Alpha` annotation may
+contain known issues affecting functionality, performance, and stability. They are also exempt from any compatibility
+guarantees made by its containing library.
+
+It is inadvisable for applications to use Alpha APIs in production environments or for libraries
+(which get included on users' CLASSPATHs, outside the library developers' control) to depend on these APIs. Alpha APIs
+are intended for experimental purposes only.
+
#### @Beta
APIs marked with the `@Beta` annotation at the class or method level are subject to change. They can be modified in any way, or even
diff --git a/THIRD-PARTY-NOTICES b/THIRD-PARTY-NOTICES
index 200d5d3803a..7229bf71926 100644
--- a/THIRD-PARTY-NOTICES
+++ b/THIRD-PARTY-NOTICES
@@ -37,7 +37,10 @@ https://github.com/mongodb/mongo-java-driver.
See the License for the specific language governing permissions and
limitations under the License.
-3) The following files: Beta.java
+3) The following files:
+
+ Alpha.java (formerly Beta.java)
+ Beta.java
Copyright 2010 The Guava Authors
Copyright 2011 The Guava Authors
diff --git a/bson/src/main/org/bson/assertions/Assertions.java b/bson/src/main/org/bson/assertions/Assertions.java
index 31b81a45f50..414c318122d 100644
--- a/bson/src/main/org/bson/assertions/Assertions.java
+++ b/bson/src/main/org/bson/assertions/Assertions.java
@@ -116,6 +116,19 @@ public static T assertNotNull(@Nullable final T value) throws AssertionError
return value;
}
+ /**
+ * Throw AssertionError if the condition if false.
+ *
+ * @param name the name of the state that is being checked
+ * @param condition the condition about the parameter to check
+ * @throws AssertionError if the condition is false
+ */
+ public static void assertTrue(final String name, final boolean condition) {
+ if (!condition) {
+ throw new AssertionError("state should be: " + assertNotNull(name));
+ }
+ }
+
/**
* Cast an object to the given class and return it, or throw IllegalArgumentException if it's not assignable to that class.
*
diff --git a/build.gradle b/build.gradle
index 693b514b738..50623ee32bf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -158,6 +158,7 @@ configure(scalaProjects) {
"-unchecked",
"-language:reflectiveCalls",
"-Wconf:cat=deprecation:ws,any:e",
+ "-Wconf:msg=While parsing annotations in:silent",
"-Xlint:strict-unsealed-patmat"
]
}
diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml
index 49f3fb18e9e..6d24f861e08 100644
--- a/config/checkstyle/suppressions.xml
+++ b/config/checkstyle/suppressions.xml
@@ -29,6 +29,7 @@
+
@@ -87,6 +88,7 @@
+
diff --git a/config/spotbugs/exclude.xml b/config/spotbugs/exclude.xml
index 09af427f8d9..fedf0c72566 100644
--- a/config/spotbugs/exclude.xml
+++ b/config/spotbugs/exclude.xml
@@ -229,7 +229,7 @@
-->
-
+
@@ -239,4 +239,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/driver-core/build.gradle b/driver-core/build.gradle
index 40a63c15d49..1f7d06f93f2 100644
--- a/driver-core/build.gradle
+++ b/driver-core/build.gradle
@@ -58,6 +58,7 @@ dependencies {
implementation "org.mongodb:mongodb-crypt:$mongoCryptVersion", optional
testImplementation project(':bson').sourceSets.test.output
+ testImplementation('org.junit.jupiter:junit-jupiter-api')
testRuntimeOnly "io.netty:netty-tcnative-boringssl-static"
classifiers.forEach {
diff --git a/driver-core/src/main/com/mongodb/AwsCredential.java b/driver-core/src/main/com/mongodb/AwsCredential.java
index dfd6c86776c..2fd6f8fb6f4 100644
--- a/driver-core/src/main/com/mongodb/AwsCredential.java
+++ b/driver-core/src/main/com/mongodb/AwsCredential.java
@@ -17,6 +17,7 @@
package com.mongodb;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.Nullable;
import static com.mongodb.assertions.Assertions.notNull;
@@ -28,7 +29,7 @@
* @see MongoCredential#AWS_CREDENTIAL_PROVIDER_KEY
* @since 4.4
*/
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public final class AwsCredential {
private final String accessKeyId;
private final String secretAccessKey;
diff --git a/driver-core/src/main/com/mongodb/ClientEncryptionSettings.java b/driver-core/src/main/com/mongodb/ClientEncryptionSettings.java
index 2df4b3363d4..ee9b88817e7 100644
--- a/driver-core/src/main/com/mongodb/ClientEncryptionSettings.java
+++ b/driver-core/src/main/com/mongodb/ClientEncryptionSettings.java
@@ -16,15 +16,21 @@
package com.mongodb;
+import com.mongodb.annotations.Alpha;
import com.mongodb.annotations.NotThreadSafe;
+import com.mongodb.annotations.Reason;
+import com.mongodb.lang.Nullable;
import javax.net.ssl.SSLContext;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import static com.mongodb.assertions.Assertions.notNull;
+import static com.mongodb.internal.TimeoutSettings.convertAndValidateTimeout;
import static java.util.Collections.unmodifiableMap;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
/**
* The client-side settings for data key creation and explicit encryption.
@@ -42,6 +48,8 @@ public final class ClientEncryptionSettings {
private final Map> kmsProviders;
private final Map>> kmsProviderPropertySuppliers;
private final Map kmsProviderSslContextMap;
+ @Nullable
+ private final Long timeoutMS;
/**
* A builder for {@code ClientEncryptionSettings} so that {@code ClientEncryptionSettings} can be immutable, and to support easier
* construction through chaining.
@@ -53,6 +61,8 @@ public static final class Builder {
private Map> kmsProviders;
private Map>> kmsProviderPropertySuppliers = new HashMap<>();
private Map kmsProviderSslContextMap = new HashMap<>();
+ @Nullable
+ private Long timeoutMS;
/**
* Sets the {@link MongoClientSettings} that will be used to access the key vault.
@@ -120,6 +130,43 @@ public Builder kmsProviderSslContextMap(final Map kmsProvide
return this;
}
+ /**
+ * Sets the time limit for the full execution of an operation.
+ *
+ *
+ * - {@code null} means that the timeout mechanism for operations will defer to using:
+ *
+ * - {@code waitQueueTimeoutMS}: The maximum wait time in milliseconds that a thread may wait for a connection to become
+ * available
+ * - {@code socketTimeoutMS}: How long a send or receive on a socket can take before timing out.
+ * - {@code wTimeoutMS}: How long the server will wait for the write concern to be fulfilled before timing out.
+ * - {@code maxTimeMS}: The cumulative time limit for processing operations on a cursor.
+ * See: cursor.maxTimeMS.
+ * - {@code maxCommitTimeMS}: The maximum amount of time to allow a single {@code commitTransaction} command to execute.
+ * See: {@link TransactionOptions#getMaxCommitTime}.
+ *
+ *
+ * - {@code 0} means infinite timeout.
+ * - {@code > 0} The time limit to use for the full execution of an operation.
+ *
+ *
+ * Note: The timeout set through this method overrides the timeout defined in the key vault client settings
+ * specified in {@link #keyVaultMongoClientSettings(MongoClientSettings)}.
+ * Essentially, for operations that require accessing the key vault, the remaining timeout from the initial operation
+ * determines the duration allowed for key vault access.
+ *
+ * @param timeout the timeout
+ * @param timeUnit the time unit
+ * @return this
+ * @since 5.2
+ * @see #getTimeout
+ */
+ @Alpha(Reason.CLIENT)
+ public ClientEncryptionSettings.Builder timeout(final long timeout, final TimeUnit timeUnit) {
+ this.timeoutMS = convertAndValidateTimeout(timeout, timeUnit);
+ return this;
+ }
+
/**
* Build an instance of {@code ClientEncryptionSettings}.
*
@@ -253,12 +300,46 @@ public Map getKmsProviderSslContextMap() {
return unmodifiableMap(kmsProviderSslContextMap);
}
+ /**
+ * The time limit for the full execution of an operation.
+ *
+ * If set the following deprecated options will be ignored:
+ * {@code waitQueueTimeoutMS}, {@code socketTimeoutMS}, {@code wTimeoutMS}, {@code maxTimeMS} and {@code maxCommitTimeMS}
+ *
+ *
+ * - {@code null} means that the timeout mechanism for operations will defer to using:
+ *
+ * - {@code waitQueueTimeoutMS}: The maximum wait time in milliseconds that a thread may wait for a connection to become
+ * available
+ * - {@code socketTimeoutMS}: How long a send or receive on a socket can take before timing out.
+ * - {@code wTimeoutMS}: How long the server will wait for the write concern to be fulfilled before timing out.
+ * - {@code maxTimeMS}: The cumulative time limit for processing operations on a cursor.
+ * See: cursor.maxTimeMS.
+ * - {@code maxCommitTimeMS}: The maximum amount of time to allow a single {@code commitTransaction} command to execute.
+ * See: {@link TransactionOptions#getMaxCommitTime}.
+ *
+ *
+ * - {@code 0} means infinite timeout.
+ * - {@code > 0} The time limit to use for the full execution of an operation.
+ *
+ *
+ * @param timeUnit the time unit
+ * @return the timeout in the given time unit
+ * @since 5.2
+ */
+ @Alpha(Reason.CLIENT)
+ @Nullable
+ public Long getTimeout(final TimeUnit timeUnit) {
+ return timeoutMS == null ? null : timeUnit.convert(timeoutMS, MILLISECONDS);
+ }
+
private ClientEncryptionSettings(final Builder builder) {
this.keyVaultMongoClientSettings = notNull("keyVaultMongoClientSettings", builder.keyVaultMongoClientSettings);
this.keyVaultNamespace = notNull("keyVaultNamespace", builder.keyVaultNamespace);
this.kmsProviders = notNull("kmsProviders", builder.kmsProviders);
this.kmsProviderPropertySuppliers = notNull("kmsProviderPropertySuppliers", builder.kmsProviderPropertySuppliers);
this.kmsProviderSslContextMap = notNull("kmsProviderSslContextMap", builder.kmsProviderSslContextMap);
+ this.timeoutMS = builder.timeoutMS;
}
}
diff --git a/driver-core/src/main/com/mongodb/ClientSessionOptions.java b/driver-core/src/main/com/mongodb/ClientSessionOptions.java
index 7a272016006..160d16c3486 100644
--- a/driver-core/src/main/com/mongodb/ClientSessionOptions.java
+++ b/driver-core/src/main/com/mongodb/ClientSessionOptions.java
@@ -16,14 +16,19 @@
package com.mongodb;
+import com.mongodb.annotations.Alpha;
import com.mongodb.annotations.Immutable;
import com.mongodb.annotations.NotThreadSafe;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.Nullable;
import com.mongodb.session.ClientSession;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import static com.mongodb.assertions.Assertions.notNull;
+import static com.mongodb.internal.TimeoutSettings.convertAndValidateTimeout;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
/**
* The options to apply to a {@code ClientSession}.
@@ -38,6 +43,7 @@ public final class ClientSessionOptions {
private final Boolean causallyConsistent;
private final Boolean snapshot;
+ private final Long defaultTimeoutMS;
private final TransactionOptions defaultTransactionOptions;
/**
@@ -77,6 +83,25 @@ public TransactionOptions getDefaultTransactionOptions() {
return defaultTransactionOptions;
}
+ /**
+ * Gets the default time limit for the following operations executed on the session:
+ *
+ *
+ * - {@code commitTransaction}
+ * - {@code abortTransaction}
+ * - {@code withTransaction}
+ * - {@code close}
+ *
+ * @param timeUnit the time unit
+ * @return the default timeout
+ * @since 5.2
+ */
+ @Alpha(Reason.CLIENT)
+ @Nullable
+ public Long getDefaultTimeout(final TimeUnit timeUnit) {
+ return defaultTimeoutMS == null ? null : timeUnit.convert(defaultTimeoutMS, MILLISECONDS);
+ }
+
@Override
public boolean equals(final Object o) {
if (this == o) {
@@ -85,36 +110,24 @@ public boolean equals(final Object o) {
if (o == null || getClass() != o.getClass()) {
return false;
}
-
- ClientSessionOptions that = (ClientSessionOptions) o;
-
- if (!Objects.equals(causallyConsistent, that.causallyConsistent)) {
- return false;
- }
-
- if (!Objects.equals(snapshot, that.snapshot)) {
- return false;
- }
- if (!Objects.equals(defaultTransactionOptions, that.defaultTransactionOptions)) {
- return false;
- }
-
- return true;
+ final ClientSessionOptions that = (ClientSessionOptions) o;
+ return Objects.equals(causallyConsistent, that.causallyConsistent)
+ && Objects.equals(snapshot, that.snapshot)
+ && Objects.equals(defaultTimeoutMS, that.defaultTimeoutMS)
+ && Objects.equals(defaultTransactionOptions, that.defaultTransactionOptions);
}
@Override
public int hashCode() {
- int result = causallyConsistent != null ? causallyConsistent.hashCode() : 0;
- result = 31 * result + (snapshot != null ? snapshot.hashCode() : 0);
- result = 31 * result + (defaultTransactionOptions != null ? defaultTransactionOptions.hashCode() : 0);
- return result;
+ return Objects.hash(causallyConsistent, snapshot, defaultTimeoutMS, defaultTransactionOptions);
}
@Override
public String toString() {
return "ClientSessionOptions{"
+ "causallyConsistent=" + causallyConsistent
- + "snapshot=" + snapshot
+ + ", snapshot=" + snapshot
+ + ", defaultTimeoutMS=" + defaultTimeoutMS
+ ", defaultTransactionOptions=" + defaultTransactionOptions
+ '}';
}
@@ -141,6 +154,7 @@ public static Builder builder(final ClientSessionOptions options) {
builder.causallyConsistent = options.isCausallyConsistent();
builder.snapshot = options.isSnapshot();
builder.defaultTransactionOptions = options.getDefaultTransactionOptions();
+ builder.defaultTimeoutMS = options.defaultTimeoutMS;
return builder;
}
@@ -151,6 +165,7 @@ public static Builder builder(final ClientSessionOptions options) {
public static final class Builder {
private Boolean causallyConsistent;
private Boolean snapshot;
+ private Long defaultTimeoutMS;
private TransactionOptions defaultTransactionOptions = TransactionOptions.builder().build();
/**
@@ -196,6 +211,27 @@ public Builder defaultTransactionOptions(final TransactionOptions defaultTransac
return this;
}
+ /**
+ * Sets the default time limit for the following operations executed on the session:
+ *
+ *
+ * - {@code commitTransaction}
+ * - {@code abortTransaction}
+ * - {@code withTransaction}
+ * - {@code close}
+ *
+ * @param defaultTimeout the timeout
+ * @param timeUnit the time unit
+ * @return this
+ * @since 5.2
+ * @see #getDefaultTimeout
+ */
+ @Alpha(Reason.CLIENT)
+ public Builder defaultTimeout(final long defaultTimeout, final TimeUnit timeUnit) {
+ this.defaultTimeoutMS = convertAndValidateTimeout(defaultTimeout, timeUnit, "defaultTimeout");
+ return this;
+ }
+
/**
* Build the session options instance.
*
@@ -218,5 +254,6 @@ private ClientSessionOptions(final Builder builder) {
: Boolean.valueOf(!builder.snapshot);
this.snapshot = builder.snapshot;
this.defaultTransactionOptions = builder.defaultTransactionOptions;
+ this.defaultTimeoutMS = builder.defaultTimeoutMS;
}
}
diff --git a/driver-core/src/main/com/mongodb/ConnectionString.java b/driver-core/src/main/com/mongodb/ConnectionString.java
index 17a990ea127..f779ab7290d 100644
--- a/driver-core/src/main/com/mongodb/ConnectionString.java
+++ b/driver-core/src/main/com/mongodb/ConnectionString.java
@@ -16,6 +16,8 @@
package com.mongodb;
+import com.mongodb.annotations.Alpha;
+import com.mongodb.annotations.Reason;
import com.mongodb.connection.ClusterSettings;
import com.mongodb.connection.ConnectionPoolSettings;
import com.mongodb.connection.ServerMonitoringMode;
@@ -139,9 +141,12 @@
* {@code sslInvalidHostNameAllowed=true|false}: Whether to allow invalid host names for TLS connections.
* {@code tlsAllowInvalidHostnames=true|false}: Whether to allow invalid host names for TLS connections. Supersedes the
* sslInvalidHostNameAllowed option
+ * {@code timeoutMS=ms}: Time limit for the full execution of an operation. Note: This parameter is part of an {@linkplain Alpha Alpha API} and may be
+ * subject to changes or even removal in future releases.
* {@code connectTimeoutMS=ms}: How long a connection can take to be opened before timing out.
* {@code socketTimeoutMS=ms}: How long a receive on a socket can take before timing out.
- * This option is the same as {@link SocketSettings#getReadTimeout(TimeUnit)}.
+ * This option is the same as {@link SocketSettings#getReadTimeout(TimeUnit)}.
+ * Deprecated, use {@code timeoutMS} instead.
* {@code maxIdleTimeMS=ms}: Maximum idle time of a pooled connection. A connection that exceeds this limit will be closed
* {@code maxLifeTimeMS=ms}: Maximum life time of a pooled connection. A connection that exceeds this limit will be closed
*
@@ -161,7 +166,7 @@
* {@code waitQueueTimeoutMS=ms}: The maximum duration to wait until either:
* an {@linkplain ConnectionCheckedOutEvent in-use connection} becomes {@linkplain ConnectionCheckedInEvent available},
* or a {@linkplain ConnectionCreatedEvent connection is created} and begins to be {@linkplain ConnectionReadyEvent established}.
- * See {@link #getMaxWaitTime()} for more details.
+ * See {@link #getMaxWaitTime()} for more details. . Deprecated, use {@code timeoutMS} instead.
* {@code maxConnecting=n}: The maximum number of connections a pool may be establishing concurrently.
*
* Write concern configuration:
@@ -189,7 +194,7 @@
* {@code wtimeoutMS=ms}
*
* - The driver adds { wtimeout : ms } to all write commands. Implies {@code safe=true}.
- * - Used in combination with {@code w}
+ * - Used in combination with {@code w}. Deprecated, use {@code timeoutMS} instead
*
*
*
@@ -311,6 +316,7 @@ public class ConnectionString {
private Integer maxConnectionLifeTime;
private Integer maxConnecting;
private Integer connectTimeout;
+ private Long timeout;
private Integer socketTimeout;
private Boolean sslEnabled;
private Boolean sslInvalidHostnameAllowed;
@@ -503,6 +509,7 @@ public ConnectionString(final String connectionString, @Nullable final DnsClient
credential = createCredentials(combinedOptionsMaps, userName, password);
warnOnUnsupportedOptions(combinedOptionsMaps);
+ warnDeprecatedTimeouts(combinedOptionsMaps);
}
private static final Set GENERAL_OPTIONS_KEYS = new LinkedHashSet<>();
@@ -511,16 +518,18 @@ public ConnectionString(final String connectionString, @Nullable final DnsClient
private static final Set WRITE_CONCERN_KEYS = new HashSet<>();
private static final Set COMPRESSOR_KEYS = new HashSet<>();
private static final Set ALL_KEYS = new HashSet<>();
+ private static final Set DEPRECATED_TIMEOUT_KEYS = new HashSet<>();
static {
GENERAL_OPTIONS_KEYS.add("minpoolsize");
GENERAL_OPTIONS_KEYS.add("maxpoolsize");
+ GENERAL_OPTIONS_KEYS.add("timeoutms");
+ GENERAL_OPTIONS_KEYS.add("sockettimeoutms");
GENERAL_OPTIONS_KEYS.add("waitqueuetimeoutms");
GENERAL_OPTIONS_KEYS.add("connecttimeoutms");
GENERAL_OPTIONS_KEYS.add("maxidletimems");
GENERAL_OPTIONS_KEYS.add("maxlifetimems");
GENERAL_OPTIONS_KEYS.add("maxconnecting");
- GENERAL_OPTIONS_KEYS.add("sockettimeoutms");
// Order matters here: Having tls after ssl means than the tls option will supersede the ssl option when both are set
GENERAL_OPTIONS_KEYS.add("ssl");
@@ -583,6 +592,10 @@ public ConnectionString(final String connectionString, @Nullable final DnsClient
ALL_KEYS.addAll(READ_PREFERENCE_KEYS);
ALL_KEYS.addAll(WRITE_CONCERN_KEYS);
ALL_KEYS.addAll(COMPRESSOR_KEYS);
+
+ DEPRECATED_TIMEOUT_KEYS.add("sockettimeoutms");
+ DEPRECATED_TIMEOUT_KEYS.add("waitqueuetimeoutms");
+ DEPRECATED_TIMEOUT_KEYS.add("wtimeoutms");
}
// Any options contained in the connection string completely replace the corresponding options specified in TXT records,
@@ -596,15 +609,23 @@ private Map> combineOptionsMaps(final Map> optionsMap) {
- for (final String key : optionsMap.keySet()) {
- if (!ALL_KEYS.contains(key)) {
- if (LOGGER.isWarnEnabled()) {
- LOGGER.warn(format("Connection string contains unsupported option '%s'.", key));
- }
- }
+ if (LOGGER.isWarnEnabled()) {
+ optionsMap.keySet()
+ .stream()
+ .filter(k -> !ALL_KEYS.contains(k))
+ .forEach(k -> LOGGER.warn(format("Connection string contains unsupported option '%s'.", k)));
+ }
+ }
+ private void warnDeprecatedTimeouts(final Map> optionsMap) {
+ if (LOGGER.isWarnEnabled()) {
+ optionsMap.keySet()
+ .stream()
+ .filter(DEPRECATED_TIMEOUT_KEYS::contains)
+ .forEach(k -> LOGGER.warn(format("Use of deprecated timeout option: '%s'. Prefer 'timeoutMS' instead.", k)));
}
}
+
private void translateOptions(final Map> optionsMap) {
boolean tlsInsecureSet = false;
boolean tlsAllowInvalidHostnamesSet = false;
@@ -639,6 +660,9 @@ private void translateOptions(final Map> optionsMap) {
case "sockettimeoutms":
socketTimeout = parseInteger(value, "sockettimeoutms");
break;
+ case "timeoutms":
+ timeout = parseLong(value, "timeoutms");
+ break;
case "proxyhost":
proxyHost = value;
break;
@@ -1159,6 +1183,15 @@ private int parseInteger(final String input, final String key) {
}
}
+ private long parseLong(final String input, final String key) {
+ try {
+ return Long.parseLong(input);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(format("The connection string contains an invalid value for '%s'. "
+ + "'%s' is not a valid long", key, input));
+ }
+ }
+
private List parseHosts(final List rawHosts) {
if (rawHosts.size() == 0){
throw new IllegalArgumentException("The connection string must contain at least one host");
@@ -1533,6 +1566,38 @@ public Integer getMaxConnecting() {
return maxConnecting;
}
+ /**
+ * The time limit for the full execution of an operation in milliseconds.
+ *
+ * If set the following deprecated options will be ignored:
+ * {@code waitQueueTimeoutMS}, {@code socketTimeoutMS}, {@code wTimeoutMS}, {@code maxTimeMS} and {@code maxCommitTimeMS}
+ *
+ *
+ * - {@code null} means that the timeout mechanism for operations will defer to using:
+ *
+ * - {@code waitQueueTimeoutMS}: The maximum wait time in milliseconds that a thread may wait for a connection to become
+ * available
+ * - {@code socketTimeoutMS}: How long a send or receive on a socket can take before timing out.
+ * - {@code wTimeoutMS}: How long the server will wait for the write concern to be fulfilled before timing out.
+ * - {@code maxTimeMS}: The cumulative time limit for processing operations on a cursor.
+ * See: cursor.maxTimeMS.
+ * - {@code maxCommitTimeMS}: The maximum amount of time to allow a single {@code commitTransaction} command to execute.
+ * See: {@link TransactionOptions#getMaxCommitTime}.
+ *
+ *
+ * - {@code 0} means infinite timeout.
+ * - {@code > 0} The time limit to use for the full execution of an operation.
+ *
+ *
+ * @return the time limit for the full execution of an operation in milliseconds or null.
+ * @since 5.2
+ */
+ @Alpha(Reason.CLIENT)
+ @Nullable
+ public Long getTimeout() {
+ return timeout;
+ }
+
/**
* Gets the socket connect timeout specified in the connection string.
* @return the socket connect timeout
@@ -1737,6 +1802,7 @@ public boolean equals(final Object o) {
&& Objects.equals(maxConnectionLifeTime, that.maxConnectionLifeTime)
&& Objects.equals(maxConnecting, that.maxConnecting)
&& Objects.equals(connectTimeout, that.connectTimeout)
+ && Objects.equals(timeout, that.timeout)
&& Objects.equals(socketTimeout, that.socketTimeout)
&& Objects.equals(proxyHost, that.proxyHost)
&& Objects.equals(proxyPort, that.proxyPort)
@@ -1760,7 +1826,7 @@ public boolean equals(final Object o) {
public int hashCode() {
return Objects.hash(credential, isSrvProtocol, hosts, database, collection, directConnection, readPreference,
writeConcern, retryWrites, retryReads, readConcern, minConnectionPoolSize, maxConnectionPoolSize, maxWaitTime,
- maxConnectionIdleTime, maxConnectionLifeTime, maxConnecting, connectTimeout, socketTimeout, sslEnabled,
+ maxConnectionIdleTime, maxConnectionLifeTime, maxConnecting, connectTimeout, timeout, socketTimeout, sslEnabled,
sslInvalidHostnameAllowed, requiredReplicaSetName, serverSelectionTimeout, localThreshold, heartbeatFrequency,
serverMonitoringMode, applicationName, compressorList, uuidRepresentation, srvServiceName, srvMaxHosts, proxyHost,
proxyPort, proxyUsername, proxyPassword);
diff --git a/driver-core/src/main/com/mongodb/MongoClientSettings.java b/driver-core/src/main/com/mongodb/MongoClientSettings.java
index 0d98bbe33d3..31206e56029 100644
--- a/driver-core/src/main/com/mongodb/MongoClientSettings.java
+++ b/driver-core/src/main/com/mongodb/MongoClientSettings.java
@@ -16,8 +16,10 @@
package com.mongodb;
+import com.mongodb.annotations.Alpha;
import com.mongodb.annotations.Immutable;
import com.mongodb.annotations.NotThreadSafe;
+import com.mongodb.annotations.Reason;
import com.mongodb.client.gridfs.codecs.GridFSFileCodecProvider;
import com.mongodb.client.model.geojson.codecs.GeoJsonCodecProvider;
import com.mongodb.client.model.mql.ExpressionCodecProvider;
@@ -49,9 +51,12 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import static com.mongodb.assertions.Assertions.isTrue;
import static com.mongodb.assertions.Assertions.isTrueArgument;
import static com.mongodb.assertions.Assertions.notNull;
+import static com.mongodb.internal.TimeoutSettings.convertAndValidateTimeout;
import static java.util.Arrays.asList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.bson.codecs.configuration.CodecRegistries.fromProviders;
@@ -111,6 +116,8 @@ public final class MongoClientSettings {
private final ContextProvider contextProvider;
private final DnsClient dnsClient;
private final InetAddressResolver inetAddressResolver;
+ @Nullable
+ private final Long timeoutMS;
/**
* Gets the default codec registry. It includes the following providers:
@@ -226,6 +233,7 @@ public static final class Builder {
private int heartbeatConnectTimeoutMS;
private int heartbeatSocketTimeoutMS;
+ private Long timeoutMS;
private ContextProvider contextProvider;
private DnsClient dnsClient;
@@ -249,6 +257,7 @@ private Builder(final MongoClientSettings settings) {
uuidRepresentation = settings.getUuidRepresentation();
serverApi = settings.getServerApi();
dnsClient = settings.getDnsClient();
+ timeoutMS = settings.getTimeout(MILLISECONDS);
inetAddressResolver = settings.getInetAddressResolver();
transportSettings = settings.getTransportSettings();
autoEncryptionSettings = settings.getAutoEncryptionSettings();
@@ -311,6 +320,9 @@ public Builder applyConnectionString(final ConnectionString connectionString) {
if (connectionString.getWriteConcern() != null) {
writeConcern = connectionString.getWriteConcern();
}
+ if (connectionString.getTimeout() != null) {
+ timeoutMS = connectionString.getTimeout();
+ }
return this;
}
@@ -666,6 +678,39 @@ public Builder inetAddressResolver(@Nullable final InetAddressResolver inetAddre
return this;
}
+
+ /**
+ * Sets the time limit for the full execution of an operation.
+ *
+ *
+ * - {@code null} means that the timeout mechanism for operations will defer to using:
+ *
+ * - {@code waitQueueTimeoutMS}: The maximum wait time in milliseconds that a thread may wait for a connection to become
+ * available
+ * - {@code socketTimeoutMS}: How long a send or receive on a socket can take before timing out.
+ * - {@code wTimeoutMS}: How long the server will wait for the write concern to be fulfilled before timing out.
+ * - {@code maxTimeMS}: The cumulative time limit for processing operations on a cursor.
+ * See: cursor.maxTimeMS.
+ * - {@code maxCommitTimeMS}: The maximum amount of time to allow a single {@code commitTransaction} command to execute.
+ * See: {@link TransactionOptions#getMaxCommitTime}.
+ *
+ *
+ * - {@code 0} means infinite timeout.
+ * - {@code > 0} The time limit to use for the full execution of an operation.
+ *
+ *
+ * @param timeout the timeout
+ * @param timeUnit the time unit
+ * @return this
+ * @since 5.2
+ * @see #getTimeout
+ */
+ @Alpha(Reason.CLIENT)
+ public Builder timeout(final long timeout, final TimeUnit timeUnit) {
+ this.timeoutMS = convertAndValidateTimeout(timeout, timeUnit);
+ return this;
+ }
+
// Package-private to provide interop with MongoClientOptions
Builder heartbeatConnectTimeoutMS(final int heartbeatConnectTimeoutMS) {
this.heartbeatConnectTimeoutMS = heartbeatConnectTimeoutMS;
@@ -846,6 +891,39 @@ public ServerApi getServerApi() {
return serverApi;
}
+ /**
+ * The time limit for the full execution of an operation.
+ *
+ * If set the following deprecated options will be ignored:
+ * {@code waitQueueTimeoutMS}, {@code socketTimeoutMS}, {@code wTimeoutMS}, {@code maxTimeMS} and {@code maxCommitTimeMS}
+ *
+ *
+ * - {@code null} means that the timeout mechanism for operations will defer to using:
+ *
+ * - {@code waitQueueTimeoutMS}: The maximum wait time in milliseconds that a thread may wait for a connection to become
+ * available
+ * - {@code socketTimeoutMS}: How long a send or receive on a socket can take before timing out.
+ * - {@code wTimeoutMS}: How long the server will wait for the write concern to be fulfilled before timing out.
+ * - {@code maxTimeMS}: The cumulative time limit for processing operations on a cursor.
+ * See: cursor.maxTimeMS.
+ * - {@code maxCommitTimeMS}: The maximum amount of time to allow a single {@code commitTransaction} command to execute.
+ * See: {@link TransactionOptions#getMaxCommitTime}.
+ *
+ *
+ * - {@code 0} means infinite timeout.
+ * - {@code > 0} The time limit to use for the full execution of an operation.
+ *
+ *
+ * @param timeUnit the time unit
+ * @return the timeout in the given time unit
+ * @since 5.2
+ */
+ @Alpha(Reason.CLIENT)
+ @Nullable
+ public Long getTimeout(final TimeUnit timeUnit) {
+ return timeoutMS == null ? null : timeUnit.convert(timeoutMS, MILLISECONDS);
+ }
+
/**
* Gets the auto-encryption settings.
*
@@ -996,7 +1074,8 @@ public boolean equals(final Object o) {
&& Objects.equals(autoEncryptionSettings, that.autoEncryptionSettings)
&& Objects.equals(dnsClient, that.dnsClient)
&& Objects.equals(inetAddressResolver, that.inetAddressResolver)
- && Objects.equals(contextProvider, that.contextProvider);
+ && Objects.equals(contextProvider, that.contextProvider)
+ && Objects.equals(timeoutMS, that.timeoutMS);
}
@Override
@@ -1005,7 +1084,8 @@ public int hashCode() {
commandListeners, codecRegistry, loggerSettings, clusterSettings, socketSettings,
heartbeatSocketSettings, connectionPoolSettings, serverSettings, sslSettings, applicationName, compressorList,
uuidRepresentation, serverApi, autoEncryptionSettings, heartbeatSocketTimeoutSetExplicitly,
- heartbeatConnectTimeoutSetExplicitly, dnsClient, inetAddressResolver, contextProvider);
+ heartbeatConnectTimeoutSetExplicitly, dnsClient, inetAddressResolver, contextProvider, timeoutMS);
+
}
@Override
@@ -1035,10 +1115,12 @@ public String toString() {
+ ", dnsClient=" + dnsClient
+ ", inetAddressResolver=" + inetAddressResolver
+ ", contextProvider=" + contextProvider
+ + ", timeoutMS=" + timeoutMS
+ '}';
}
private MongoClientSettings(final Builder builder) {
+ isTrue("timeoutMS > 0 ", builder.timeoutMS == null || builder.timeoutMS >= 0);
readPreference = builder.readPreference;
writeConcern = builder.writeConcern;
retryWrites = builder.retryWrites;
@@ -1073,5 +1155,6 @@ private MongoClientSettings(final Builder builder) {
heartbeatSocketTimeoutSetExplicitly = builder.heartbeatSocketTimeoutMS != 0;
heartbeatConnectTimeoutSetExplicitly = builder.heartbeatConnectTimeoutMS != 0;
contextProvider = builder.contextProvider;
+ timeoutMS = builder.timeoutMS;
}
}
diff --git a/driver-core/src/main/com/mongodb/MongoCredential.java b/driver-core/src/main/com/mongodb/MongoCredential.java
index 8f731027cf4..f55251a7603 100644
--- a/driver-core/src/main/com/mongodb/MongoCredential.java
+++ b/driver-core/src/main/com/mongodb/MongoCredential.java
@@ -19,6 +19,7 @@
import com.mongodb.annotations.Beta;
import com.mongodb.annotations.Evolving;
import com.mongodb.annotations.Immutable;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.Nullable;
import java.time.Duration;
@@ -182,7 +183,7 @@ public final class MongoCredential {
* @see AwsCredential
* @since 4.4
*/
- @Beta(Beta.Reason.CLIENT)
+ @Beta(Reason.CLIENT)
public static final String AWS_CREDENTIAL_PROVIDER_KEY = "AWS_CREDENTIAL_PROVIDER";
/**
diff --git a/driver-core/src/main/com/mongodb/MongoExecutionTimeoutException.java b/driver-core/src/main/com/mongodb/MongoExecutionTimeoutException.java
index a48328b5ca9..e257991ccda 100644
--- a/driver-core/src/main/com/mongodb/MongoExecutionTimeoutException.java
+++ b/driver-core/src/main/com/mongodb/MongoExecutionTimeoutException.java
@@ -16,6 +16,8 @@
package com.mongodb;
+import com.mongodb.annotations.Alpha;
+import com.mongodb.annotations.Reason;
import org.bson.BsonDocument;
/**
@@ -26,6 +28,18 @@
public class MongoExecutionTimeoutException extends MongoException {
private static final long serialVersionUID = 5955669123800274594L;
+ /**
+ * Construct a new instance.
+ *
+ * @param message the error message
+ * @since 5.2
+ */
+ @Alpha(Reason.CLIENT)
+ public MongoExecutionTimeoutException(final String message) {
+ super(message);
+
+ }
+
/**
* Construct a new instance.
*
diff --git a/driver-core/src/main/com/mongodb/MongoOperationTimeoutException.java b/driver-core/src/main/com/mongodb/MongoOperationTimeoutException.java
new file mode 100644
index 00000000000..707df3e7b73
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/MongoOperationTimeoutException.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.mongodb;
+
+import com.mongodb.annotations.Alpha;
+import com.mongodb.annotations.Reason;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Exception thrown to indicate that a MongoDB operation has exceeded the specified timeout for
+ * the full execution of operation.
+ *
+ *
The {@code MongoOperationTimeoutException} might provide information about the underlying
+ * cause of the timeout, if available. For example, if retries are attempted due to transient failures,
+ * and a timeout occurs in any of the attempts, the exception from one of the retries may be appended
+ * as the cause to this {@code MongoOperationTimeoutException}.
+ *
+ *
The key difference between {@code MongoOperationTimeoutException} and {@code MongoExecutionTimeoutException}
+ * lies in the nature of these exceptions. {@code MongoExecutionTimeoutException} indicates a server-side timeout
+ * capped by a user-specified number. These server errors are transformed into the new {@code MongoOperationTimeoutException}.
+ * On the other hand, {@code MongoOperationExecutionException} denotes a timeout during the execution of the entire operation.
+ *
+ * @see MongoClientSettings.Builder#timeout(long, TimeUnit)
+ * @see MongoClientSettings#getTimeout(TimeUnit)
+ * @since 5.2
+ */
+@Alpha(Reason.CLIENT)
+public final class MongoOperationTimeoutException extends MongoTimeoutException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct a new instance.
+ *
+ * @param message the message
+ */
+ public MongoOperationTimeoutException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Construct a new instance
+ * @param message the message
+ * @param cause the cause
+ */
+ public MongoOperationTimeoutException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/driver-core/src/main/com/mongodb/MongoSocketWriteTimeoutException.java b/driver-core/src/main/com/mongodb/MongoSocketWriteTimeoutException.java
new file mode 100644
index 00000000000..bd95430e595
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/MongoSocketWriteTimeoutException.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mongodb;
+
+import com.mongodb.annotations.Alpha;
+import com.mongodb.annotations.Reason;
+
+/**
+ * This exception is thrown when there is a timeout writing a response from the socket.
+ *
+ * @since 5.2
+ */
+@Alpha(Reason.CLIENT)
+public class MongoSocketWriteTimeoutException extends MongoSocketException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Construct a new instance
+ *
+ * @param message the message
+ * @param address the address
+ * @param cause the cause
+ */
+ public MongoSocketWriteTimeoutException(final String message, final ServerAddress address, final Throwable cause) {
+ super(message, address, cause);
+ }
+
+}
diff --git a/driver-core/src/main/com/mongodb/MongoTimeoutException.java b/driver-core/src/main/com/mongodb/MongoTimeoutException.java
index ff9623b09f0..e2cce02403a 100644
--- a/driver-core/src/main/com/mongodb/MongoTimeoutException.java
+++ b/driver-core/src/main/com/mongodb/MongoTimeoutException.java
@@ -16,6 +16,9 @@
package com.mongodb;
+import com.mongodb.annotations.Alpha;
+import com.mongodb.annotations.Reason;
+
/**
* An exception indicating that the driver has timed out waiting for either a server or a connection to become available.
*/
@@ -31,4 +34,15 @@ public class MongoTimeoutException extends MongoClientException {
public MongoTimeoutException(final String message) {
super(message);
}
+
+ /**
+ * Construct a new instance
+ * @param message the message
+ * @param cause the cause
+ * @since 5.2
+ */
+ @Alpha(Reason.CLIENT)
+ public MongoTimeoutException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/driver-core/src/main/com/mongodb/MongoUpdatedEncryptedFieldsException.java b/driver-core/src/main/com/mongodb/MongoUpdatedEncryptedFieldsException.java
index 1db6b4eba07..c91a3c87fc5 100644
--- a/driver-core/src/main/com/mongodb/MongoUpdatedEncryptedFieldsException.java
+++ b/driver-core/src/main/com/mongodb/MongoUpdatedEncryptedFieldsException.java
@@ -16,6 +16,7 @@
package com.mongodb;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import org.bson.BsonDocument;
import static com.mongodb.assertions.Assertions.assertNotNull;
@@ -26,7 +27,7 @@
*
* @since 4.9
*/
-@Beta(Beta.Reason.SERVER)
+@Beta(Reason.SERVER)
public final class MongoUpdatedEncryptedFieldsException extends MongoClientException {
private static final long serialVersionUID = 1;
diff --git a/driver-core/src/main/com/mongodb/TransactionOptions.java b/driver-core/src/main/com/mongodb/TransactionOptions.java
index e4cafe9161c..e5f22c22def 100644
--- a/driver-core/src/main/com/mongodb/TransactionOptions.java
+++ b/driver-core/src/main/com/mongodb/TransactionOptions.java
@@ -16,7 +16,9 @@
package com.mongodb;
+import com.mongodb.annotations.Alpha;
import com.mongodb.annotations.Immutable;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.Nullable;
import java.util.Objects;
@@ -24,6 +26,7 @@
import static com.mongodb.assertions.Assertions.isTrueArgument;
import static com.mongodb.assertions.Assertions.notNull;
+import static com.mongodb.internal.TimeoutSettings.convertAndValidateTimeoutNullable;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
/**
@@ -42,6 +45,7 @@ public final class TransactionOptions {
private final WriteConcern writeConcern;
private final ReadPreference readPreference;
private final Long maxCommitTimeMS;
+ private final Long timeoutMS;
/**
* Gets the read concern.
@@ -91,6 +95,34 @@ public Long getMaxCommitTime(final TimeUnit timeUnit) {
return timeUnit.convert(maxCommitTimeMS, MILLISECONDS);
}
+ /**
+ * The time limit for the full execution of the transaction.
+ *
+ *
If set the following deprecated options will be ignored:
+ * {@code waitQueueTimeoutMS}, {@code socketTimeoutMS}, {@code wTimeoutMS}, {@code maxTimeMS} and {@code maxCommitTimeMS}
+ *
+ *
+ * - {@code null} means that the timeout mechanism for operations will defer to using
+ * {@link ClientSessionOptions#getDefaultTimeout(TimeUnit)} or {@link MongoClientSettings#getTimeout(TimeUnit)}
+ *
+ * - {@code 0} means infinite timeout.
+ * - {@code > 0} The time limit to use for the full execution of an operation.
+ *
+ *
+ * @param timeUnit the time unit
+ * @return the timeout in the given time unit
+ * @since 5.2
+ */
+ @Nullable
+ @Alpha(Reason.CLIENT)
+ public Long getTimeout(final TimeUnit timeUnit) {
+ notNull("timeUnit", timeUnit);
+ if (timeoutMS == null) {
+ return null;
+ }
+ return timeUnit.convert(timeoutMS, MILLISECONDS);
+ }
+
/**
* Gets an instance of a builder
*
@@ -120,6 +152,9 @@ public static TransactionOptions merge(final TransactionOptions options, final T
.maxCommitTime(options.getMaxCommitTime(MILLISECONDS) == null
? defaultOptions.getMaxCommitTime(MILLISECONDS) : options.getMaxCommitTime(MILLISECONDS),
MILLISECONDS)
+ .timeout(options.getTimeout(MILLISECONDS) == null
+ ? defaultOptions.getTimeout(MILLISECONDS) : options.getTimeout(MILLISECONDS),
+ MILLISECONDS)
.build();
}
@@ -134,6 +169,9 @@ public boolean equals(final Object o) {
TransactionOptions that = (TransactionOptions) o;
+ if (!Objects.equals(timeoutMS, that.timeoutMS)) {
+ return false;
+ }
if (!Objects.equals(maxCommitTimeMS, that.maxCommitTimeMS)) {
return false;
}
@@ -156,6 +194,7 @@ public int hashCode() {
result = 31 * result + (writeConcern != null ? writeConcern.hashCode() : 0);
result = 31 * result + (readPreference != null ? readPreference.hashCode() : 0);
result = 31 * result + (maxCommitTimeMS != null ? maxCommitTimeMS.hashCode() : 0);
+ result = 31 * result + (timeoutMS != null ? timeoutMS.hashCode() : 0);
return result;
}
@@ -165,7 +204,8 @@ public String toString() {
+ "readConcern=" + readConcern
+ ", writeConcern=" + writeConcern
+ ", readPreference=" + readPreference
- + ", maxCommitTimeMS" + maxCommitTimeMS
+ + ", maxCommitTimeMS=" + maxCommitTimeMS
+ + ", timeoutMS=" + timeoutMS
+ '}';
}
@@ -177,6 +217,8 @@ public static final class Builder {
private WriteConcern writeConcern;
private ReadPreference readPreference;
private Long maxCommitTimeMS;
+ @Nullable
+ private Long timeoutMS;
/**
* Sets the read concern.
@@ -231,6 +273,36 @@ public Builder maxCommitTime(@Nullable final Long maxCommitTime, final TimeUnit
return this;
}
+ /**
+ * Sets the time limit for the full execution of the operations for this transaction.
+ *
+ *
+ * - {@code null} means that the timeout mechanism for operations will defer to using:
+ *
+ * - {@code waitQueueTimeoutMS}: The maximum wait time in milliseconds that a thread may wait for a connection to become
+ * available
+ * - {@code socketTimeoutMS}: How long a send or receive on a socket can take before timing out.
+ * - {@code wTimeoutMS}: How long the server will wait for the write concern to be fulfilled before timing out.
+ * - {@code maxTimeMS}: The cumulative time limit for processing operations on a cursor.
+ * See: cursor.maxTimeMS.
+ * - {@code maxCommitTimeMS}: The maximum amount of time to allow a single {@code commitTransaction} command to execute.
+ *
+ *
+ * - {@code 0} means infinite timeout.
+ * - {@code > 0} The time limit to use for the full execution of an operation.
+ *
+ *
+ * @param timeout the timeout
+ * @param timeUnit the time unit
+ * @return this
+ * @since 5.2
+ */
+ @Alpha(Reason.CLIENT)
+ public Builder timeout(@Nullable final Long timeout, final TimeUnit timeUnit) {
+ this.timeoutMS = convertAndValidateTimeoutNullable(timeout, timeUnit);
+ return this;
+ }
+
/**
* Build the transaction options instance.
*
@@ -250,5 +322,6 @@ private TransactionOptions(final Builder builder) {
writeConcern = builder.writeConcern;
readPreference = builder.readPreference;
maxCommitTimeMS = builder.maxCommitTimeMS;
+ timeoutMS = builder.timeoutMS;
}
}
diff --git a/driver-core/src/main/com/mongodb/annotations/Alpha.java b/driver-core/src/main/com/mongodb/annotations/Alpha.java
new file mode 100644
index 00000000000..3698c7ac860
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/annotations/Alpha.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ * Copyright 2010 The Guava Authors
+ * Copyright 2011 The Guava Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.mongodb.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Signifies that a public API element is in the early stages of development, subject to
+ * incompatible changes, or even removal, in a future release and may lack some intended features.
+ * An API bearing this annotation may contain known issues affecting functionality, performance,
+ * and stability. It is also exempt from any compatibility guarantees made by its containing library.
+ *
+ * It is inadvisable for applications to use Alpha APIs in production environments or
+ * for libraries (which get included on users' CLASSPATHs, outside the library developers'
+ * control) to depend on these APIs. Alpha APIs are intended for experimental purposes only.
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target({
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.FIELD,
+ ElementType.METHOD,
+ ElementType.PACKAGE,
+ ElementType.TYPE })
+@Documented
+@Beta(Reason.CLIENT)
+public @interface Alpha {
+ /**
+ * @return The reason an API element is marked with {@link Alpha}.
+ */
+ Reason[] value();
+}
diff --git a/driver-core/src/main/com/mongodb/annotations/Beta.java b/driver-core/src/main/com/mongodb/annotations/Beta.java
index a44dae43cd5..55753ddc051 100644
--- a/driver-core/src/main/com/mongodb/annotations/Beta.java
+++ b/driver-core/src/main/com/mongodb/annotations/Beta.java
@@ -47,25 +47,10 @@
ElementType.PACKAGE,
ElementType.TYPE })
@Documented
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public @interface Beta {
/**
* @return The reason an API element is marked with {@link Beta}.
*/
Reason[] value();
-
- /**
- * @see Beta#value()
- */
- enum Reason {
- /**
- * The driver API is in preview.
- */
- CLIENT,
- /**
- * The driver API relies on the server API, which is in preview.
- * We still may decide to change the driver API even if the server API stays unchanged.
- */
- SERVER
- }
}
diff --git a/driver-core/src/main/com/mongodb/annotations/Reason.java b/driver-core/src/main/com/mongodb/annotations/Reason.java
new file mode 100644
index 00000000000..af72098a9de
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/annotations/Reason.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mongodb.annotations;
+
+/**
+ * Enumerates the reasons an API element might be marked with annotations like {@link Alpha} or {@link Beta}.
+ */
+@Beta(Reason.CLIENT)
+public enum Reason {
+ /**
+ * Indicates that the status of the driver API is the reason for the annotation.
+ */
+ CLIENT,
+
+ /**
+ * The driver API relies on the server API.
+ * This dependency is the reason for the annotation and suggests that changes in the server API could impact the driver API.
+ */
+ SERVER
+}
diff --git a/driver-core/src/main/com/mongodb/assertions/Assertions.java b/driver-core/src/main/com/mongodb/assertions/Assertions.java
index 9866c222c6d..a40b4e4b7b6 100644
--- a/driver-core/src/main/com/mongodb/assertions/Assertions.java
+++ b/driver-core/src/main/com/mongodb/assertions/Assertions.java
@@ -20,10 +20,11 @@
import com.mongodb.lang.Nullable;
import java.util.Collection;
+import java.util.function.Function;
import java.util.function.Supplier;
/**
- * Design by contract assertions.
This class is not part of the public API and may be removed or changed at any time.
+ * Design by contract assertions.
* All {@code assert...} methods throw {@link AssertionError} and should be used to check conditions which may be violated if and only if
* the driver code is incorrect. The intended usage of this methods is the same as of the
* Java {@code assert} statement. The reason
@@ -104,6 +105,24 @@ public static void isTrueArgument(final String name, final boolean condition) {
}
}
+ /**
+ * Throw IllegalArgumentException if the condition returns false.
+ *
+ * @param msg the error message if the condition returns false
+ * @param supplier the supplier of the value
+ * @param condition the condition function
+ * @return the supplied value if it meets the condition
+ * @param the type of the supplied value
+ */
+ public static T isTrueArgument(final String msg, final Supplier supplier, final Function condition) {
+ T value = doesNotThrow(supplier);
+ if (!condition.apply(value)) {
+ throw new IllegalArgumentException(msg);
+ }
+
+ return value;
+ }
+
/**
* Throw IllegalArgumentException if the collection contains a null value.
*
diff --git a/driver-core/src/main/com/mongodb/client/cursor/TimeoutMode.java b/driver-core/src/main/com/mongodb/client/cursor/TimeoutMode.java
new file mode 100644
index 00000000000..cdaa92d4923
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/cursor/TimeoutMode.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.mongodb.client.cursor;
+
+import com.mongodb.annotations.Alpha;
+import com.mongodb.annotations.Reason;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The timeout mode for a cursor
+ *
+ * For operations that create cursors, {@code timeoutMS} can either cap the lifetime of the cursor or be applied separately to the
+ * original operation and all next calls.
+ *
+ * @see com.mongodb.MongoClientSettings#getTimeout(TimeUnit)
+ * @since 5.2
+ */
+@Alpha(Reason.CLIENT)
+public enum TimeoutMode {
+
+ /**
+ * The timeout lasts for the lifetime of the cursor
+ */
+ CURSOR_LIFETIME,
+
+ /**
+ * The timeout is reset for each batch iteration of the cursor
+ */
+ ITERATION
+}
diff --git a/driver-core/src/main/com/mongodb/client/cursor/package-info.java b/driver-core/src/main/com/mongodb/client/cursor/package-info.java
new file mode 100644
index 00000000000..ea907688087
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/client/cursor/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This package contains models and options that help describe MongoCollection operations
+ */
+@NonNullApi
+package com.mongodb.client.cursor;
+
+import com.mongodb.lang.NonNullApi;
diff --git a/driver-core/src/main/com/mongodb/client/model/Aggregates.java b/driver-core/src/main/com/mongodb/client/model/Aggregates.java
index 08e2fb10b02..53e9e1eaf52 100644
--- a/driver-core/src/main/com/mongodb/client/model/Aggregates.java
+++ b/driver-core/src/main/com/mongodb/client/model/Aggregates.java
@@ -18,6 +18,7 @@
import com.mongodb.MongoNamespace;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.client.model.densify.DensifyOptions;
import com.mongodb.client.model.densify.DensifyRange;
import com.mongodb.client.model.fill.FillOptions;
@@ -955,7 +956,7 @@ public static Bson searchMeta(final SearchCollector collector, final SearchOptio
* @mongodb.server.release 6.0.10
* @since 4.11
*/
- @Beta(Beta.Reason.SERVER)
+ @Beta(Reason.SERVER)
public static Bson vectorSearch(
final FieldSearchPath path,
final Iterable queryVector,
@@ -984,7 +985,7 @@ public static Bson vectorSearch(
* @mongodb.server.release 6.0.10
* @since 4.11
*/
- @Beta(Beta.Reason.SERVER)
+ @Beta(Reason.SERVER)
public static Bson vectorSearch(
final FieldSearchPath path,
final Iterable queryVector,
diff --git a/driver-core/src/main/com/mongodb/client/model/CreateCollectionOptions.java b/driver-core/src/main/com/mongodb/client/model/CreateCollectionOptions.java
index 5aa79112871..31165688d4a 100644
--- a/driver-core/src/main/com/mongodb/client/model/CreateCollectionOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/CreateCollectionOptions.java
@@ -18,6 +18,7 @@
import com.mongodb.AutoEncryptionSettings;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.Nullable;
import org.bson.conversions.Bson;
@@ -353,7 +354,7 @@ public CreateCollectionOptions changeStreamPreAndPostImagesOptions(
* @since 4.7
* @mongodb.server.release 7.0
*/
- @Beta(Beta.Reason.SERVER)
+ @Beta(Reason.SERVER)
@Nullable
public Bson getEncryptedFields() {
return encryptedFields;
@@ -370,7 +371,7 @@ public Bson getEncryptedFields() {
* @mongodb.driver.manual core/security-client-side-encryption/ In-use encryption
* @mongodb.server.release 7.0
*/
- @Beta(Beta.Reason.SERVER)
+ @Beta(Reason.SERVER)
public CreateCollectionOptions encryptedFields(@Nullable final Bson encryptedFields) {
this.encryptedFields = encryptedFields;
return this;
diff --git a/driver-core/src/main/com/mongodb/client/model/CreateEncryptedCollectionParams.java b/driver-core/src/main/com/mongodb/client/model/CreateEncryptedCollectionParams.java
index eba101ac000..537efdc1716 100644
--- a/driver-core/src/main/com/mongodb/client/model/CreateEncryptedCollectionParams.java
+++ b/driver-core/src/main/com/mongodb/client/model/CreateEncryptedCollectionParams.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.client.model.vault.DataKeyOptions;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
@@ -28,7 +29,7 @@
*
* @since 4.9
*/
-@Beta(Beta.Reason.SERVER)
+@Beta(Reason.SERVER)
public final class CreateEncryptedCollectionParams {
private final String kmsProvider;
@Nullable
diff --git a/driver-core/src/main/com/mongodb/client/model/DropCollectionOptions.java b/driver-core/src/main/com/mongodb/client/model/DropCollectionOptions.java
index 5c904888c00..cf2dbca66c4 100644
--- a/driver-core/src/main/com/mongodb/client/model/DropCollectionOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/DropCollectionOptions.java
@@ -18,6 +18,7 @@
import com.mongodb.AutoEncryptionSettings;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.Nullable;
import org.bson.conversions.Bson;
@@ -39,7 +40,7 @@ public class DropCollectionOptions {
* @since 4.7
* @mongodb.server.release 7.0
*/
- @Beta(Beta.Reason.SERVER)
+ @Beta(Reason.SERVER)
@Nullable
public Bson getEncryptedFields() {
return encryptedFields;
@@ -56,7 +57,7 @@ public Bson getEncryptedFields() {
* @mongodb.server.release 7.0
* @mongodb.driver.manual core/security-client-side-encryption/ In-use encryption
*/
- @Beta(Beta.Reason.SERVER)
+ @Beta(Reason.SERVER)
public DropCollectionOptions encryptedFields(@Nullable final Bson encryptedFields) {
this.encryptedFields = encryptedFields;
return this;
diff --git a/driver-core/src/main/com/mongodb/client/model/Projections.java b/driver-core/src/main/com/mongodb/client/model/Projections.java
index e92a95abf81..98fd2810ed5 100644
--- a/driver-core/src/main/com/mongodb/client/model/Projections.java
+++ b/driver-core/src/main/com/mongodb/client/model/Projections.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.client.model.search.FieldSearchPath;
import com.mongodb.client.model.search.SearchCollector;
import com.mongodb.client.model.search.SearchCount;
@@ -223,7 +224,7 @@ public static Bson metaSearchScore(final String fieldName) {
* @mongodb.server.release 6.0.10
* @since 4.11
*/
- @Beta(Beta.Reason.SERVER)
+ @Beta(Reason.SERVER)
public static Bson metaVectorSearchScore(final String fieldName) {
return meta(fieldName, "vectorSearchScore");
}
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/Branches.java b/driver-core/src/main/com/mongodb/client/model/mql/Branches.java
index 1a576cfe581..c6b414de213 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/Branches.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/Branches.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.assertions.Assertions;
import java.util.ArrayList;
@@ -36,7 +37,7 @@
* @param the type of the values that may be checked.
* @since 4.9.0
*/
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public final class Branches {
Branches() {
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/BranchesIntermediary.java b/driver-core/src/main/com/mongodb/client/model/mql/BranchesIntermediary.java
index 9b1b88e4467..b068c118ad3 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/BranchesIntermediary.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/BranchesIntermediary.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.assertions.Assertions;
import java.util.ArrayList;
@@ -32,7 +33,7 @@
* @param the type of the value produced.
* @since 4.9.0
*/
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public final class BranchesIntermediary extends BranchesTerminal {
BranchesIntermediary(final List>> branches) {
super(branches, null);
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/BranchesTerminal.java b/driver-core/src/main/com/mongodb/client/model/mql/BranchesTerminal.java
index f72cb5cb1f4..299942ebdbf 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/BranchesTerminal.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/BranchesTerminal.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.Nullable;
import java.util.List;
@@ -30,7 +31,7 @@
* @param the type of the value produced.
* @since 4.9.0
*/
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public class BranchesTerminal {
private final List>> branches;
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/ExpressionCodecProvider.java b/driver-core/src/main/com/mongodb/client/model/mql/ExpressionCodecProvider.java
index d4176b7205f..893c57c5c86 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/ExpressionCodecProvider.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/ExpressionCodecProvider.java
@@ -18,6 +18,7 @@
import com.mongodb.annotations.Beta;
import com.mongodb.annotations.Immutable;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.Nullable;
import org.bson.codecs.Codec;
import org.bson.codecs.configuration.CodecProvider;
@@ -35,7 +36,7 @@
*
* @since 4.9.0
*/
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
@Immutable
public final class ExpressionCodecProvider implements CodecProvider {
@Override
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlArray.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlArray.java
index 047e294c8e9..e979b4687e7 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlArray.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlArray.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import java.util.function.Function;
@@ -33,7 +34,7 @@
* @since 4.9.0
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MqlArray extends MqlValue {
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlBoolean.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlBoolean.java
index 5e594a757c7..28290cf25f4 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlBoolean.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlBoolean.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import java.util.function.Function;
@@ -28,7 +29,7 @@
* @since 4.9.0
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MqlBoolean extends MqlValue {
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlDate.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlDate.java
index 7c39057ee23..b6600aaf689 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlDate.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlDate.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import java.util.function.Function;
@@ -30,7 +31,7 @@
* @since 4.9.0
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MqlDate extends MqlValue {
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlDocument.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlDocument.java
index b99d5b3354b..c60fde8f82a 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlDocument.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlDocument.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.assertions.Assertions;
import org.bson.conversions.Bson;
@@ -40,7 +41,7 @@
* @since 4.9.0
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MqlDocument extends MqlValue {
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlEntry.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlEntry.java
index bcb1f26e251..dffa35405f1 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlEntry.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlEntry.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -34,7 +35,7 @@
* @since 4.9.0
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MqlEntry extends MqlValue {
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlInteger.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlInteger.java
index 0fe85fd88d9..46380b57773 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlInteger.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlInteger.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import java.util.function.Function;
@@ -30,7 +31,7 @@
* @since 4.9.0
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MqlInteger extends MqlNumber {
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlMap.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlMap.java
index 24ee3ef405b..58a279c89c7 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlMap.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlMap.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.assertions.Assertions;
@@ -35,7 +36,7 @@
* @since 4.9.0
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MqlMap extends MqlValue {
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlNumber.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlNumber.java
index ec3099047b8..7b6590b7624 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlNumber.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlNumber.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.assertions.Assertions;
@@ -31,7 +32,7 @@
* @since 4.9.0
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MqlNumber extends MqlValue {
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlString.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlString.java
index dd24a8c94a2..e5b6e8fa8bc 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlString.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlString.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import java.util.function.Function;
@@ -30,7 +31,7 @@
* @since 4.9.0
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MqlString extends MqlValue {
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlValue.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlValue.java
index 9366ce77fe9..8cb50885584 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlValue.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlValue.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import java.util.function.Function;
@@ -89,7 +90,7 @@
* @since 4.9.0
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MqlValue {
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/MqlValues.java b/driver-core/src/main/com/mongodb/client/model/mql/MqlValues.java
index 8d791dc6b3b..a2d58fbc02b 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/MqlValues.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/MqlValues.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.assertions.Assertions;
import org.bson.BsonArray;
import org.bson.BsonBoolean;
@@ -46,7 +47,7 @@
*
* @since 4.9.0
*/
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public final class MqlValues {
private MqlValues() {}
diff --git a/driver-core/src/main/com/mongodb/client/model/mql/package-info.java b/driver-core/src/main/com/mongodb/client/model/mql/package-info.java
index 08cbc6195a7..caef0925787 100644
--- a/driver-core/src/main/com/mongodb/client/model/mql/package-info.java
+++ b/driver-core/src/main/com/mongodb/client/model/mql/package-info.java
@@ -19,8 +19,9 @@
* @see com.mongodb.client.model.mql.MqlValues
* @since 4.9.0
*/
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
@NonNullApi
package com.mongodb.client.model.mql;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.NonNullApi;
diff --git a/driver-core/src/main/com/mongodb/client/model/search/AddSearchScoreExpression.java b/driver-core/src/main/com/mongodb/client/model/search/AddSearchScoreExpression.java
index 11411ca923d..d8a2fe5e908 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/AddSearchScoreExpression.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/AddSearchScoreExpression.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface AddSearchScoreExpression extends SearchScoreExpression {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/AutocompleteSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/AutocompleteSearchOperator.java
index 2a700e6a770..447de8168cd 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/AutocompleteSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/AutocompleteSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -24,7 +25,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface AutocompleteSearchOperator extends SearchOperator {
@Override
AutocompleteSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/CompoundSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/CompoundSearchOperator.java
index 3d1549fb2fa..b12a86ae78a 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/CompoundSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/CompoundSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,7 +24,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface CompoundSearchOperator extends CompoundSearchOperatorBase, SearchOperator {
@Override
CompoundSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/CompoundSearchOperatorBase.java b/driver-core/src/main/com/mongodb/client/model/search/CompoundSearchOperatorBase.java
index f3fe27dbe3d..2834199a4e0 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/CompoundSearchOperatorBase.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/CompoundSearchOperatorBase.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -26,7 +27,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface CompoundSearchOperatorBase {
/**
* Creates a new {@link CompoundSearchOperator} by adding to it {@code clauses} that must all be satisfied.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/ConstantSearchScore.java b/driver-core/src/main/com/mongodb/client/model/search/ConstantSearchScore.java
index 31c9cfb4c21..463df7634e3 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/ConstantSearchScore.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/ConstantSearchScore.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface ConstantSearchScore extends SearchScore {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/ConstantSearchScoreExpression.java b/driver-core/src/main/com/mongodb/client/model/search/ConstantSearchScoreExpression.java
index e7ae9be59f2..691ee643572 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/ConstantSearchScoreExpression.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/ConstantSearchScoreExpression.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface ConstantSearchScoreExpression extends SearchScoreExpression {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/DateNearSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/DateNearSearchOperator.java
index 5edb7a02756..8421d058eeb 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/DateNearSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/DateNearSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import java.time.Duration;
@@ -27,7 +28,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface DateNearSearchOperator extends SearchOperator {
@Override
DateNearSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/DateRangeSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/DateRangeSearchOperator.java
index dfa98485837..f8c654cae1d 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/DateRangeSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/DateRangeSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -24,7 +25,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface DateRangeSearchOperator extends DateRangeSearchOperatorBase, SearchOperator {
@Override
DateRangeSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/DateRangeSearchOperatorBase.java b/driver-core/src/main/com/mongodb/client/model/search/DateRangeSearchOperatorBase.java
index b7db8c190e9..df8fbaa93d8 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/DateRangeSearchOperatorBase.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/DateRangeSearchOperatorBase.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import java.time.Instant;
@@ -29,7 +30,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface DateRangeSearchOperatorBase {
/**
* Creates a new {@link DateRangeSearchOperator} that tests if values are within (l; ∞).
diff --git a/driver-core/src/main/com/mongodb/client/model/search/DateSearchFacet.java b/driver-core/src/main/com/mongodb/client/model/search/DateSearchFacet.java
index 936ac3040f8..39d8bb2ddf0 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/DateSearchFacet.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/DateSearchFacet.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,7 +24,7 @@
* @since 4.7
*/
@Sealed
-@Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+@Beta({Reason.CLIENT, Reason.SERVER})
public interface DateSearchFacet extends SearchFacet {
/**
* Creates a new {@link DateSearchFacet} with the default bucket specified.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/ExistsSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/ExistsSearchOperator.java
index cb847a49b66..847070dc3bc 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/ExistsSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/ExistsSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,7 +24,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface ExistsSearchOperator extends SearchOperator {
@Override
ExistsSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/FacetSearchCollector.java b/driver-core/src/main/com/mongodb/client/model/search/FacetSearchCollector.java
index 72be0245b2c..01190216633 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/FacetSearchCollector.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/FacetSearchCollector.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+@Beta({Reason.CLIENT, Reason.SERVER})
public interface FacetSearchCollector extends SearchCollector {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/FieldSearchPath.java b/driver-core/src/main/com/mongodb/client/model/search/FieldSearchPath.java
index cc4b89f6381..2be4cdecb90 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/FieldSearchPath.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/FieldSearchPath.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import org.bson.conversions.Bson;
@@ -26,7 +27,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface FieldSearchPath extends SearchPath {
/**
* Creates a new {@link FieldSearchPath} with the name of the alternate analyzer specified.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/FilterCompoundSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/FilterCompoundSearchOperator.java
index 92b414ebbc8..df23133d1a8 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/FilterCompoundSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/FilterCompoundSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -27,7 +28,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface FilterCompoundSearchOperator extends CompoundSearchOperator {
@Override
FilterCompoundSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/FunctionSearchScore.java b/driver-core/src/main/com/mongodb/client/model/search/FunctionSearchScore.java
index 047cf65b2e4..e2bf09bf1a5 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/FunctionSearchScore.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/FunctionSearchScore.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface FunctionSearchScore extends SearchScore {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/FuzzySearchOptions.java b/driver-core/src/main/com/mongodb/client/model/search/FuzzySearchOptions.java
index 7afe5fc1c8a..2acbb244537 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/FuzzySearchOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/FuzzySearchOptions.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import org.bson.conversions.Bson;
@@ -27,7 +28,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface FuzzySearchOptions extends Bson {
/**
* Creates a new {@link FuzzySearchOptions} with the maximum
diff --git a/driver-core/src/main/com/mongodb/client/model/search/GaussSearchScoreExpression.java b/driver-core/src/main/com/mongodb/client/model/search/GaussSearchScoreExpression.java
index 038d5973d78..b3ac5fadedb 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/GaussSearchScoreExpression.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/GaussSearchScoreExpression.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,7 +24,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface GaussSearchScoreExpression extends SearchScoreExpression {
/**
* Creates a new {@link GaussSearchScoreExpression} which does not decay, i.e., its output stays 1, if the value of the
diff --git a/driver-core/src/main/com/mongodb/client/model/search/GeoNearSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/GeoNearSearchOperator.java
index 5c02fce3030..1501bbd819e 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/GeoNearSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/GeoNearSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.client.model.geojson.Point;
@@ -25,7 +26,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface GeoNearSearchOperator extends SearchOperator {
@Override
GeoNearSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/Log1pSearchScoreExpression.java b/driver-core/src/main/com/mongodb/client/model/search/Log1pSearchScoreExpression.java
index f1499a5de16..40ad061cbcb 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/Log1pSearchScoreExpression.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/Log1pSearchScoreExpression.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface Log1pSearchScoreExpression extends SearchScoreExpression {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/LogSearchScoreExpression.java b/driver-core/src/main/com/mongodb/client/model/search/LogSearchScoreExpression.java
index 10ad3b9d40d..ae4e5fa8725 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/LogSearchScoreExpression.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/LogSearchScoreExpression.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface LogSearchScoreExpression extends SearchScoreExpression {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/LowerBoundSearchCount.java b/driver-core/src/main/com/mongodb/client/model/search/LowerBoundSearchCount.java
index 888d66d50b0..15576d4a5b6 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/LowerBoundSearchCount.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/LowerBoundSearchCount.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,7 +24,7 @@
* @since 4.7
*/
@Sealed
-@Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+@Beta({Reason.CLIENT, Reason.SERVER})
public interface LowerBoundSearchCount extends SearchCount {
/**
* Creates a new {@link LowerBoundSearchCount} that instructs to count documents up to the {@code threshold} exactly,
diff --git a/driver-core/src/main/com/mongodb/client/model/search/MultiplySearchScoreExpression.java b/driver-core/src/main/com/mongodb/client/model/search/MultiplySearchScoreExpression.java
index 31d330ba161..e6ab2332bfe 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/MultiplySearchScoreExpression.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/MultiplySearchScoreExpression.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MultiplySearchScoreExpression extends SearchScoreExpression {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/MustCompoundSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/MustCompoundSearchOperator.java
index e9715a9b076..d9db7f7e34b 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/MustCompoundSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/MustCompoundSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -27,7 +28,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MustCompoundSearchOperator extends CompoundSearchOperator {
@Override
MustCompoundSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/MustNotCompoundSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/MustNotCompoundSearchOperator.java
index aad0bb633cc..5bdcc56009d 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/MustNotCompoundSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/MustNotCompoundSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -27,7 +28,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface MustNotCompoundSearchOperator extends CompoundSearchOperator {
@Override
MustNotCompoundSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/NumberNearSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/NumberNearSearchOperator.java
index 1baf5f2303f..65d6ec4969e 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/NumberNearSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/NumberNearSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -24,7 +25,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface NumberNearSearchOperator extends SearchOperator {
@Override
NumberNearSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/NumberRangeSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/NumberRangeSearchOperator.java
index e0acad425c6..fe5d37bdc41 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/NumberRangeSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/NumberRangeSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -24,7 +25,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface NumberRangeSearchOperator extends NumberRangeSearchOperatorBase, SearchOperator {
@Override
NumberRangeSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/NumberRangeSearchOperatorBase.java b/driver-core/src/main/com/mongodb/client/model/search/NumberRangeSearchOperatorBase.java
index 2492f1db11c..daa31d48656 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/NumberRangeSearchOperatorBase.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/NumberRangeSearchOperatorBase.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -27,7 +28,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface NumberRangeSearchOperatorBase {
/**
* Creates a new {@link NumberRangeSearchOperator} that tests if values are within (l; ∞).
diff --git a/driver-core/src/main/com/mongodb/client/model/search/NumberSearchFacet.java b/driver-core/src/main/com/mongodb/client/model/search/NumberSearchFacet.java
index 4fc6bc27d21..4587f688097 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/NumberSearchFacet.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/NumberSearchFacet.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,7 +24,7 @@
* @since 4.7
*/
@Sealed
-@Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+@Beta({Reason.CLIENT, Reason.SERVER})
public interface NumberSearchFacet extends SearchFacet {
/**
* Creates a new {@link NumberSearchFacet} with the default bucket specified.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/PathBoostSearchScore.java b/driver-core/src/main/com/mongodb/client/model/search/PathBoostSearchScore.java
index 37c675e523b..40459fa1724 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/PathBoostSearchScore.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/PathBoostSearchScore.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,7 +24,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface PathBoostSearchScore extends SearchScore {
/**
* Creates a new {@link PathBoostSearchScore} with the value to fall back to
diff --git a/driver-core/src/main/com/mongodb/client/model/search/PathSearchScoreExpression.java b/driver-core/src/main/com/mongodb/client/model/search/PathSearchScoreExpression.java
index a144addae89..b3c14025f4e 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/PathSearchScoreExpression.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/PathSearchScoreExpression.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,7 +24,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface PathSearchScoreExpression extends SearchScoreExpression {
/**
* Creates a new {@link PathSearchScoreExpression} with the value to fall back to
diff --git a/driver-core/src/main/com/mongodb/client/model/search/RelevanceSearchScoreExpression.java b/driver-core/src/main/com/mongodb/client/model/search/RelevanceSearchScoreExpression.java
index 89491f5c935..2a36a679ad5 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/RelevanceSearchScoreExpression.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/RelevanceSearchScoreExpression.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface RelevanceSearchScoreExpression extends SearchScoreExpression {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java b/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java
index a93c5690699..6f2c45b4961 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/SearchCollector.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Projections;
@@ -34,7 +35,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface SearchCollector extends Bson {
/**
* Returns a {@link SearchCollector} that groups results by values or ranges in the specified faceted fields and returns the count
@@ -45,7 +46,7 @@ public interface SearchCollector extends Bson {
* @return The requested {@link SearchCollector}.
* @mongodb.atlas.manual atlas-search/facet/ facet collector
*/
- @Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+ @Beta({Reason.CLIENT, Reason.SERVER})
static FacetSearchCollector facet(final SearchOperator operator, final Iterable extends SearchFacet> facets) {
notNull("operator", operator);
notNull("facets", facets);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchCount.java b/driver-core/src/main/com/mongodb/client/model/search/SearchCount.java
index bb80a894f95..f9a5917582b 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/SearchCount.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/SearchCount.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.client.model.Projections;
import org.bson.BsonDocument;
@@ -33,7 +34,7 @@
* @since 4.7
*/
@Sealed
-@Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+@Beta({Reason.CLIENT, Reason.SERVER})
public interface SearchCount extends Bson {
/**
* Returns a {@link SearchCount} that instructs to count documents exactly.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchFacet.java b/driver-core/src/main/com/mongodb/client/model/search/SearchFacet.java
index fcc4e2866b8..4aac0fef089 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/SearchFacet.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/SearchFacet.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import org.bson.BsonDocument;
import org.bson.BsonType;
@@ -43,7 +44,7 @@
* @since 4.7
*/
@Sealed
-@Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+@Beta({Reason.CLIENT, Reason.SERVER})
public interface SearchFacet extends Bson {
/**
* Returns a {@link SearchFacet} that allows narrowing down search results based on the most frequent
diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchHighlight.java b/driver-core/src/main/com/mongodb/client/model/search/SearchHighlight.java
index c337be57e5b..6610c57590f 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/SearchHighlight.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/SearchHighlight.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.client.model.Projections;
import org.bson.BsonDocument;
@@ -37,7 +38,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface SearchHighlight extends Bson {
/**
* Creates a new {@link SearchHighlight} with the maximum number of characters to examine on a document
diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/SearchOperator.java
index e9fd4796234..9234db91c51 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/SearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/SearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.geojson.Point;
@@ -40,7 +41,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface SearchOperator extends Bson {
/**
* Creates a new {@link SearchOperator} with the scoring modifier specified.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchOptions.java b/driver-core/src/main/com/mongodb/client/model/search/SearchOptions.java
index 8550c672ee5..f5cd0261e8f 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/SearchOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/SearchOptions.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.client.model.Aggregates;
import org.bson.conversions.Bson;
@@ -29,7 +30,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface SearchOptions extends Bson {
/**
* Creates a new {@link SearchOptions} with the index name specified.
@@ -53,7 +54,7 @@ public interface SearchOptions extends Bson {
* @param option The counting option.
* @return A new {@link SearchOptions}.
*/
- @Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+ @Beta({Reason.CLIENT, Reason.SERVER})
SearchOptions count(SearchCount option);
/**
@@ -63,7 +64,7 @@ public interface SearchOptions extends Bson {
* @return A new {@link SearchOptions}.
* @mongodb.atlas.manual atlas-search/return-stored-source/ Return stored source fields
*/
- @Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+ @Beta({Reason.CLIENT, Reason.SERVER})
SearchOptions returnStoredSource(boolean returnStoredSource);
/**
diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchPath.java b/driver-core/src/main/com/mongodb/client/model/search/SearchPath.java
index c620c2995f0..7213f3f894b 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/SearchPath.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/SearchPath.java
@@ -17,6 +17,7 @@
import com.mongodb.annotations.Beta;
import com.mongodb.annotations.Sealed;
+import com.mongodb.annotations.Reason;
import com.mongodb.internal.client.model.Util;
import org.bson.BsonDocument;
import org.bson.BsonString;
@@ -37,7 +38,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface SearchPath extends Bson {
/**
* Returns a {@link SearchPath} for the given {@code path}.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchScore.java b/driver-core/src/main/com/mongodb/client/model/search/SearchScore.java
index 7c241e8ec06..825264cf7f5 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/SearchScore.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/SearchScore.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.client.model.Projections;
import org.bson.BsonDocument;
@@ -34,7 +35,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface SearchScore extends Bson {
/**
* Returns a {@link SearchScore} that instructs to multiply the score by the specified {@code value}.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/SearchScoreExpression.java b/driver-core/src/main/com/mongodb/client/model/search/SearchScoreExpression.java
index 442b361d813..268786c3344 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/SearchScoreExpression.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/SearchScoreExpression.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import org.bson.BsonDocument;
import org.bson.BsonDouble;
@@ -36,7 +37,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface SearchScoreExpression extends Bson {
/**
* Returns a {@link SearchScoreExpression} that evaluates into the relevance score of a document.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/ShouldCompoundSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/ShouldCompoundSearchOperator.java
index 388a08bcb03..a6bda94e206 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/ShouldCompoundSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/ShouldCompoundSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -27,7 +28,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface ShouldCompoundSearchOperator extends CompoundSearchOperator {
@Override
ShouldCompoundSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/StringSearchFacet.java b/driver-core/src/main/com/mongodb/client/model/search/StringSearchFacet.java
index 523d20bfe98..209eaf9ff47 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/StringSearchFacet.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/StringSearchFacet.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,7 +24,7 @@
* @since 4.7
*/
@Sealed
-@Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+@Beta({Reason.CLIENT, Reason.SERVER})
public interface StringSearchFacet extends SearchFacet {
/**
* Creates a new {@link StringSearchFacet} that explicitly limits the number of facet categories.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/TextSearchOperator.java b/driver-core/src/main/com/mongodb/client/model/search/TextSearchOperator.java
index 71d1206d2d7..241639f3a47 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/TextSearchOperator.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/TextSearchOperator.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -24,7 +25,7 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface TextSearchOperator extends SearchOperator {
@Override
TextSearchOperator score(SearchScore modifier);
diff --git a/driver-core/src/main/com/mongodb/client/model/search/TotalSearchCount.java b/driver-core/src/main/com/mongodb/client/model/search/TotalSearchCount.java
index 5df56e6bbbd..2bcbde468f3 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/TotalSearchCount.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/TotalSearchCount.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta({Beta.Reason.CLIENT, Beta.Reason.SERVER})
+@Beta({Reason.CLIENT, Reason.SERVER})
public interface TotalSearchCount extends SearchCount {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/ValueBoostSearchScore.java b/driver-core/src/main/com/mongodb/client/model/search/ValueBoostSearchScore.java
index 5b180b7c14f..d760bd60d52 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/ValueBoostSearchScore.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/ValueBoostSearchScore.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface ValueBoostSearchScore extends SearchScore {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/VectorSearchOptions.java b/driver-core/src/main/com/mongodb/client/model/search/VectorSearchOptions.java
index e512ab0a31c..df3607d039b 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/VectorSearchOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/VectorSearchOptions.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Filters;
@@ -30,7 +31,7 @@
* @since 4.11
*/
@Sealed
-@Beta(Beta.Reason.SERVER)
+@Beta(Reason.SERVER)
public interface VectorSearchOptions extends Bson {
/**
* Creates a new {@link VectorSearchOptions} with the filter specified.
diff --git a/driver-core/src/main/com/mongodb/client/model/search/WildcardSearchPath.java b/driver-core/src/main/com/mongodb/client/model/search/WildcardSearchPath.java
index 9fb66644fbd..2fceaaaad7a 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/WildcardSearchPath.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/WildcardSearchPath.java
@@ -16,6 +16,7 @@
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.annotations.Sealed;
/**
@@ -23,6 +24,6 @@
* @since 4.7
*/
@Sealed
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
public interface WildcardSearchPath extends SearchPath {
}
diff --git a/driver-core/src/main/com/mongodb/client/model/search/package-info.java b/driver-core/src/main/com/mongodb/client/model/search/package-info.java
index d17cba4139e..c3664cb5560 100644
--- a/driver-core/src/main/com/mongodb/client/model/search/package-info.java
+++ b/driver-core/src/main/com/mongodb/client/model/search/package-info.java
@@ -31,8 +31,9 @@
* @since 4.7
*/
@NonNullApi
-@Beta(Beta.Reason.CLIENT)
+@Beta(Reason.CLIENT)
package com.mongodb.client.model.search;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.NonNullApi;
diff --git a/driver-core/src/main/com/mongodb/client/model/vault/EncryptOptions.java b/driver-core/src/main/com/mongodb/client/model/vault/EncryptOptions.java
index aef24b54765..509e467273b 100644
--- a/driver-core/src/main/com/mongodb/client/model/vault/EncryptOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/vault/EncryptOptions.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.vault;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.Nullable;
import org.bson.BsonBinary;
@@ -181,7 +182,7 @@ public String getQueryType() {
* @mongodb.server.release 6.2
* @mongodb.driver.manual /core/queryable-encryption/ queryable encryption
*/
- @Beta(Beta.Reason.SERVER)
+ @Beta(Reason.SERVER)
public EncryptOptions rangeOptions(@Nullable final RangeOptions rangeOptions) {
this.rangeOptions = rangeOptions;
return this;
@@ -195,7 +196,7 @@ public EncryptOptions rangeOptions(@Nullable final RangeOptions rangeOptions) {
* @mongodb.driver.manual /core/queryable-encryption/ queryable encryption
*/
@Nullable
- @Beta(Beta.Reason.SERVER)
+ @Beta(Reason.SERVER)
public RangeOptions getRangeOptions() {
return rangeOptions;
}
diff --git a/driver-core/src/main/com/mongodb/client/model/vault/RangeOptions.java b/driver-core/src/main/com/mongodb/client/model/vault/RangeOptions.java
index b763b0bf112..42a6618bcdb 100644
--- a/driver-core/src/main/com/mongodb/client/model/vault/RangeOptions.java
+++ b/driver-core/src/main/com/mongodb/client/model/vault/RangeOptions.java
@@ -17,6 +17,7 @@
package com.mongodb.client.model.vault;
import com.mongodb.annotations.Beta;
+import com.mongodb.annotations.Reason;
import com.mongodb.lang.Nullable;
import org.bson.BsonValue;
@@ -33,7 +34,7 @@
* @mongodb.server.release 6.2
* @mongodb.driver.manual /core/queryable-encryption/ queryable encryption
*/
-@Beta(Beta.Reason.SERVER)
+@Beta(Reason.SERVER)
public class RangeOptions {
private BsonValue min;
diff --git a/driver-core/src/main/com/mongodb/connection/ServerDescription.java b/driver-core/src/main/com/mongodb/connection/ServerDescription.java
index d97e848c163..f3de13006d1 100644
--- a/driver-core/src/main/com/mongodb/connection/ServerDescription.java
+++ b/driver-core/src/main/com/mongodb/connection/ServerDescription.java
@@ -18,8 +18,10 @@
import com.mongodb.ServerAddress;
import com.mongodb.TagSet;
+import com.mongodb.annotations.Alpha;
import com.mongodb.annotations.Immutable;
import com.mongodb.annotations.NotThreadSafe;
+import com.mongodb.annotations.Reason;
import com.mongodb.internal.connection.DecimalFormatHelper;
import com.mongodb.internal.connection.Time;
import com.mongodb.lang.Nullable;
@@ -70,6 +72,10 @@ public class ServerDescription {
private final ServerAddress address;
private final ServerType type;
+ /**
+ * Identifies whether the server is a mongocryptd.
+ */
+ private final boolean cryptd;
private final String canonicalAddress;
private final Set hosts;
private final Set passives;
@@ -79,6 +85,7 @@ public class ServerDescription {
private final TagSet tagSet;
private final String setName;
private final long roundTripTimeNanos;
+ private final long minRoundTripTimeNanos;
private final boolean ok;
private final ServerConnectionState state;
@@ -159,6 +166,7 @@ public boolean isHelloOk() {
public static class Builder {
private ServerAddress address;
private ServerType type = UNKNOWN;
+ private boolean cryptd = false;
private String canonicalAddress;
private Set hosts = Collections.emptySet();
private Set passives = Collections.emptySet();
@@ -168,6 +176,7 @@ public static class Builder {
private TagSet tagSet = new TagSet();
private String setName;
private long roundTripTimeNanos;
+ private long minRoundTripTimeNanos;
private boolean ok;
private ServerConnectionState state;
private int minWireVersion = 0;
@@ -188,6 +197,7 @@ public static class Builder {
Builder(final ServerDescription serverDescription) {
this.address = serverDescription.address;
this.type = serverDescription.type;
+ this.cryptd = serverDescription.cryptd;
this.canonicalAddress = serverDescription.canonicalAddress;
this.hosts = serverDescription.hosts;
this.passives = serverDescription.passives;
@@ -245,6 +255,17 @@ public Builder type(final ServerType type) {
return this;
}
+ /**
+ * Sets whether this server is a mongocryptd.
+ *
+ * @param cryptd true if this server is a mongocryptd.
+ * @return this
+ */
+ public Builder cryptd(final boolean cryptd) {
+ this.cryptd = cryptd;
+ return this;
+ }
+
/**
* Sets all members of the replica set that are neither hidden, passive, nor arbiters.
*
@@ -315,7 +336,7 @@ public Builder tagSet(@Nullable final TagSet tagSet) {
}
/**
- * Set the time it took to make the round trip for requesting this information from the server
+ * Set the weighted average time it took to make the round trip for requesting this information from the server
*
* @param roundTripTime the time taken
* @param timeUnit the units of the time taken
@@ -326,6 +347,21 @@ public Builder roundTripTime(final long roundTripTime, final TimeUnit timeUnit)
return this;
}
+
+ /**
+ * Set the recent min time it took to make the round trip for requesting this information from the server
+ *
+ * @param minRoundTripTime the minimum time taken
+ * @param timeUnit the units of the time taken
+ * @return this
+ * @since 5.2
+ */
+ @Alpha(Reason.CLIENT)
+ public Builder minRoundTripTime(final long minRoundTripTime, final TimeUnit timeUnit) {
+ this.minRoundTripTimeNanos = timeUnit.toNanos(minRoundTripTime);
+ return this;
+ }
+
/**
* Sets the name of the replica set
*
@@ -628,6 +664,15 @@ public boolean isSecondary() {
return ok && (type == REPLICA_SET_SECONDARY || type == SHARD_ROUTER || type == STANDALONE || type == LOAD_BALANCER);
}
+ /**
+ * Returns whether this server is mongocryptd.
+ *
+ * @return true if this server is a mongocryptd.
+ */
+ public boolean isCryptd() {
+ return cryptd;
+ }
+
/**
* Get a Set of strings in the format of "[hostname]:[port]" that contains all members of the replica set that are neither hidden,
* passive, nor arbiters.
@@ -824,7 +869,7 @@ public ClusterType getClusterType() {
}
/**
- * Get the time it took to make the round trip for requesting this information from the server in nanoseconds.
+ * Get the weighted average time it took to make the round trip for requesting this information from the server in nanoseconds.
*
* @return the time taken to request the information, in nano seconds
*/
@@ -832,6 +877,17 @@ public long getRoundTripTimeNanos() {
return roundTripTimeNanos;
}
+ /**
+ * Get the recent min time it took to make the round trip for requesting this information from the server in nanoseconds.
+ *
+ * @return the recent min time taken to request the information, in nano seconds
+ * @since 5.2
+ */
+ @Alpha(Reason.CLIENT)
+ public long getMinRoundTripTimeNanos() {
+ return minRoundTripTimeNanos;
+ }
+
/**
* Gets the exception thrown while attempting to determine the server description. This is useful for diagnostic purposed when
* determining the root cause of a connectivity failure.
@@ -843,12 +899,6 @@ public Throwable getException() {
return exception;
}
- /**
- * Returns true if this instance is equals to @code{o}. Note that equality is defined to NOT include the round trip time.
- *
- * @param o the object to compare to
- * @return true if this instance is equals to @code{o}
- */
@Override
public boolean equals(final Object o) {
if (this == o) {
@@ -857,7 +907,6 @@ public boolean equals(final Object o) {
if (o == null || getClass() != o.getClass()) {
return false;
}
-
ServerDescription that = (ServerDescription) o;
if (maxDocumentSize != that.maxDocumentSize) {
@@ -928,6 +977,10 @@ public boolean equals(final Object o) {
return false;
}
+ if (cryptd != that.cryptd) {
+ return false;
+ }
+
// Compare class equality and message as exceptions rarely override equals
Class> thisExceptionClass = exception != null ? exception.getClass() : null;
Class> thatExceptionClass = that.exception != null ? that.exception.getClass() : null;
@@ -946,30 +999,9 @@ public boolean equals(final Object o) {
@Override
public int hashCode() {
- int result = address.hashCode();
- result = 31 * result + type.hashCode();
- result = 31 * result + (canonicalAddress != null ? canonicalAddress.hashCode() : 0);
- result = 31 * result + hosts.hashCode();
- result = 31 * result + passives.hashCode();
- result = 31 * result + arbiters.hashCode();
- result = 31 * result + (primary != null ? primary.hashCode() : 0);
- result = 31 * result + maxDocumentSize;
- result = 31 * result + tagSet.hashCode();
- result = 31 * result + (setName != null ? setName.hashCode() : 0);
- result = 31 * result + (electionId != null ? electionId.hashCode() : 0);
- result = 31 * result + (setVersion != null ? setVersion.hashCode() : 0);
- result = 31 * result + (topologyVersion != null ? topologyVersion.hashCode() : 0);
- result = 31 * result + (lastWriteDate != null ? lastWriteDate.hashCode() : 0);
- result = 31 * result + (int) (lastUpdateTimeNanos ^ (lastUpdateTimeNanos >>> 32));
- result = 31 * result + (ok ? 1 : 0);
- result = 31 * result + state.hashCode();
- result = 31 * result + minWireVersion;
- result = 31 * result + maxWireVersion;
- result = 31 * result + (logicalSessionTimeoutMinutes != null ? logicalSessionTimeoutMinutes.hashCode() : 0);
- result = 31 * result + (helloOk ? 1 : 0);
- result = 31 * result + (exception == null ? 0 : exception.getClass().hashCode());
- result = 31 * result + (exception == null ? 0 : exception.getMessage().hashCode());
- return result;
+ return Objects.hash(address, type, cryptd, canonicalAddress, hosts, passives, arbiters, primary, maxDocumentSize, tagSet, setName,
+ roundTripTimeNanos, minRoundTripTimeNanos, ok, state, minWireVersion, maxWireVersion, electionId, setVersion,
+ topologyVersion, lastWriteDate, lastUpdateTimeNanos, logicalSessionTimeoutMinutes, exception, helloOk);
}
@Override
@@ -977,6 +1009,7 @@ public String toString() {
return "ServerDescription{"
+ "address=" + address
+ ", type=" + type
+ + ", cryptd=" + cryptd
+ ", state=" + state
+ (state == CONNECTED
?
@@ -986,6 +1019,7 @@ public String toString() {
+ ", maxDocumentSize=" + maxDocumentSize
+ ", logicalSessionTimeoutMinutes=" + logicalSessionTimeoutMinutes
+ ", roundTripTimeNanos=" + roundTripTimeNanos
+ + ", minRoundTripTimeNanos=" + minRoundTripTimeNanos
: "")
+ (isReplicaSetMember()
?
@@ -1047,6 +1081,7 @@ private String getRoundTripFormattedInMilliseconds() {
ServerDescription(final Builder builder) {
address = notNull("address", builder.address);
type = notNull("type", builder.type);
+ cryptd = builder.cryptd;
state = notNull("state", builder.state);
canonicalAddress = builder.canonicalAddress;
hosts = builder.hosts;
@@ -1057,6 +1092,7 @@ private String getRoundTripFormattedInMilliseconds() {
tagSet = builder.tagSet;
setName = builder.setName;
roundTripTimeNanos = builder.roundTripTimeNanos;
+ minRoundTripTimeNanos = builder.minRoundTripTimeNanos;
ok = builder.ok;
minWireVersion = builder.minWireVersion;
maxWireVersion = builder.maxWireVersion;
diff --git a/driver-core/src/main/com/mongodb/internal/ExceptionUtils.java b/driver-core/src/main/com/mongodb/internal/ExceptionUtils.java
index 96083f66833..9ccb5ef0c8b 100644
--- a/driver-core/src/main/com/mongodb/internal/ExceptionUtils.java
+++ b/driver-core/src/main/com/mongodb/internal/ExceptionUtils.java
@@ -17,6 +17,8 @@
package com.mongodb.internal;
import com.mongodb.MongoCommandException;
+import com.mongodb.MongoOperationTimeoutException;
+import com.mongodb.MongoSocketException;
import org.bson.BsonArray;
import org.bson.BsonDocument;
import org.bson.BsonInt32;
@@ -35,6 +37,15 @@
* This class is not part of the public API and may be removed or changed at any time
*/
public final class ExceptionUtils {
+
+ public static boolean isMongoSocketException(final Throwable e) {
+ return e instanceof MongoSocketException;
+ }
+
+ public static boolean isOperationTimeoutFromSocketException(final Throwable e) {
+ return e instanceof MongoOperationTimeoutException && e.getCause() instanceof MongoSocketException;
+ }
+
public static final class MongoCommandExceptionUtils {
public static int extractErrorCode(final BsonDocument response) {
return extractErrorCodeAsBson(response).intValue();
diff --git a/driver-core/src/main/com/mongodb/internal/Locks.java b/driver-core/src/main/com/mongodb/internal/Locks.java
index 984de156f27..8e8260f50d3 100644
--- a/driver-core/src/main/com/mongodb/internal/Locks.java
+++ b/driver-core/src/main/com/mongodb/internal/Locks.java
@@ -17,6 +17,7 @@
package com.mongodb.internal;
import com.mongodb.MongoInterruptedException;
+import com.mongodb.internal.function.CheckedSupplier;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
diff --git a/driver-core/src/main/com/mongodb/internal/TimeoutContext.java b/driver-core/src/main/com/mongodb/internal/TimeoutContext.java
new file mode 100644
index 00000000000..0b4907c2ff1
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/internal/TimeoutContext.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.mongodb.internal;
+
+import com.mongodb.MongoClientException;
+import com.mongodb.MongoOperationTimeoutException;
+import com.mongodb.internal.time.StartTime;
+import com.mongodb.internal.time.Timeout;
+import com.mongodb.lang.Nullable;
+import com.mongodb.session.ClientSession;
+
+import java.util.Objects;
+import java.util.function.LongConsumer;
+
+import static com.mongodb.assertions.Assertions.assertNull;
+import static com.mongodb.assertions.Assertions.isTrue;
+import static com.mongodb.internal.VisibleForTesting.AccessModifier.PRIVATE;
+import static com.mongodb.internal.time.Timeout.ZeroSemantics.ZERO_DURATION_MEANS_INFINITE;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+/**
+ * Timeout Context.
+ *
+ * The context for handling timeouts in relation to the Client Side Operation Timeout specification.
+ */
+public class TimeoutContext {
+
+ private final boolean isMaintenanceContext;
+ private final TimeoutSettings timeoutSettings;
+
+ @Nullable
+ private Timeout timeout;
+ @Nullable
+ private Timeout computedServerSelectionTimeout;
+ private long minRoundTripTimeMS = 0;
+
+ @Nullable
+ private MaxTimeSupplier maxTimeSupplier = null;
+
+ public static MongoOperationTimeoutException createMongoRoundTripTimeoutException() {
+ return createMongoTimeoutException("Remaining timeoutMS is less than or equal to the server's minimum round trip time.");
+ }
+
+ public static MongoOperationTimeoutException createMongoTimeoutException(final String message) {
+ return new MongoOperationTimeoutException(message);
+ }
+
+ public static T throwMongoTimeoutException(final String message) {
+ throw new MongoOperationTimeoutException(message);
+ }
+
+ public static MongoOperationTimeoutException createMongoTimeoutException(final Throwable cause) {
+ return createMongoTimeoutException("Operation exceeded the timeout limit: " + cause.getMessage(), cause);
+ }
+
+ public static MongoOperationTimeoutException createMongoTimeoutException(final String message, final Throwable cause) {
+ if (cause instanceof MongoOperationTimeoutException) {
+ return (MongoOperationTimeoutException) cause;
+ }
+ return new MongoOperationTimeoutException(message, cause);
+ }
+
+ public static TimeoutContext createMaintenanceTimeoutContext(final TimeoutSettings timeoutSettings) {
+ return new TimeoutContext(true, timeoutSettings, startTimeout(timeoutSettings.getTimeoutMS()));
+ }
+
+ public static TimeoutContext createTimeoutContext(final ClientSession session, final TimeoutSettings timeoutSettings) {
+ TimeoutContext sessionTimeoutContext = session.getTimeoutContext();
+
+ if (sessionTimeoutContext != null) {
+ TimeoutSettings sessionTimeoutSettings = sessionTimeoutContext.timeoutSettings;
+ if (timeoutSettings.getGenerationId() > sessionTimeoutSettings.getGenerationId()) {
+ throw new MongoClientException("Cannot change the timeoutMS during a transaction.");
+ }
+
+ // Check for any legacy operation timeouts
+ if (sessionTimeoutSettings.getTimeoutMS() == null) {
+ if (timeoutSettings.getMaxTimeMS() != 0) {
+ sessionTimeoutSettings = sessionTimeoutSettings.withMaxTimeMS(timeoutSettings.getMaxTimeMS());
+ }
+ if (timeoutSettings.getMaxAwaitTimeMS() != 0) {
+ sessionTimeoutSettings = sessionTimeoutSettings.withMaxAwaitTimeMS(timeoutSettings.getMaxAwaitTimeMS());
+ }
+ if (timeoutSettings.getMaxCommitTimeMS() != null) {
+ sessionTimeoutSettings = sessionTimeoutSettings.withMaxCommitMS(timeoutSettings.getMaxCommitTimeMS());
+ }
+ return new TimeoutContext(sessionTimeoutSettings);
+ }
+ return sessionTimeoutContext;
+ }
+ return new TimeoutContext(timeoutSettings);
+ }
+
+ // Creates a copy of the timeout context that can be reset without resetting the original.
+ public TimeoutContext copyTimeoutContext() {
+ return new TimeoutContext(getTimeoutSettings(), getTimeout());
+ }
+
+ public TimeoutContext(final TimeoutSettings timeoutSettings) {
+ this(false, timeoutSettings, startTimeout(timeoutSettings.getTimeoutMS()));
+ }
+
+ private TimeoutContext(final TimeoutSettings timeoutSettings, @Nullable final Timeout timeout) {
+ this(false, timeoutSettings, timeout);
+ }
+
+ private TimeoutContext(final boolean isMaintenanceContext, final TimeoutSettings timeoutSettings, @Nullable final Timeout timeout) {
+ this.isMaintenanceContext = isMaintenanceContext;
+ this.timeoutSettings = timeoutSettings;
+ this.timeout = timeout;
+ }
+
+ /**
+ * Allows for the differentiation between users explicitly setting a global operation timeout via {@code timeoutMS}.
+ *
+ * @return true if a timeout has been set.
+ */
+ public boolean hasTimeoutMS() {
+ return timeoutSettings.getTimeoutMS() != null;
+ }
+
+ /**
+ * Runs the runnable if the timeout is expired.
+ * @param onExpired the runnable to run
+ */
+ public void onExpired(final Runnable onExpired) {
+ Timeout.nullAsInfinite(timeout).onExpired(onExpired);
+ }
+
+ /**
+ * Sets the recent min round trip time
+ * @param minRoundTripTimeMS the min round trip time
+ * @return this
+ */
+ public TimeoutContext minRoundTripTimeMS(final long minRoundTripTimeMS) {
+ isTrue("'minRoundTripTimeMS' must be a positive number", minRoundTripTimeMS >= 0);
+ this.minRoundTripTimeMS = minRoundTripTimeMS;
+ return this;
+ }
+
+ @Nullable
+ public Timeout timeoutIncludingRoundTrip() {
+ return timeout == null ? null : timeout.shortenBy(minRoundTripTimeMS, MILLISECONDS);
+ }
+
+ /**
+ * Returns the remaining {@code timeoutMS} if set or the {@code alternativeTimeoutMS}.
+ *
+ * @param alternativeTimeoutMS the alternative timeout.
+ * @return timeout to use.
+ */
+ public long timeoutOrAlternative(final long alternativeTimeoutMS) {
+ if (timeout == null) {
+ return alternativeTimeoutMS;
+ } else {
+ return timeout.call(MILLISECONDS,
+ () -> 0L,
+ (ms) -> ms,
+ () -> throwMongoTimeoutException("The operation exceeded the timeout limit."));
+ }
+ }
+
+ public TimeoutSettings getTimeoutSettings() {
+ return timeoutSettings;
+ }
+
+ public long getMaxAwaitTimeMS() {
+ return timeoutSettings.getMaxAwaitTimeMS();
+ }
+
+ public void runMaxTimeMS(final LongConsumer onRemaining) {
+ if (maxTimeSupplier != null) {
+ runWithFixedTimeout(maxTimeSupplier.get(), onRemaining);
+ return;
+ }
+ if (timeout == null) {
+ runWithFixedTimeout(timeoutSettings.getMaxTimeMS(), onRemaining);
+ return;
+ }
+ timeout.shortenBy(minRoundTripTimeMS, MILLISECONDS)
+ .run(MILLISECONDS,
+ () -> {},
+ onRemaining,
+ () -> {
+ throw createMongoRoundTripTimeoutException();
+ });
+
+ }
+
+ private static void runWithFixedTimeout(final long ms, final LongConsumer onRemaining) {
+ if (ms != 0) {
+ onRemaining.accept(ms);
+ }
+ }
+
+ public void resetToDefaultMaxTime() {
+ this.maxTimeSupplier = null;
+ }
+
+ /**
+ * The override will be provided as the remaining value in
+ * {@link #runMaxTimeMS}, where 0 is ignored.
+ *
+ * NOTE: Suitable for static user-defined values only (i.e MaxAwaitTimeMS),
+ * not for running timeouts that adjust dynamically.
+ */
+ public void setMaxTimeOverride(final long maxTimeMS) {
+ this.maxTimeSupplier = () -> maxTimeMS;
+ }
+
+ /**
+ * The override will be provided as the remaining value in
+ * {@link #runMaxTimeMS}, where 0 is ignored.
+ */
+ public void setMaxTimeOverrideToMaxCommitTime() {
+ this.maxTimeSupplier = () -> getMaxCommitTimeMS();
+ }
+
+ @VisibleForTesting(otherwise = PRIVATE)
+ public long getMaxCommitTimeMS() {
+ Long maxCommitTimeMS = timeoutSettings.getMaxCommitTimeMS();
+ return timeoutOrAlternative(maxCommitTimeMS != null ? maxCommitTimeMS : 0);
+ }
+
+ public long getReadTimeoutMS() {
+ return timeoutOrAlternative(timeoutSettings.getReadTimeoutMS());
+ }
+
+ public long getWriteTimeoutMS() {
+ return timeoutOrAlternative(0);
+ }
+
+ public int getConnectTimeoutMs() {
+ final long connectTimeoutMS = getTimeoutSettings().getConnectTimeoutMS();
+ return Math.toIntExact(Timeout.nullAsInfinite(timeout).call(MILLISECONDS,
+ () -> connectTimeoutMS,
+ (ms) -> connectTimeoutMS == 0 ? ms : Math.min(ms, connectTimeoutMS),
+ () -> throwMongoTimeoutException("The operation exceeded the timeout limit.")));
+ }
+
+ public void resetTimeoutIfPresent() {
+ if (hasTimeoutMS()) {
+ timeout = startTimeout(timeoutSettings.getTimeoutMS());
+ }
+ }
+
+ /**
+ * Resets the timeout if this timeout context is being used by pool maintenance
+ */
+ public void resetMaintenanceTimeout() {
+ if (!isMaintenanceContext) {
+ return;
+ }
+ timeout = Timeout.nullAsInfinite(timeout).call(NANOSECONDS,
+ () -> timeout,
+ (ms) -> startTimeout(timeoutSettings.getTimeoutMS()),
+ () -> startTimeout(timeoutSettings.getTimeoutMS()));
+ }
+
+ public TimeoutContext withAdditionalReadTimeout(final int additionalReadTimeout) {
+ // Only used outside timeoutMS usage
+ assertNull(timeout);
+
+ // Check existing read timeout is infinite
+ if (timeoutSettings.getReadTimeoutMS() == 0) {
+ return this;
+ }
+
+ long newReadTimeout = getReadTimeoutMS() + additionalReadTimeout;
+ return new TimeoutContext(timeoutSettings.withReadTimeoutMS(newReadTimeout > 0 ? newReadTimeout : Long.MAX_VALUE));
+ }
+
+ @Override
+ public String toString() {
+ return "TimeoutContext{"
+ + "isMaintenanceContext=" + isMaintenanceContext
+ + ", timeoutSettings=" + timeoutSettings
+ + ", timeout=" + timeout
+ + ", minRoundTripTimeMS=" + minRoundTripTimeMS
+ + '}';
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final TimeoutContext that = (TimeoutContext) o;
+ return isMaintenanceContext == that.isMaintenanceContext
+ && minRoundTripTimeMS == that.minRoundTripTimeMS
+ && Objects.equals(timeoutSettings, that.timeoutSettings)
+ && Objects.equals(timeout, that.timeout);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(isMaintenanceContext, timeoutSettings, timeout, minRoundTripTimeMS);
+ }
+
+ @Nullable
+ public static Timeout startTimeout(@Nullable final Long timeoutMS) {
+ if (timeoutMS != null) {
+ return Timeout.expiresIn(timeoutMS, MILLISECONDS, ZERO_DURATION_MEANS_INFINITE);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the computed server selection timeout
+ *
+ *
Caches the computed server selection timeout if:
+ *
+ * - not in a maintenance context
+ * - there is a timeoutMS, so to keep the same legacy behavior.
+ * - the server selection timeout is less than the remaining overall timeout.
+ *
+ *
+ * @return the timeout context
+ */
+ public Timeout computeServerSelectionTimeout() {
+ Timeout serverSelectionTimeout = StartTime.now()
+ .timeoutAfterOrInfiniteIfNegative(getTimeoutSettings().getServerSelectionTimeoutMS(), MILLISECONDS);
+
+
+ if (isMaintenanceContext || !hasTimeoutMS()) {
+ return serverSelectionTimeout;
+ }
+
+ if (timeout != null && Timeout.earliest(serverSelectionTimeout, timeout) == timeout) {
+ return timeout;
+ }
+
+ computedServerSelectionTimeout = serverSelectionTimeout;
+ return computedServerSelectionTimeout;
+ }
+
+ /**
+ * Returns the timeout context to use for the handshake process
+ *
+ * @return a new timeout context with the cached computed server selection timeout if available or this
+ */
+ public TimeoutContext withComputedServerSelectionTimeoutContext() {
+ if (this.hasTimeoutMS() && computedServerSelectionTimeout != null) {
+ return new TimeoutContext(false, timeoutSettings, computedServerSelectionTimeout);
+ }
+ return this;
+ }
+
+ public Timeout startWaitQueueTimeout(final StartTime checkoutStart) {
+ final long ms = getTimeoutSettings().getMaxWaitTimeMS();
+ return checkoutStart.timeoutAfterOrInfiniteIfNegative(ms, MILLISECONDS);
+ }
+
+ @Nullable
+ public Timeout getTimeout() {
+ return timeout;
+ }
+
+ public interface MaxTimeSupplier {
+ long get();
+ }
+}
diff --git a/driver-core/src/main/com/mongodb/internal/TimeoutSettings.java b/driver-core/src/main/com/mongodb/internal/TimeoutSettings.java
new file mode 100644
index 00000000000..486a893d74c
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/internal/TimeoutSettings.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.mongodb.internal;
+
+import com.mongodb.MongoClientSettings;
+import com.mongodb.lang.Nullable;
+
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static com.mongodb.assertions.Assertions.isTrueArgument;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+/**
+ * Timeout Settings.
+ *
+ * Includes all client based timeouts
+ */
+public class TimeoutSettings {
+ private static final AtomicLong NEXT_ID = new AtomicLong(0);
+ private final long generationId;
+ private final long serverSelectionTimeoutMS;
+ private final long connectTimeoutMS;
+ @Nullable
+ private final Long timeoutMS;
+
+ // Deprecated configuration timeout options
+ private final long readTimeoutMS; // aka socketTimeoutMS
+ private final long maxWaitTimeMS; // aka waitQueueTimeoutMS
+ @Nullable
+ private final Long wTimeoutMS;
+
+ // Deprecated options for CRUD methods
+ private final long maxTimeMS;
+ private final long maxAwaitTimeMS;
+ @Nullable
+ private final Long maxCommitTimeMS;
+
+ public static final TimeoutSettings DEFAULT = create(MongoClientSettings.builder().build());
+
+ @Nullable
+ public static Long convertAndValidateTimeoutNullable(@Nullable final Long timeout, final TimeUnit timeUnit) {
+ return timeout == null ? null : convertAndValidateTimeout(timeout, timeUnit, "timeout");
+ }
+
+ public static long convertAndValidateTimeout(final long timeout, final TimeUnit timeUnit) {
+ return convertAndValidateTimeout(timeout, timeUnit, "timeout");
+ }
+
+ public static long convertAndValidateTimeout(final long timeout, final TimeUnit timeUnit, final String fieldName) {
+ return isTrueArgument(fieldName + " was too small. After conversion it was rounded to 0 milliseconds, "
+ + " which would result in an unintended infinite timeout.",
+ () -> MILLISECONDS.convert(timeout, timeUnit),
+ (timeoutMS) -> timeout == 0 && timeoutMS == 0 || timeoutMS > 0);
+ }
+
+ @SuppressWarnings("deprecation")
+ public static TimeoutSettings create(final MongoClientSettings settings) {
+ return new TimeoutSettings(
+ settings.getClusterSettings().getServerSelectionTimeout(TimeUnit.MILLISECONDS),
+ settings.getSocketSettings().getConnectTimeout(TimeUnit.MILLISECONDS),
+ settings.getSocketSettings().getReadTimeout(TimeUnit.MILLISECONDS),
+ settings.getTimeout(TimeUnit.MILLISECONDS),
+ settings.getConnectionPoolSettings().getMaxWaitTime(TimeUnit.MILLISECONDS));
+ }
+
+ public static TimeoutSettings createHeartbeatSettings(final MongoClientSettings settings) {
+ return new TimeoutSettings(
+ settings.getClusterSettings().getServerSelectionTimeout(TimeUnit.MILLISECONDS),
+ settings.getHeartbeatSocketSettings().getConnectTimeout(TimeUnit.MILLISECONDS),
+ settings.getHeartbeatSocketSettings().getReadTimeout(TimeUnit.MILLISECONDS),
+ settings.getTimeout(TimeUnit.MILLISECONDS),
+ settings.getConnectionPoolSettings().getMaxWaitTime(TimeUnit.MILLISECONDS));
+ }
+
+ public TimeoutSettings(final long serverSelectionTimeoutMS, final long connectTimeoutMS, final long readTimeoutMS,
+ @Nullable final Long timeoutMS, final long maxWaitTimeMS) {
+ this(-1, timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, 0, 0, null, null, maxWaitTimeMS);
+ }
+
+ TimeoutSettings(@Nullable final Long timeoutMS, final long serverSelectionTimeoutMS, final long connectTimeoutMS,
+ final long readTimeoutMS, final long maxAwaitTimeMS, final long maxTimeMS, @Nullable final Long maxCommitTimeMS,
+ @Nullable final Long wTimeoutMS, final long maxWaitTimeMS) {
+ this(timeoutMS != null ? NEXT_ID.incrementAndGet() : -1, timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS,
+ maxAwaitTimeMS, maxTimeMS, maxCommitTimeMS, wTimeoutMS, maxWaitTimeMS);
+ }
+
+ private TimeoutSettings(final long generationId, @Nullable final Long timeoutMS, final long serverSelectionTimeoutMS,
+ final long connectTimeoutMS, final long readTimeoutMS, final long maxAwaitTimeMS, final long maxTimeMS,
+ @Nullable final Long maxCommitTimeMS, @Nullable final Long wTimeoutMS, final long maxWaitTimeMS) {
+
+ isTrueArgument("timeoutMS must be >= 0", timeoutMS == null || timeoutMS >= 0);
+ isTrueArgument("maxAwaitTimeMS must be >= 0", maxAwaitTimeMS >= 0);
+ isTrueArgument("maxTimeMS must be >= 0", maxTimeMS >= 0);
+ isTrueArgument("timeoutMS must be greater than maxAwaitTimeMS", timeoutMS == null || timeoutMS == 0
+ || timeoutMS > maxAwaitTimeMS);
+ isTrueArgument("maxCommitTimeMS must be >= 0", maxCommitTimeMS == null || maxCommitTimeMS >= 0);
+
+ this.generationId = generationId;
+ this.serverSelectionTimeoutMS = serverSelectionTimeoutMS;
+ this.connectTimeoutMS = connectTimeoutMS;
+ this.timeoutMS = timeoutMS;
+ this.maxAwaitTimeMS = maxAwaitTimeMS;
+ this.readTimeoutMS = readTimeoutMS;
+ this.maxTimeMS = maxTimeMS;
+ this.maxCommitTimeMS = maxCommitTimeMS;
+ this.wTimeoutMS = wTimeoutMS;
+ this.maxWaitTimeMS = maxWaitTimeMS;
+ }
+
+ public TimeoutSettings connectionOnly() {
+ return new TimeoutSettings(serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, null, maxWaitTimeMS);
+ }
+
+ public TimeoutSettings withTimeout(@Nullable final Long timeout, final TimeUnit timeUnit) {
+ return withTimeoutMS(convertAndValidateTimeoutNullable(timeout, timeUnit));
+ }
+
+ TimeoutSettings withTimeoutMS(@Nullable final Long timeoutMS) {
+ return new TimeoutSettings(timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, maxAwaitTimeMS,
+ maxTimeMS, maxCommitTimeMS, wTimeoutMS, maxWaitTimeMS);
+ }
+
+ public TimeoutSettings withMaxTimeMS(final long maxTimeMS) {
+ return new TimeoutSettings(generationId, timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, maxAwaitTimeMS,
+ maxTimeMS, maxCommitTimeMS, wTimeoutMS, maxWaitTimeMS);
+ }
+
+ public TimeoutSettings withMaxAwaitTimeMS(final long maxAwaitTimeMS) {
+ return new TimeoutSettings(generationId, timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, maxAwaitTimeMS,
+ maxTimeMS, maxCommitTimeMS, wTimeoutMS, maxWaitTimeMS);
+ }
+
+ public TimeoutSettings withMaxTimeAndMaxAwaitTimeMS(final long maxTimeMS, final long maxAwaitTimeMS) {
+ return new TimeoutSettings(generationId, timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, maxAwaitTimeMS,
+ maxTimeMS, maxCommitTimeMS, wTimeoutMS, maxWaitTimeMS);
+ }
+
+ public TimeoutSettings withMaxCommitMS(@Nullable final Long maxCommitTimeMS) {
+ return new TimeoutSettings(generationId, timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, maxAwaitTimeMS,
+ maxTimeMS, maxCommitTimeMS, wTimeoutMS, maxWaitTimeMS);
+ }
+
+ public TimeoutSettings withWTimeoutMS(@Nullable final Long wTimeoutMS) {
+ return new TimeoutSettings(timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, maxAwaitTimeMS,
+ maxTimeMS, maxCommitTimeMS, wTimeoutMS, maxWaitTimeMS);
+ }
+
+ public TimeoutSettings withReadTimeoutMS(final long readTimeoutMS) {
+ return new TimeoutSettings(generationId, timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, maxAwaitTimeMS,
+ maxTimeMS, maxCommitTimeMS, wTimeoutMS, maxWaitTimeMS);
+ }
+
+ public TimeoutSettings withServerSelectionTimeoutMS(final long serverSelectionTimeoutMS) {
+ return new TimeoutSettings(timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, maxAwaitTimeMS,
+ maxTimeMS, maxCommitTimeMS, wTimeoutMS, maxWaitTimeMS);
+ }
+
+ public TimeoutSettings withMaxWaitTimeMS(final long maxWaitTimeMS) {
+ return new TimeoutSettings(timeoutMS, serverSelectionTimeoutMS, connectTimeoutMS, readTimeoutMS, maxAwaitTimeMS,
+ maxTimeMS, maxCommitTimeMS, wTimeoutMS, maxWaitTimeMS);
+ }
+
+ public long getServerSelectionTimeoutMS() {
+ return serverSelectionTimeoutMS;
+ }
+
+ public long getConnectTimeoutMS() {
+ return connectTimeoutMS;
+ }
+
+ @Nullable
+ public Long getTimeoutMS() {
+ return timeoutMS;
+ }
+
+ public long getMaxAwaitTimeMS() {
+ return maxAwaitTimeMS;
+ }
+
+ public long getReadTimeoutMS() {
+ return readTimeoutMS;
+ }
+
+ public long getMaxTimeMS() {
+ return maxTimeMS;
+ }
+
+ @Nullable
+ public Long getWTimeoutMS() {
+ return wTimeoutMS;
+ }
+
+ public long getMaxWaitTimeMS() {
+ return maxWaitTimeMS;
+ }
+
+ @Nullable
+ public Long getMaxCommitTimeMS() {
+ return maxCommitTimeMS;
+ }
+
+ /**
+ * The generation id represents a creation counter for {@code TimeoutSettings} that contain a {@code timeoutMS} value.
+ *
+ * This is used to determine if a new set of {@code TimeoutSettings} has been created within a {@code withTransaction}
+ * block, so that a client side error can be issued.
+ *
+ * @return the generation id or -1 if no timeout MS is set.
+ */
+ public long getGenerationId() {
+ return generationId;
+ }
+
+ @Override
+ public String toString() {
+ return "TimeoutSettings{"
+ + "generationId=" + generationId
+ + ", timeoutMS=" + timeoutMS
+ + ", serverSelectionTimeoutMS=" + serverSelectionTimeoutMS
+ + ", connectTimeoutMS=" + connectTimeoutMS
+ + ", readTimeoutMS=" + readTimeoutMS
+ + ", maxWaitTimeMS=" + maxWaitTimeMS
+ + ", wTimeoutMS=" + wTimeoutMS
+ + ", maxTimeMS=" + maxTimeMS
+ + ", maxAwaitTimeMS=" + maxAwaitTimeMS
+ + ", maxCommitTimeMS=" + maxCommitTimeMS
+ + '}';
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final TimeoutSettings that = (TimeoutSettings) o;
+ return serverSelectionTimeoutMS == that.serverSelectionTimeoutMS && connectTimeoutMS == that.connectTimeoutMS
+ && readTimeoutMS == that.readTimeoutMS && maxWaitTimeMS == that.maxWaitTimeMS && maxTimeMS == that.maxTimeMS
+ && maxAwaitTimeMS == that.maxAwaitTimeMS && Objects.equals(timeoutMS, that.timeoutMS)
+ && Objects.equals(wTimeoutMS, that.wTimeoutMS) && Objects.equals(maxCommitTimeMS, that.maxCommitTimeMS);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(generationId, serverSelectionTimeoutMS, connectTimeoutMS, timeoutMS, readTimeoutMS, maxWaitTimeMS, wTimeoutMS, maxTimeMS,
+ maxAwaitTimeMS, maxCommitTimeMS);
+ }
+}
diff --git a/driver-core/src/main/com/mongodb/internal/async/AsyncRunnable.java b/driver-core/src/main/com/mongodb/internal/async/AsyncRunnable.java
index 33e1af001bb..a81b2fdd12c 100644
--- a/driver-core/src/main/com/mongodb/internal/async/AsyncRunnable.java
+++ b/driver-core/src/main/com/mongodb/internal/async/AsyncRunnable.java
@@ -16,6 +16,7 @@
package com.mongodb.internal.async;
+import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.async.function.RetryState;
import com.mongodb.internal.async.function.RetryingAsyncCallbackSupplier;
@@ -267,10 +268,10 @@ default AsyncSupplier thenSupply(final AsyncSupplier supplier) {
* @see RetryingAsyncCallbackSupplier
*/
default AsyncRunnable thenRunRetryingWhile(
- final AsyncRunnable runnable, final Predicate shouldRetry) {
+ final TimeoutContext timeoutContext, final AsyncRunnable runnable, final Predicate shouldRetry) {
return thenRun(callback -> {
new RetryingAsyncCallbackSupplier(
- new RetryState(),
+ new RetryState(timeoutContext),
(rs, lastAttemptFailure) -> shouldRetry.test(lastAttemptFailure),
// `finish` is required here instead of `unsafeFinish`
// because only `finish` meets the contract of
diff --git a/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java b/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java
index 89329f16a24..e1cecf721fc 100644
--- a/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java
+++ b/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java
@@ -15,7 +15,9 @@
*/
package com.mongodb.internal.async.function;
+import com.mongodb.MongoOperationTimeoutException;
import com.mongodb.annotations.NotThreadSafe;
+import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.async.function.LoopState.AttachmentKey;
import com.mongodb.lang.NonNull;
@@ -29,6 +31,7 @@
import static com.mongodb.assertions.Assertions.assertFalse;
import static com.mongodb.assertions.Assertions.assertNotNull;
import static com.mongodb.assertions.Assertions.assertTrue;
+import static com.mongodb.internal.TimeoutContext.createMongoTimeoutException;
/**
* Represents both the state associated with a retryable activity and a handle that can be used to affect retrying, e.g.,
@@ -48,25 +51,62 @@ public final class RetryState {
private final LoopState loopState;
private final int attempts;
+ private final boolean retryUntilTimeoutThrowsException;
@Nullable
- private Throwable exception;
+ private Throwable previouslyChosenException;
/**
- * @param retries A non-negative number of allowed retries. {@link Integer#MAX_VALUE} is a special value interpreted as being unlimited.
+ * Creates a {@code RetryState} with a positive number of allowed retries. {@link Integer#MAX_VALUE} is a special value interpreted as
+ * being unlimited.
+ *
+ * If a timeout is not specified in the {@link TimeoutContext#hasTimeoutMS()}, the specified {@code retries} param acts as a fallback
+ * bound. Otherwise, retries are unbounded until the timeout is reached.
+ *
+ * It is possible to provide an additional {@code retryPredicate} in the {@link #doAdvanceOrThrow} method,
+ * which can be used to stop retrying based on a custom condition additionally to {@code retires} and {@link TimeoutContext}.
+ *
+ *
+ * @param retries A positive number of allowed retries. {@link Integer#MAX_VALUE} is a special value interpreted as being unlimited.
+ * @param timeoutContext A timeout context that will be used to determine if the operation has timed out.
* @see #attempts()
*/
- public RetryState(final int retries) {
- assertTrue(retries >= 0);
- loopState = new LoopState();
- attempts = retries == INFINITE_ATTEMPTS ? INFINITE_ATTEMPTS : retries + 1;
+ public static RetryState withRetryableState(final int retries, final TimeoutContext timeoutContext) {
+ assertTrue(retries > 0);
+ if (timeoutContext.hasTimeoutMS()){
+ return new RetryState(INFINITE_ATTEMPTS, timeoutContext);
+ }
+ return new RetryState(retries, null);
+ }
+
+ public static RetryState withNonRetryableState() {
+ return new RetryState(0, null);
}
/**
* Creates a {@link RetryState} that does not limit the number of retries.
+ * The number of attempts is limited iff {@link TimeoutContext#hasTimeoutMS()} is true and timeout has expired.
+ *
+ * It is possible to provide an additional {@code retryPredicate} in the {@link #doAdvanceOrThrow} method,
+ * which can be used to stop retrying based on a custom condition additionally to {@code retires} and {@link TimeoutContext}.
+ *
+ *
+ * @param timeoutContext A timeout context that will be used to determine if the operation has timed out.
+ * @see #attempts()
+ */
+ public RetryState(final TimeoutContext timeoutContext) {
+ this(INFINITE_ATTEMPTS, timeoutContext);
+ }
+
+ /**
+ * @param retries A non-negative number of allowed retries. {@link Integer#MAX_VALUE} is a special value interpreted as being unlimited.
+ * @param timeoutContext A timeout context that will be used to determine if the operation has timed out.
* @see #attempts()
*/
- public RetryState() {
- this(INFINITE_ATTEMPTS);
+ private RetryState(final int retries, @Nullable final TimeoutContext timeoutContext) {
+ assertTrue(retries >= 0);
+ loopState = new LoopState();
+ attempts = retries == INFINITE_ATTEMPTS ? INFINITE_ATTEMPTS : retries + 1;
+ this.retryUntilTimeoutThrowsException = timeoutContext != null && timeoutContext.hasTimeoutMS();
}
/**
@@ -136,7 +176,7 @@ void advanceOrThrow(final Throwable attemptException, final BinaryOperator predicate) throws RuntimeException {
assertFalse(loopState.isLastIteration());
if (!isFirstAttempt()) {
- assertNotNull(exception);
- assertTrue(exception instanceof RuntimeException);
- RuntimeException localException = (RuntimeException) exception;
+ assertNotNull(previouslyChosenException);
+ assertTrue(previouslyChosenException instanceof RuntimeException);
+ RuntimeException localException = (RuntimeException) previouslyChosenException;
try {
if (predicate.get()) {
loopState.markAsLastIteration();
@@ -310,14 +367,23 @@ public boolean isFirstAttempt() {
/**
* Returns {@code true} iff the current attempt is known to be the last one, i.e., it is known that no more retries will be made.
- * An attempt is known to be the last one either because the number of {@linkplain #attempts() attempts} is limited and the current
- * attempt is the last one, or because {@link #breakAndThrowIfRetryAnd(Supplier)} /
- * {@link #breakAndCompleteIfRetryAnd(Supplier, SingleResultCallback)} / {@link #markAsLastAttempt()} was called.
+ * An attempt is known to be the last one iff any of the following applies:
+ *
+ * - {@link #breakAndThrowIfRetryAnd(Supplier)} / {@link #breakAndCompleteIfRetryAnd(Supplier, SingleResultCallback)} / {@link #markAsLastAttempt()} was called.
+ * - A timeout is set and has been reached.
+ * - No timeout is set, and the number of {@linkplain #attempts() attempts} is limited, and the current attempt is the last one.
+ *
*
* @see #attempts()
*/
public boolean isLastAttempt() {
- return attempt() == attempts - 1 || loopState.isLastIteration();
+ if (loopState.isLastIteration()){
+ return true;
+ }
+ if (retryUntilTimeoutThrowsException) {
+ return false;
+ }
+ return attempt() == attempts - 1;
}
/**
@@ -332,9 +398,9 @@ public int attempt() {
/**
* Returns a positive maximum number of attempts:
*
- * - 0 if the number of retries is {@linkplain #RetryState() unlimited};
+ * - 0 if the number of retries is {@linkplain #RetryState(TimeoutContext) unlimited};
* - 1 if no retries are allowed;
- * - {@link #RetryState(int) retries} + 1 otherwise.
+ * - {@link #RetryState(int, TimeoutContext) retries} + 1 otherwise.
*
*
* @see #attempt()
@@ -353,8 +419,8 @@ public int attempts() {
* In synchronous code the returned exception is of the type {@link RuntimeException}.
*/
public Optional exception() {
- assertTrue(exception == null || !isFirstAttempt());
- return Optional.ofNullable(exception);
+ assertTrue(previouslyChosenException == null || !isFirstAttempt());
+ return Optional.ofNullable(previouslyChosenException);
}
/**
@@ -377,7 +443,7 @@ public String toString() {
return "RetryState{"
+ "loopState=" + loopState
+ ", attempts=" + (attempts == INFINITE_ATTEMPTS ? "infinite" : attempts)
- + ", exception=" + exception
+ + ", exception=" + previouslyChosenException
+ '}';
}
}
diff --git a/driver-core/src/main/com/mongodb/internal/async/package-info.java b/driver-core/src/main/com/mongodb/internal/async/package-info.java
index f6f0693821d..39b952eead1 100644
--- a/driver-core/src/main/com/mongodb/internal/async/package-info.java
+++ b/driver-core/src/main/com/mongodb/internal/async/package-info.java
@@ -15,7 +15,6 @@
*/
/**
- * This package contains cluster and connection event related classes
*/
@NonNullApi
diff --git a/driver-core/src/main/com/mongodb/internal/authentication/package-info.java b/driver-core/src/main/com/mongodb/internal/authentication/package-info.java
index 7a697f21ace..bbeb09628af 100644
--- a/driver-core/src/main/com/mongodb/internal/authentication/package-info.java
+++ b/driver-core/src/main/com/mongodb/internal/authentication/package-info.java
@@ -15,7 +15,6 @@
*/
/**
- * This package contains cluster and connection event related classes
*/
@NonNullApi
diff --git a/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterBinding.java b/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterBinding.java
index acf75a3b1e8..fd46261a6df 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterBinding.java
@@ -18,26 +18,22 @@
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
-import com.mongodb.RequestContext;
import com.mongodb.ServerAddress;
-import com.mongodb.ServerApi;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ServerDescription;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.connection.AsyncConnection;
import com.mongodb.internal.connection.Cluster;
import com.mongodb.internal.connection.OperationContext;
-import com.mongodb.internal.connection.ReadConcernAwareNoOpSessionContext;
import com.mongodb.internal.connection.Server;
import com.mongodb.internal.selector.ReadPreferenceServerSelector;
import com.mongodb.internal.selector.ReadPreferenceWithFallbackServerSelector;
import com.mongodb.internal.selector.ServerAddressSelector;
import com.mongodb.internal.selector.WritableServerSelector;
-import com.mongodb.internal.session.SessionContext;
-import com.mongodb.lang.Nullable;
import com.mongodb.selector.ServerSelector;
import static com.mongodb.assertions.Assertions.notNull;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
/**
* A simple ReadWriteBinding implementation that supplies write connection sources bound to a possibly different primary each time, and a
@@ -49,9 +45,6 @@ public class AsyncClusterBinding extends AbstractReferenceCounted implements Asy
private final Cluster cluster;
private final ReadPreference readPreference;
private final ReadConcern readConcern;
- @Nullable
- private final ServerApi serverApi;
- private final RequestContext requestContext;
private final OperationContext operationContext;
/**
@@ -60,18 +53,15 @@ public class AsyncClusterBinding extends AbstractReferenceCounted implements Asy
* @param cluster a non-null Cluster which will be used to select a server to bind to
* @param readPreference a non-null ReadPreference for read operations
* @param readConcern a non-null read concern
- * @param serverApi a server API, which may be null
- * @param requestContext the request context
+ * @param operationContext the operation context
* This class is not part of the public API and may be removed or changed at any time
*/
public AsyncClusterBinding(final Cluster cluster, final ReadPreference readPreference, final ReadConcern readConcern,
- @Nullable final ServerApi serverApi, final RequestContext requestContext) {
+ final OperationContext operationContext) {
this.cluster = notNull("cluster", cluster);
this.readPreference = notNull("readPreference", readPreference);
- this.readConcern = (notNull("readConcern", readConcern));
- this.serverApi = serverApi;
- this.requestContext = notNull("requestContext", requestContext);
- operationContext = new OperationContext();
+ this.readConcern = notNull("readConcern", readConcern);
+ this.operationContext = notNull("operationContext", operationContext);
}
@Override
@@ -85,22 +75,6 @@ public ReadPreference getReadPreference() {
return readPreference;
}
- @Override
- public SessionContext getSessionContext() {
- return new ReadConcernAwareNoOpSessionContext(readConcern);
- }
-
- @Override
- @Nullable
- public ServerApi getServerApi() {
- return serverApi;
- }
-
- @Override
- public RequestContext getRequestContext() {
- return requestContext;
- }
-
@Override
public OperationContext getOperationContext() {
return operationContext;
@@ -163,6 +137,7 @@ private AsyncClusterBindingConnectionSource(final Server server, final ServerDes
this.server = server;
this.serverDescription = serverDescription;
this.appliedReadPreference = appliedReadPreference;
+ operationContext.getTimeoutContext().minRoundTripTimeMS(NANOSECONDS.toMillis(serverDescription.getMinRoundTripTimeNanos()));
AsyncClusterBinding.this.retain();
}
@@ -171,22 +146,6 @@ public ServerDescription getServerDescription() {
return serverDescription;
}
- @Override
- public SessionContext getSessionContext() {
- return new ReadConcernAwareNoOpSessionContext(readConcern);
- }
-
- @Override
- @Nullable
- public ServerApi getServerApi() {
- return serverApi;
- }
-
- @Override
- public RequestContext getRequestContext() {
- return requestContext;
- }
-
@Override
public OperationContext getOperationContext() {
return operationContext;
diff --git a/driver-core/src/main/com/mongodb/internal/binding/BindingContext.java b/driver-core/src/main/com/mongodb/internal/binding/BindingContext.java
index c98e88232ba..c10f0fb16ac 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/BindingContext.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/BindingContext.java
@@ -16,23 +16,18 @@
package com.mongodb.internal.binding;
-import com.mongodb.RequestContext;
-import com.mongodb.ServerApi;
import com.mongodb.internal.connection.OperationContext;
-import com.mongodb.internal.session.SessionContext;
-import com.mongodb.lang.Nullable;
/**
* This class is not part of the public API and may be removed or changed at any time
*/
public interface BindingContext {
- SessionContext getSessionContext();
-
- @Nullable
- ServerApi getServerApi();
-
- RequestContext getRequestContext();
+ /**
+ * Note: Will return the same operation context if called multiple times.
+ *
+ * @return the operation context for the binding context.
+ */
OperationContext getOperationContext();
}
diff --git a/driver-core/src/main/com/mongodb/internal/binding/ClusterBinding.java b/driver-core/src/main/com/mongodb/internal/binding/ClusterBinding.java
index a2223d02014..cd3f8473bbb 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/ClusterBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/ClusterBinding.java
@@ -18,25 +18,21 @@
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
-import com.mongodb.RequestContext;
import com.mongodb.ServerAddress;
-import com.mongodb.ServerApi;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ServerDescription;
import com.mongodb.internal.connection.Cluster;
import com.mongodb.internal.connection.Connection;
import com.mongodb.internal.connection.OperationContext;
-import com.mongodb.internal.connection.ReadConcernAwareNoOpSessionContext;
import com.mongodb.internal.connection.Server;
import com.mongodb.internal.connection.ServerTuple;
import com.mongodb.internal.selector.ReadPreferenceServerSelector;
import com.mongodb.internal.selector.ReadPreferenceWithFallbackServerSelector;
import com.mongodb.internal.selector.ServerAddressSelector;
import com.mongodb.internal.selector.WritableServerSelector;
-import com.mongodb.internal.session.SessionContext;
-import com.mongodb.lang.Nullable;
import static com.mongodb.assertions.Assertions.notNull;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
/**
* A simple ReadWriteBinding implementation that supplies write connection sources bound to a possibly different primary each time, and a
@@ -48,27 +44,21 @@ public class ClusterBinding extends AbstractReferenceCounted implements ClusterA
private final Cluster cluster;
private final ReadPreference readPreference;
private final ReadConcern readConcern;
- @Nullable
- private final ServerApi serverApi;
- private final RequestContext requestContext;
private final OperationContext operationContext;
/**
* Creates an instance.
- * @param cluster a non-null Cluster which will be used to select a server to bind to
- * @param readPreference a non-null ReadPreference for read operations
- * @param readConcern a non-null read concern
- * @param serverApi a server API, which may be null
- * @param requestContext the request context
+ * @param cluster a non-null Cluster which will be used to select a server to bind to
+ * @param readPreference a non-null ReadPreference for read operations
+ * @param readConcern a non-null read concern
+ * @param operationContext the operation context
*/
public ClusterBinding(final Cluster cluster, final ReadPreference readPreference, final ReadConcern readConcern,
- @Nullable final ServerApi serverApi, final RequestContext requestContext) {
+ final OperationContext operationContext) {
this.cluster = notNull("cluster", cluster);
this.readPreference = notNull("readPreference", readPreference);
this.readConcern = notNull("readConcern", readConcern);
- this.serverApi = serverApi;
- this.requestContext = notNull("requestContext", requestContext);
- operationContext = new OperationContext();
+ this.operationContext = notNull("operationContext", operationContext);
}
@Override
@@ -82,22 +72,6 @@ public ReadPreference getReadPreference() {
return readPreference;
}
- @Override
- public SessionContext getSessionContext() {
- return new ReadConcernAwareNoOpSessionContext(readConcern);
- }
-
- @Override
- @Nullable
- public ServerApi getServerApi() {
- return serverApi;
- }
-
- @Override
- public RequestContext getRequestContext() {
- return requestContext;
- }
-
@Override
public OperationContext getOperationContext() {
return operationContext;
@@ -140,6 +114,7 @@ private ClusterBindingConnectionSource(final ServerTuple serverTuple, final Read
this.server = serverTuple.getServer();
this.serverDescription = serverTuple.getServerDescription();
this.appliedReadPreference = appliedReadPreference;
+ operationContext.getTimeoutContext().minRoundTripTimeMS(NANOSECONDS.toMillis(serverDescription.getMinRoundTripTimeNanos()));
ClusterBinding.this.retain();
}
@@ -148,26 +123,11 @@ public ServerDescription getServerDescription() {
return serverDescription;
}
- @Override
- public SessionContext getSessionContext() {
- return new ReadConcernAwareNoOpSessionContext(readConcern);
- }
-
@Override
public OperationContext getOperationContext() {
return operationContext;
}
- @Override
- public ServerApi getServerApi() {
- return serverApi;
- }
-
- @Override
- public RequestContext getRequestContext() {
- return requestContext;
- }
-
@Override
public ReadPreference getReadPreference() {
return appliedReadPreference;
diff --git a/driver-core/src/main/com/mongodb/internal/binding/SingleServerBinding.java b/driver-core/src/main/com/mongodb/internal/binding/SingleServerBinding.java
index 47bb2be22fb..7d7e948c344 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/SingleServerBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/SingleServerBinding.java
@@ -17,18 +17,13 @@
package com.mongodb.internal.binding;
import com.mongodb.ReadPreference;
-import com.mongodb.RequestContext;
import com.mongodb.ServerAddress;
-import com.mongodb.ServerApi;
-import com.mongodb.internal.connection.OperationContext;
import com.mongodb.connection.ServerDescription;
import com.mongodb.internal.connection.Cluster;
import com.mongodb.internal.connection.Connection;
-import com.mongodb.internal.connection.NoOpSessionContext;
+import com.mongodb.internal.connection.OperationContext;
import com.mongodb.internal.connection.ServerTuple;
import com.mongodb.internal.selector.ServerAddressSelector;
-import com.mongodb.internal.session.SessionContext;
-import com.mongodb.lang.Nullable;
import static com.mongodb.assertions.Assertions.notNull;
@@ -40,25 +35,18 @@
public class SingleServerBinding extends AbstractReferenceCounted implements ReadWriteBinding {
private final Cluster cluster;
private final ServerAddress serverAddress;
- @Nullable
- private final ServerApi serverApi;
- private final RequestContext requestContext;
private final OperationContext operationContext;
/**
* Creates an instance, defaulting to {@link com.mongodb.ReadPreference#primary()} for reads.
* @param cluster a non-null Cluster which will be used to select a server to bind to
* @param serverAddress a non-null address of the server to bind to
- * @param serverApi the server API, which may be null
- * @param requestContext the request context, which may not be null
+ * @param operationContext the operation context
*/
- public SingleServerBinding(final Cluster cluster, final ServerAddress serverAddress, @Nullable final ServerApi serverApi,
- final RequestContext requestContext) {
+ public SingleServerBinding(final Cluster cluster, final ServerAddress serverAddress, final OperationContext operationContext) {
this.cluster = notNull("cluster", cluster);
this.serverAddress = notNull("serverAddress", serverAddress);
- this.serverApi = serverApi;
- this.requestContext = notNull("requestContext", requestContext);
- operationContext = new OperationContext();
+ this.operationContext = notNull("operationContext", operationContext);
}
@Override
@@ -81,22 +69,6 @@ public ConnectionSource getReadConnectionSource(final int minWireVersion, final
throw new UnsupportedOperationException();
}
- @Override
- public SessionContext getSessionContext() {
- return NoOpSessionContext.INSTANCE;
- }
-
- @Override
- @Nullable
- public ServerApi getServerApi() {
- return serverApi;
- }
-
- @Override
- public RequestContext getRequestContext() {
- return requestContext;
- }
-
@Override
public OperationContext getOperationContext() {
return operationContext;
@@ -122,26 +94,11 @@ public ServerDescription getServerDescription() {
return serverDescription;
}
- @Override
- public SessionContext getSessionContext() {
- return NoOpSessionContext.INSTANCE;
- }
-
@Override
public OperationContext getOperationContext() {
return operationContext;
}
- @Override
- public ServerApi getServerApi() {
- return serverApi;
- }
-
- @Override
- public RequestContext getRequestContext() {
- return requestContext;
- }
-
@Override
public ReadPreference getReadPreference() {
return ReadPreference.primary();
@@ -149,8 +106,10 @@ public ReadPreference getReadPreference() {
@Override
public Connection getConnection() {
- return cluster.selectServer(new ServerAddressSelector(serverAddress), operationContext)
- .getServer().getConnection(operationContext);
+ return cluster
+ .selectServer(new ServerAddressSelector(serverAddress), operationContext)
+ .getServer()
+ .getConnection(operationContext);
}
@Override
diff --git a/driver-core/src/main/com/mongodb/internal/binding/StaticBindingContext.java b/driver-core/src/main/com/mongodb/internal/binding/StaticBindingContext.java
deleted file mode 100644
index e0e7f40ade0..00000000000
--- a/driver-core/src/main/com/mongodb/internal/binding/StaticBindingContext.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2008-present MongoDB, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.mongodb.internal.binding;
-
-import com.mongodb.RequestContext;
-import com.mongodb.ServerApi;
-import com.mongodb.internal.connection.OperationContext;
-import com.mongodb.internal.session.SessionContext;
-import com.mongodb.lang.Nullable;
-
-/**
- *
- * This class is not part of the public API and may be removed or changed at any time
- */
-public class StaticBindingContext implements BindingContext {
- private final SessionContext sessionContext;
- private final ServerApi serverApi;
- private final RequestContext requestContext;
- private final OperationContext operationContext;
-
- public StaticBindingContext(final SessionContext sessionContext, @Nullable final ServerApi serverApi,
- final RequestContext requestContext, final OperationContext operationContext) {
- this.sessionContext = sessionContext;
- this.serverApi = serverApi;
- this.requestContext = requestContext;
- this.operationContext = operationContext;
- }
-
- @Override
- public SessionContext getSessionContext() {
- return sessionContext;
- }
-
- @Nullable
- @Override
- public ServerApi getServerApi() {
- return serverApi;
- }
-
- @Override
- public RequestContext getRequestContext() {
- return requestContext;
- }
-
- @Override
- public OperationContext getOperationContext() {
- return operationContext;
- }
-}
diff --git a/driver-core/src/main/com/mongodb/internal/client/model/FindOptions.java b/driver-core/src/main/com/mongodb/internal/client/model/FindOptions.java
index 3a87434e9ed..1c7f3ef9858 100644
--- a/driver-core/src/main/com/mongodb/internal/client/model/FindOptions.java
+++ b/driver-core/src/main/com/mongodb/internal/client/model/FindOptions.java
@@ -17,6 +17,9 @@
package com.mongodb.internal.client.model;
import com.mongodb.CursorType;
+import com.mongodb.annotations.Alpha;
+import com.mongodb.annotations.Reason;
+import com.mongodb.client.cursor.TimeoutMode;
import com.mongodb.client.model.Collation;
import com.mongodb.lang.Nullable;
import org.bson.BsonString;
@@ -54,6 +57,7 @@ public final class FindOptions {
private boolean returnKey;
private boolean showRecordId;
private Boolean allowDiskUse;
+ private TimeoutMode timeoutMode;
/**
* Construct a new instance.
@@ -66,7 +70,8 @@ public FindOptions() {
final int batchSize, final int limit, final Bson projection, final long maxTimeMS, final long maxAwaitTimeMS, final int skip,
final Bson sort, final CursorType cursorType, final boolean noCursorTimeout, final boolean partial,
final Collation collation, final BsonValue comment, final Bson hint, final String hintString, final Bson variables,
- final Bson max, final Bson min, final boolean returnKey, final boolean showRecordId, final Boolean allowDiskUse) {
+ final Bson max, final Bson min, final boolean returnKey, final boolean showRecordId, final Boolean allowDiskUse,
+ final TimeoutMode timeoutMode) {
this.batchSize = batchSize;
this.limit = limit;
this.projection = projection;
@@ -87,12 +92,13 @@ public FindOptions() {
this.returnKey = returnKey;
this.showRecordId = showRecordId;
this.allowDiskUse = allowDiskUse;
+ this.timeoutMode = timeoutMode;
}
//CHECKSTYLE:ON
public FindOptions withBatchSize(final int batchSize) {
return new FindOptions(batchSize, limit, projection, maxTimeMS, maxAwaitTimeMS, skip, sort, cursorType, noCursorTimeout,
- partial, collation, comment, hint, hintString, variables, max, min, returnKey, showRecordId, allowDiskUse);
+ partial, collation, comment, hint, hintString, variables, max, min, returnKey, showRecordId, allowDiskUse, timeoutMode);
}
/**
@@ -224,6 +230,41 @@ public FindOptions batchSize(final int batchSize) {
return this;
}
+ /**
+ * Sets the timeoutMode for the cursor.
+ *
+ *
+ * Requires the {@code timeout} to be set, either in the {@link com.mongodb.MongoClientSettings},
+ * via {@code MongoDatabase} or via {@code MongoCollection}
+ *
+ *
+ * If the {@code timeout} is set then:
+ *
+ * - For non-tailable cursors, the default value of timeoutMode is {@link TimeoutMode#CURSOR_LIFETIME}
+ * - For tailable cursors, the default value of timeoutMode is {@link TimeoutMode#ITERATION} and its an error
+ * to configure it as: {@link TimeoutMode#CURSOR_LIFETIME}
+ *
+ *
+ * @param timeoutMode the timeout mode
+ * @return this
+ * @since 5.2
+ */
+ @Alpha(Reason.CLIENT)
+ public FindOptions timeoutMode(final TimeoutMode timeoutMode) {
+ this.timeoutMode = timeoutMode;
+ return this;
+ }
+
+ /**
+ * @see #timeoutMode(TimeoutMode)
+ * @return timeout mode
+ */
+ @Alpha(Reason.CLIENT)
+ @Nullable
+ public TimeoutMode getTimeoutMode() {
+ return timeoutMode;
+ }
+
/**
* Gets a document describing the fields to return for all matching documents.
*
diff --git a/driver-core/src/main/com/mongodb/internal/connection/AbstractMultiServerCluster.java b/driver-core/src/main/com/mongodb/internal/connection/AbstractMultiServerCluster.java
index e1d7d6946cb..137a2f266e3 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/AbstractMultiServerCluster.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/AbstractMultiServerCluster.java
@@ -24,8 +24,10 @@
import com.mongodb.connection.ClusterType;
import com.mongodb.connection.ServerDescription;
import com.mongodb.event.ServerDescriptionChangedEvent;
+import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.diagnostics.logging.Logger;
import com.mongodb.internal.diagnostics.logging.Loggers;
+import com.mongodb.internal.time.Timeout;
import com.mongodb.lang.Nullable;
import org.bson.types.ObjectId;
@@ -125,7 +127,8 @@ public void close() {
}
@Override
- public ServersSnapshot getServersSnapshot() {
+ public ServersSnapshot getServersSnapshot(final Timeout serverSelectionTimeout,
+ final TimeoutContext timeoutContext) {
isTrue("is open", !isClosed());
Map nonAtomicSnapshot = new HashMap<>(addressToServerTupleMap);
return serverAddress -> {
diff --git a/driver-core/src/main/com/mongodb/internal/connection/AbstractProtocolExecutor.java b/driver-core/src/main/com/mongodb/internal/connection/AbstractProtocolExecutor.java
new file mode 100644
index 00000000000..ba200933860
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/internal/connection/AbstractProtocolExecutor.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.mongodb.internal.connection;
+
+import com.mongodb.internal.session.SessionContext;
+
+import static com.mongodb.internal.ExceptionUtils.isMongoSocketException;
+import static com.mongodb.internal.ExceptionUtils.isOperationTimeoutFromSocketException;
+
+/**
+ * This class is not part of the public API and may be removed or changed at any time
+ */
+public abstract class AbstractProtocolExecutor implements ProtocolExecutor {
+
+ protected boolean shouldMarkSessionDirty(final Throwable e, final SessionContext sessionContext) {
+ if (!sessionContext.hasSession()) {
+ return false;
+ }
+ return isMongoSocketException(e) || isOperationTimeoutFromSocketException(e);
+ }
+}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/AsyncConnection.java b/driver-core/src/main/com/mongodb/internal/connection/AsyncConnection.java
index 0ba1985b4b0..2891bc28732 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/AsyncConnection.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/AsyncConnection.java
@@ -20,7 +20,6 @@
import com.mongodb.annotations.ThreadSafe;
import com.mongodb.connection.ConnectionDescription;
import com.mongodb.internal.async.SingleResultCallback;
-import com.mongodb.internal.binding.BindingContext;
import com.mongodb.internal.binding.ReferenceCounted;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
@@ -46,12 +45,12 @@ public interface AsyncConnection extends ReferenceCounted {
ConnectionDescription getDescription();
void commandAsync(String database, BsonDocument command, FieldNameValidator fieldNameValidator,
- @Nullable ReadPreference readPreference, Decoder commandResultDecoder, BindingContext context,
+ @Nullable ReadPreference readPreference, Decoder commandResultDecoder, OperationContext operationContext,
SingleResultCallback callback);
void commandAsync(String database, BsonDocument command, FieldNameValidator commandFieldNameValidator,
@Nullable ReadPreference readPreference, Decoder commandResultDecoder,
- BindingContext context, boolean responseExpected, @Nullable SplittablePayload payload,
+ OperationContext operationContext, boolean responseExpected, @Nullable SplittablePayload payload,
@Nullable FieldNameValidator payloadFieldNameValidator, SingleResultCallback callback);
void markAsPinned(Connection.PinningMode pinningMode);
diff --git a/driver-core/src/main/com/mongodb/internal/connection/AsynchronousChannelStream.java b/driver-core/src/main/com/mongodb/internal/connection/AsynchronousChannelStream.java
index 6f2b7e5c172..bbb18497ee4 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/AsynchronousChannelStream.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/AsynchronousChannelStream.java
@@ -20,6 +20,7 @@
import com.mongodb.MongoInternalException;
import com.mongodb.MongoSocketReadException;
import com.mongodb.MongoSocketReadTimeoutException;
+import com.mongodb.MongoSocketWriteTimeoutException;
import com.mongodb.ServerAddress;
import com.mongodb.connection.AsyncCompletionHandler;
import com.mongodb.connection.SocketSettings;
@@ -86,14 +87,15 @@ protected void setChannel(final ExtendedAsynchronousByteChannel channel) {
}
@Override
- public void writeAsync(final List buffers, final AsyncCompletionHandler handler) {
+ public void writeAsync(final List buffers, final OperationContext operationContext,
+ final AsyncCompletionHandler handler) {
AsyncWritableByteChannelAdapter byteChannel = new AsyncWritableByteChannelAdapter();
Iterator iter = buffers.iterator();
- pipeOneBuffer(byteChannel, iter.next(), new AsyncCompletionHandler() {
+ pipeOneBuffer(byteChannel, iter.next(), operationContext, new AsyncCompletionHandler() {
@Override
public void completed(@Nullable final Void t) {
if (iter.hasNext()) {
- pipeOneBuffer(byteChannel, iter.next(), this);
+ pipeOneBuffer(byteChannel, iter.next(), operationContext, this);
} else {
handler.completed(null);
}
@@ -107,46 +109,31 @@ public void failed(final Throwable t) {
}
@Override
- public void readAsync(final int numBytes, final AsyncCompletionHandler handler) {
- readAsync(numBytes, 0, handler);
- }
-
- private void readAsync(final int numBytes, final int additionalTimeout, final AsyncCompletionHandler handler) {
+ public void readAsync(final int numBytes, final OperationContext operationContext, final AsyncCompletionHandler handler) {
ByteBuf buffer = bufferProvider.getBuffer(numBytes);
- int timeout = settings.getReadTimeout(MILLISECONDS);
- if (timeout > 0 && additionalTimeout > 0) {
- timeout += additionalTimeout;
- }
-
- getChannel().read(buffer.asNIO(), timeout, MILLISECONDS, null, new BasicCompletionHandler(buffer, handler));
+ long timeout = operationContext.getTimeoutContext().getReadTimeoutMS();
+ getChannel().read(buffer.asNIO(), timeout, MILLISECONDS, null, new BasicCompletionHandler(buffer, operationContext, handler));
}
@Override
- public void open() throws IOException {
+ public void open(final OperationContext operationContext) throws IOException {
FutureAsyncCompletionHandler handler = new FutureAsyncCompletionHandler<>();
- openAsync(handler);
+ openAsync(operationContext, handler);
handler.getOpen();
}
@Override
- public void write(final List buffers) throws IOException {
+ public void write(final List buffers, final OperationContext operationContext) throws IOException {
FutureAsyncCompletionHandler handler = new FutureAsyncCompletionHandler<>();
- writeAsync(buffers, handler);
+ writeAsync(buffers, operationContext, handler);
handler.getWrite();
}
@Override
- public ByteBuf read(final int numBytes) throws IOException {
+ public ByteBuf read(final int numBytes, final OperationContext operationContext) throws IOException {
FutureAsyncCompletionHandler handler = new FutureAsyncCompletionHandler<>();
- readAsync(numBytes, handler);
- return handler.getRead();
- }
-
- @Override
- public ByteBuf read(final int numBytes, final int additionalTimeout) throws IOException {
- FutureAsyncCompletionHandler handler = new FutureAsyncCompletionHandler<>();
- readAsync(numBytes, additionalTimeout, handler);
+ readAsync(numBytes, operationContext, handler);
return handler.getRead();
}
@@ -182,12 +169,12 @@ public ByteBuf getBuffer(final int size) {
}
private void pipeOneBuffer(final AsyncWritableByteChannelAdapter byteChannel, final ByteBuf byteBuffer,
- final AsyncCompletionHandler outerHandler) {
- byteChannel.write(byteBuffer.asNIO(), new AsyncCompletionHandler() {
+ final OperationContext operationContext, final AsyncCompletionHandler outerHandler) {
+ byteChannel.write(byteBuffer.asNIO(), operationContext, new AsyncCompletionHandler() {
@Override
public void completed(@Nullable final Void t) {
if (byteBuffer.hasRemaining()) {
- byteChannel.write(byteBuffer.asNIO(), this);
+ byteChannel.write(byteBuffer.asNIO(), operationContext, this);
} else {
outerHandler.completed(null);
}
@@ -201,8 +188,9 @@ public void failed(final Throwable t) {
}
private class AsyncWritableByteChannelAdapter {
- void write(final ByteBuffer src, final AsyncCompletionHandler handler) {
- getChannel().write(src, null, new AsyncWritableByteChannelAdapter.WriteCompletionHandler(handler));
+ void write(final ByteBuffer src, final OperationContext operationContext, final AsyncCompletionHandler handler) {
+ getChannel().write(src, operationContext.getTimeoutContext().getWriteTimeoutMS(), MILLISECONDS, null,
+ new AsyncWritableByteChannelAdapter.WriteCompletionHandler(handler));
}
private class WriteCompletionHandler extends BaseCompletionHandler {
@@ -218,19 +206,26 @@ public void completed(final Integer result, final Object attachment) {
}
@Override
- public void failed(final Throwable exc, final Object attachment) {
+ public void failed(final Throwable t, final Object attachment) {
AsyncCompletionHandler localHandler = getHandlerAndClear();
- localHandler.failed(exc);
+ if (t instanceof InterruptedByTimeoutException) {
+ localHandler.failed(new MongoSocketWriteTimeoutException("Timeout while writing message", serverAddress, t));
+ } else {
+ localHandler.failed(t);
+ }
}
}
}
private final class BasicCompletionHandler extends BaseCompletionHandler {
private final AtomicReference byteBufReference;
+ private final OperationContext operationContext;
- private BasicCompletionHandler(final ByteBuf dst, final AsyncCompletionHandler handler) {
+ private BasicCompletionHandler(final ByteBuf dst, final OperationContext operationContext,
+ final AsyncCompletionHandler handler) {
super(handler);
this.byteBufReference = new AtomicReference<>(dst);
+ this.operationContext = operationContext;
}
@Override
@@ -244,8 +239,8 @@ public void completed(final Integer result, final Void attachment) {
localByteBuf.flip();
localHandler.completed(localByteBuf);
} else {
- getChannel().read(localByteBuf.asNIO(), settings.getReadTimeout(MILLISECONDS), MILLISECONDS, null,
- new BasicCompletionHandler(localByteBuf, localHandler));
+ getChannel().read(localByteBuf.asNIO(), operationContext.getTimeoutContext().getReadTimeoutMS(), MILLISECONDS, null,
+ new BasicCompletionHandler(localByteBuf, operationContext, localHandler));
}
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/AsynchronousSocketChannelStream.java b/driver-core/src/main/com/mongodb/internal/connection/AsynchronousSocketChannelStream.java
index cb1e2a54868..4818b1f7ac4 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/AsynchronousSocketChannelStream.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/AsynchronousSocketChannelStream.java
@@ -56,7 +56,7 @@ public AsynchronousSocketChannelStream(final ServerAddress serverAddress, final
}
@Override
- public void openAsync(final AsyncCompletionHandler handler) {
+ public void openAsync(final OperationContext operationContext, final AsyncCompletionHandler handler) {
isTrue("unopened", getChannel() == null);
Queue socketAddressQueue;
diff --git a/driver-core/src/main/com/mongodb/internal/connection/Authenticator.java b/driver-core/src/main/com/mongodb/internal/connection/Authenticator.java
index 232eeb45049..cd1809966b0 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/Authenticator.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/Authenticator.java
@@ -96,19 +96,20 @@ T getNonNullMechanismProperty(final String key, @Nullable final T defaultVal
}
- abstract void authenticate(InternalConnection connection, ConnectionDescription connectionDescription);
+ abstract void authenticate(InternalConnection connection, ConnectionDescription connectionDescription,
+ OperationContext operationContext);
abstract void authenticateAsync(InternalConnection connection, ConnectionDescription connectionDescription,
- SingleResultCallback callback);
+ OperationContext operationContext, SingleResultCallback callback);
- public void reauthenticate(final InternalConnection connection) {
- authenticate(connection, connection.getDescription());
+ public void reauthenticate(final InternalConnection connection, final OperationContext operationContext) {
+ authenticate(connection, connection.getDescription(), operationContext);
}
- public void reauthenticateAsync(final InternalConnection connection, final SingleResultCallback callback) {
+ public void reauthenticateAsync(final InternalConnection connection, final OperationContext operationContext,
+ final SingleResultCallback callback) {
beginAsync().thenRun((c) -> {
- authenticateAsync(connection, connection.getDescription(), c);
+ authenticateAsync(connection, connection.getDescription(), operationContext, c);
}).finish(callback);
}
-
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/BaseCluster.java b/driver-core/src/main/com/mongodb/internal/connection/BaseCluster.java
index 292822244b7..df3e4d1c1fe 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/BaseCluster.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/BaseCluster.java
@@ -19,6 +19,8 @@
import com.mongodb.MongoClientException;
import com.mongodb.MongoException;
import com.mongodb.MongoIncompatibleDriverException;
+import com.mongodb.MongoInterruptedException;
+import com.mongodb.MongoOperationTimeoutException;
import com.mongodb.MongoTimeoutException;
import com.mongodb.ServerAddress;
import com.mongodb.UnixServerAddress;
@@ -31,6 +33,7 @@
import com.mongodb.event.ClusterDescriptionChangedEvent;
import com.mongodb.event.ClusterListener;
import com.mongodb.event.ClusterOpeningEvent;
+import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.VisibleForTesting;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.connection.OperationContext.ServerDeprioritization;
@@ -42,6 +45,7 @@
import com.mongodb.internal.selector.AtMostTwoRandomServerSelector;
import com.mongodb.internal.selector.LatencyMinimizingServerSelector;
import com.mongodb.internal.selector.MinimumOperationCountServerSelector;
+import com.mongodb.internal.time.Timeout;
import com.mongodb.lang.Nullable;
import com.mongodb.selector.CompositeServerSelector;
import com.mongodb.selector.ServerSelector;
@@ -78,7 +82,7 @@
import static com.mongodb.internal.logging.LogMessage.Entry.Name.TOPOLOGY_DESCRIPTION;
import static com.mongodb.internal.logging.LogMessage.Level.DEBUG;
import static com.mongodb.internal.logging.LogMessage.Level.INFO;
-import static com.mongodb.internal.thread.InterruptionUtil.interruptAndCreateMongoInterruptedException;
+import static com.mongodb.internal.time.Timeout.ZeroSemantics.ZERO_DURATION_MEANS_EXPIRED;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -121,58 +125,46 @@ public ClusterClock getClock() {
public ServerTuple selectServer(final ServerSelector serverSelector, final OperationContext operationContext) {
isTrue("open", !isClosed());
- try {
- CountDownLatch currentPhase = phase.get();
- ClusterDescription curDescription = description;
- logServerSelectionStarted(clusterId, operationContext, serverSelector, curDescription);
- ServerDeprioritization serverDeprioritization = operationContext.getServerDeprioritization();
- ServerTuple serverTuple = createCompleteSelectorAndSelectServer(serverSelector, curDescription, serverDeprioritization);
-
- boolean selectionWaitingLogged = false;
-
- long startTimeNanos = System.nanoTime();
- long curTimeNanos = startTimeNanos;
- Long maxWaitTimeNanos = getMaxWaitTimeNanos();
-
- while (true) {
- if (!curDescription.isCompatibleWithDriver()) {
- throw createAndLogIncompatibleException(operationContext, serverSelector, curDescription);
- }
-
- if (serverTuple != null) {
- ServerAddress serverAddress = serverTuple.getServerDescription().getAddress();
- logServerSelectionSucceeded(
- clusterId, operationContext, serverAddress, serverSelector, curDescription);
- serverDeprioritization.updateCandidate(serverAddress);
- return serverTuple;
- }
-
- Long remainingTimeNanos = maxWaitTimeNanos == null ? null : maxWaitTimeNanos - (curTimeNanos - startTimeNanos);
-
- if (remainingTimeNanos != null && remainingTimeNanos <= 0) {
- throw createAndLogTimeoutException(operationContext, serverSelector, curDescription);
- }
-
- if (!selectionWaitingLogged) {
- logServerSelectionWaiting(clusterId, operationContext, remainingTimeNanos, serverSelector, curDescription);
- selectionWaitingLogged = true;
- }
-
- connect();
-
- currentPhase.await(
- remainingTimeNanos == null ? getMinWaitTimeNanos() : Math.min(remainingTimeNanos, getMinWaitTimeNanos()),
- NANOSECONDS);
-
- curTimeNanos = System.nanoTime();
+ ServerDeprioritization serverDeprioritization = operationContext.getServerDeprioritization();
+ boolean selectionWaitingLogged = false;
+ Timeout computedServerSelectionTimeout = operationContext.getTimeoutContext().computeServerSelectionTimeout();
+ logServerSelectionStarted(clusterId, operationContext.getId(), serverSelector, description);
+ while (true) {
+ CountDownLatch currentPhaseLatch = phase.get();
+ ClusterDescription currentDescription = description;
+ ServerTuple serverTuple = createCompleteSelectorAndSelectServer(
+ serverSelector, currentDescription, serverDeprioritization,
+ computedServerSelectionTimeout, operationContext.getTimeoutContext());
+
+ if (!currentDescription.isCompatibleWithDriver()) {
+ logAndThrowIncompatibleException(operationContext.getId(), serverSelector, currentDescription);
+ }
+ if (serverTuple != null) {
+ ServerAddress serverAddress = serverTuple.getServerDescription().getAddress();
+ logServerSelectionSucceeded(
+ clusterId,
+ operationContext.getId(),
+ serverAddress,
+ serverSelector,
+ currentDescription);
+ serverDeprioritization.updateCandidate(serverAddress);
+ return serverTuple;
+ }
+ computedServerSelectionTimeout.onExpired(() ->
+ logAndThrowTimeoutException(operationContext, serverSelector, currentDescription));
- currentPhase = phase.get();
- curDescription = description;
- serverTuple = createCompleteSelectorAndSelectServer(serverSelector, curDescription, serverDeprioritization);
+ if (!selectionWaitingLogged) {
+ logServerSelectionWaiting(clusterId, operationContext.getId(), computedServerSelectionTimeout, serverSelector, currentDescription);
+ selectionWaitingLogged = true;
}
+ connect();
+
+ Timeout heartbeatLimitedTimeout = Timeout.earliest(
+ computedServerSelectionTimeout,
+ startMinWaitHeartbeatTimeout());
- } catch (InterruptedException e) {
- throw interruptAndCreateMongoInterruptedException(format("Interrupted while waiting for a server that matches %s", serverSelector), e);
+ heartbeatLimitedTimeout.awaitOn(currentPhaseLatch,
+ () -> format("waiting for a server that matches %s", serverSelector));
}
}
@@ -181,11 +173,18 @@ public void selectServerAsync(final ServerSelector serverSelector, final Operati
final SingleResultCallback callback) {
isTrue("open", !isClosed());
+ Timeout computedServerSelectionTimeout = operationContext.getTimeoutContext().computeServerSelectionTimeout();
+ ServerSelectionRequest request = new ServerSelectionRequest(
+ serverSelector, operationContext, computedServerSelectionTimeout, callback);
+
CountDownLatch currentPhase = phase.get();
ClusterDescription currentDescription = description;
- logServerSelectionStarted(clusterId, operationContext, serverSelector, currentDescription);
- ServerSelectionRequest request = new ServerSelectionRequest(operationContext, serverSelector, getMaxWaitTimeNanos(), callback);
+ logServerSelectionStarted(
+ clusterId,
+ operationContext.getId(),
+ serverSelector,
+ currentDescription);
if (!handleServerSelectionRequest(request, currentPhase, currentDescription)) {
notifyWaitQueueHandler(request);
@@ -257,50 +256,60 @@ private void updatePhase() {
withLock(() -> phase.getAndSet(new CountDownLatch(1)).countDown());
}
- @Nullable
- private Long getMaxWaitTimeNanos() {
- if (settings.getServerSelectionTimeout(NANOSECONDS) < 0) {
- return null;
- }
- return settings.getServerSelectionTimeout(NANOSECONDS);
+ private Timeout startMinWaitHeartbeatTimeout() {
+ long minHeartbeatFrequency = serverFactory.getSettings().getMinHeartbeatFrequency(NANOSECONDS);
+ minHeartbeatFrequency = Math.max(0, minHeartbeatFrequency);
+ return Timeout.expiresIn(minHeartbeatFrequency, NANOSECONDS, ZERO_DURATION_MEANS_EXPIRED);
}
- private long getMinWaitTimeNanos() {
- return serverFactory.getSettings().getMinHeartbeatFrequency(NANOSECONDS);
- }
+ private boolean handleServerSelectionRequest(
+ final ServerSelectionRequest request, final CountDownLatch currentPhase,
+ final ClusterDescription description) {
- private boolean handleServerSelectionRequest(final ServerSelectionRequest request, final CountDownLatch currentPhase,
- final ClusterDescription description) {
try {
+ OperationContext operationContext = request.getOperationContext();
+ long operationId = operationContext.getId();
if (currentPhase != request.phase) {
CountDownLatch prevPhase = request.phase;
request.phase = currentPhase;
if (!description.isCompatibleWithDriver()) {
- request.onResult(null, createAndLogIncompatibleException(request.operationContext, request.originalSelector, description));
- return true;
+ logAndThrowIncompatibleException(operationId, request.originalSelector, description);
}
+
ServerDeprioritization serverDeprioritization = request.operationContext.getServerDeprioritization();
- ServerTuple serverTuple = createCompleteSelectorAndSelectServer(request.originalSelector, description, serverDeprioritization);
+ ServerTuple serverTuple = createCompleteSelectorAndSelectServer(
+ request.originalSelector,
+ description,
+ serverDeprioritization,
+ request.getTimeout(),
+ operationContext.getTimeoutContext());
+
if (serverTuple != null) {
ServerAddress serverAddress = serverTuple.getServerDescription().getAddress();
- logServerSelectionSucceeded(clusterId, request.operationContext, serverAddress,
- request.originalSelector, description);
+ logServerSelectionSucceeded(
+ clusterId,
+ operationId,
+ serverAddress,
+ request.originalSelector,
+ description);
serverDeprioritization.updateCandidate(serverAddress);
request.onResult(serverTuple, null);
return true;
}
if (prevPhase == null) {
logServerSelectionWaiting(
- clusterId, request.operationContext, request.getRemainingTime(), request.originalSelector, description);
+ clusterId,
+ operationId,
+ request.getTimeout(),
+ request.originalSelector,
+ description);
}
}
- if (request.timedOut()) {
- request.onResult(null, createAndLogTimeoutException(request.operationContext, request.originalSelector, description));
- return true;
- }
-
+ Timeout.onExistsAndExpired(request.getTimeout(), () -> {
+ logAndThrowTimeoutException(operationContext, request.originalSelector, description);
+ });
return false;
} catch (Exception e) {
request.onResult(null, e);
@@ -312,9 +321,15 @@ private boolean handleServerSelectionRequest(final ServerSelectionRequest reques
private ServerTuple createCompleteSelectorAndSelectServer(
final ServerSelector serverSelector,
final ClusterDescription clusterDescription,
- final ServerDeprioritization serverDeprioritization) {
+ final ServerDeprioritization serverDeprioritization,
+ final Timeout serverSelectionTimeout,
+ final TimeoutContext timeoutContext) {
return createCompleteSelectorAndSelectServer(
- serverSelector, clusterDescription, getServersSnapshot(), serverDeprioritization, settings);
+ serverSelector,
+ clusterDescription,
+ getServersSnapshot(serverSelectionTimeout, timeoutContext),
+ serverDeprioritization,
+ settings);
}
@Nullable
@@ -372,13 +387,13 @@ protected ClusterableServer createServer(final ServerAddress serverAddress) {
return serverFactory.create(this, serverAddress);
}
- private MongoIncompatibleDriverException createAndLogIncompatibleException(
- final OperationContext operationContext,
+ private void logAndThrowIncompatibleException(
+ final long operationId,
final ServerSelector serverSelector,
final ClusterDescription clusterDescription) {
MongoIncompatibleDriverException exception = createIncompatibleException(clusterDescription);
- logServerSelectionFailed(clusterId, operationContext, exception, serverSelector, clusterDescription);
- return exception;
+ logServerSelectionFailed(clusterId, operationId, exception, serverSelector, clusterDescription);
+ throw exception;
}
private MongoIncompatibleDriverException createIncompatibleException(final ClusterDescription curDescription) {
@@ -400,34 +415,36 @@ private MongoIncompatibleDriverException createIncompatibleException(final Clust
return new MongoIncompatibleDriverException(message, curDescription);
}
- private MongoException createAndLogTimeoutException(
+ private void logAndThrowTimeoutException(
final OperationContext operationContext,
final ServerSelector serverSelector,
final ClusterDescription clusterDescription) {
- MongoTimeoutException exception = new MongoTimeoutException(format(
+ String message = format(
"Timed out while waiting for a server that matches %s. Client view of cluster state is %s",
- serverSelector, clusterDescription.getShortDescription()));
- logServerSelectionFailed(clusterId, operationContext, exception, serverSelector, clusterDescription);
- return exception;
+ serverSelector, clusterDescription.getShortDescription());
+
+ MongoTimeoutException exception = operationContext.getTimeoutContext().hasTimeoutMS()
+ ? new MongoOperationTimeoutException(message) : new MongoTimeoutException(message);
+
+ logServerSelectionFailed(clusterId, operationContext.getId(), exception, serverSelector, clusterDescription);
+ throw exception;
}
private static final class ServerSelectionRequest {
- private final OperationContext operationContext;
private final ServerSelector originalSelector;
- @Nullable
- private final Long maxWaitTimeNanos;
private final SingleResultCallback callback;
- private final long startTimeNanos = System.nanoTime();
+ private final OperationContext operationContext;
+ private final Timeout timeout;
private CountDownLatch phase;
- ServerSelectionRequest(final OperationContext operationContext,
- final ServerSelector serverSelector,
- @Nullable
- final Long maxWaitTimeNanos,
- final SingleResultCallback callback) {
- this.operationContext = operationContext;
+ ServerSelectionRequest(
+ final ServerSelector serverSelector,
+ final OperationContext operationContext,
+ final Timeout timeout,
+ final SingleResultCallback callback) {
this.originalSelector = serverSelector;
- this.maxWaitTimeNanos = maxWaitTimeNanos;
+ this.operationContext = operationContext;
+ this.timeout = timeout;
this.callback = callback;
}
@@ -439,14 +456,12 @@ void onResult(@Nullable final ServerTuple serverTuple, @Nullable final Throwable
}
}
- boolean timedOut() {
- Long remainingTimeNanos = getRemainingTime();
- return remainingTimeNanos != null && remainingTimeNanos <= 0;
+ Timeout getTimeout() {
+ return timeout;
}
- @Nullable
- Long getRemainingTime() {
- return maxWaitTimeNanos == null ? null : maxWaitTimeNanos - (System.nanoTime() - startTimeNanos);
+ public OperationContext getOperationContext() {
+ return operationContext;
}
}
@@ -477,31 +492,37 @@ private void stopWaitQueueHandler() {
}
private final class WaitQueueHandler implements Runnable {
+
+ WaitQueueHandler() {
+ }
+
public void run() {
while (!isClosed) {
CountDownLatch currentPhase = phase.get();
ClusterDescription curDescription = description;
- long waitTimeNanos = Long.MAX_VALUE;
+ Timeout timeout = Timeout.infinite();
+ boolean someWaitersNotSatisfied = false;
for (Iterator iter = waitQueue.iterator(); iter.hasNext();) {
- ServerSelectionRequest nextRequest = iter.next();
- if (handleServerSelectionRequest(nextRequest, currentPhase, curDescription)) {
+ ServerSelectionRequest currentRequest = iter.next();
+ if (handleServerSelectionRequest(currentRequest, currentPhase, curDescription)) {
iter.remove();
} else {
- Long remainingTimeNanos = nextRequest.getRemainingTime();
- long minWaitTimeNanos = Math.min(getMinWaitTimeNanos(), waitTimeNanos);
- waitTimeNanos = remainingTimeNanos == null ? minWaitTimeNanos : Math.min(remainingTimeNanos, minWaitTimeNanos);
+ someWaitersNotSatisfied = true;
+ timeout = Timeout.earliest(
+ timeout,
+ currentRequest.getTimeout(),
+ startMinWaitHeartbeatTimeout());
}
}
- // if there are any waiters that were not satisfied, connect
- if (waitTimeNanos < Long.MAX_VALUE) {
+ if (someWaitersNotSatisfied) {
connect();
}
try {
- currentPhase.await(waitTimeNanos, NANOSECONDS);
- } catch (InterruptedException closed) {
+ timeout.awaitOn(currentPhase, () -> "ignored");
+ } catch (MongoInterruptedException closed) {
// The cluster has been closed and the while loop will exit.
}
}
@@ -515,7 +536,7 @@ public void run() {
static void logServerSelectionStarted(
final ClusterId clusterId,
- final OperationContext operationContext,
+ final long operationId,
final ServerSelector serverSelector,
final ClusterDescription clusterDescription) {
if (STRUCTURED_LOGGER.isRequired(DEBUG, clusterId)) {
@@ -523,7 +544,7 @@ static void logServerSelectionStarted(
SERVER_SELECTION, DEBUG, "Server selection started", clusterId,
asList(
new Entry(OPERATION, null),
- new Entry(OPERATION_ID, operationContext.getId()),
+ new Entry(OPERATION_ID, operationId),
new Entry(SELECTOR, serverSelector.toString()),
new Entry(TOPOLOGY_DESCRIPTION, clusterDescription.getShortDescription())),
"Server selection started for operation[ {}] with ID {}. Selector: {}, topology description: {}"));
@@ -532,9 +553,8 @@ static void logServerSelectionStarted(
private static void logServerSelectionWaiting(
final ClusterId clusterId,
- final OperationContext operationContext,
- @Nullable
- final Long remainingTimeNanos,
+ final long operationId,
+ final Timeout timeout,
final ServerSelector serverSelector,
final ClusterDescription clusterDescription) {
if (STRUCTURED_LOGGER.isRequired(INFO, clusterId)) {
@@ -542,8 +562,11 @@ private static void logServerSelectionWaiting(
SERVER_SELECTION, INFO, "Waiting for suitable server to become available", clusterId,
asList(
new Entry(OPERATION, null),
- new Entry(OPERATION_ID, operationContext.getId()),
- new Entry(REMAINING_TIME_MS, remainingTimeNanos == null ? null : NANOSECONDS.toMillis(remainingTimeNanos)),
+ new Entry(OPERATION_ID, operationId),
+ timeout.call(MILLISECONDS,
+ () -> new Entry(REMAINING_TIME_MS, "infinite"),
+ (ms) -> new Entry(REMAINING_TIME_MS, ms),
+ () -> new Entry(REMAINING_TIME_MS, 0L)),
new Entry(SELECTOR, serverSelector.toString()),
new Entry(TOPOLOGY_DESCRIPTION, clusterDescription.getShortDescription())),
"Waiting for server to become available for operation[ {}] with ID {}.[ Remaining time: {} ms.]"
@@ -553,7 +576,7 @@ private static void logServerSelectionWaiting(
private static void logServerSelectionFailed(
final ClusterId clusterId,
- final OperationContext operationContext,
+ final long operationId,
final MongoException failure,
final ServerSelector serverSelector,
final ClusterDescription clusterDescription) {
@@ -568,7 +591,7 @@ private static void logServerSelectionFailed(
SERVER_SELECTION, DEBUG, "Server selection failed", clusterId,
asList(
new Entry(OPERATION, null),
- new Entry(OPERATION_ID, operationContext.getId()),
+ new Entry(OPERATION_ID, operationId),
new Entry(FAILURE, failureDescription),
new Entry(SELECTOR, serverSelector.toString()),
new Entry(TOPOLOGY_DESCRIPTION, clusterDescription.getShortDescription())),
@@ -578,7 +601,7 @@ private static void logServerSelectionFailed(
static void logServerSelectionSucceeded(
final ClusterId clusterId,
- final OperationContext operationContext,
+ final long operationId,
final ServerAddress serverAddress,
final ServerSelector serverSelector,
final ClusterDescription clusterDescription) {
@@ -587,7 +610,7 @@ static void logServerSelectionSucceeded(
SERVER_SELECTION, DEBUG, "Server selection succeeded", clusterId,
asList(
new Entry(OPERATION, null),
- new Entry(OPERATION_ID, operationContext.getId()),
+ new Entry(OPERATION_ID, operationId),
new Entry(SERVER_HOST, serverAddress.getHost()),
new Entry(SERVER_PORT, serverAddress instanceof UnixServerAddress ? null : serverAddress.getPort()),
new Entry(SELECTOR, serverSelector.toString()),
diff --git a/driver-core/src/main/com/mongodb/internal/connection/Cluster.java b/driver-core/src/main/com/mongodb/internal/connection/Cluster.java
index 358eb90a175..a6d4a026608 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/Cluster.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/Cluster.java
@@ -19,11 +19,13 @@
import com.mongodb.ServerAddress;
import com.mongodb.annotations.ThreadSafe;
+import com.mongodb.connection.ClusterDescription;
import com.mongodb.connection.ClusterId;
+import com.mongodb.connection.ClusterSettings;
import com.mongodb.event.ServerDescriptionChangedEvent;
+import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.async.SingleResultCallback;
-import com.mongodb.connection.ClusterDescription;
-import com.mongodb.connection.ClusterSettings;
+import com.mongodb.internal.time.Timeout;
import com.mongodb.lang.Nullable;
import com.mongodb.selector.ServerSelector;
@@ -41,7 +43,7 @@ public interface Cluster extends Closeable {
ClusterId getClusterId();
- ServersSnapshot getServersSnapshot();
+ ServersSnapshot getServersSnapshot(Timeout serverSelectionTimeout, TimeoutContext timeoutContext);
/**
* Get the current description of this cluster.
diff --git a/driver-core/src/main/com/mongodb/internal/connection/CommandHelper.java b/driver-core/src/main/com/mongodb/internal/connection/CommandHelper.java
index dc0df6ac27e..31737d7b22b 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/CommandHelper.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/CommandHelper.java
@@ -20,7 +20,6 @@
import com.mongodb.MongoServerException;
import com.mongodb.ServerApi;
import com.mongodb.connection.ClusterConnectionMode;
-import com.mongodb.internal.IgnorableRequestContext;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.validator.NoOpFieldNameValidator;
import com.mongodb.lang.Nullable;
@@ -44,27 +43,30 @@ public final class CommandHelper {
static final String LEGACY_HELLO_LOWER = LEGACY_HELLO.toLowerCase(Locale.ROOT);
static BsonDocument executeCommand(final String database, final BsonDocument command, final ClusterConnectionMode clusterConnectionMode,
- @Nullable final ServerApi serverApi, final InternalConnection internalConnection) {
- return sendAndReceive(database, command, clusterConnectionMode, serverApi, internalConnection);
+ @Nullable final ServerApi serverApi, final InternalConnection internalConnection, final OperationContext operationContext) {
+ return sendAndReceive(database, command, clusterConnectionMode, serverApi, internalConnection, operationContext);
}
static BsonDocument executeCommandWithoutCheckingForFailure(final String database, final BsonDocument command,
- final ClusterConnectionMode clusterConnectionMode, @Nullable final ServerApi serverApi,
- final InternalConnection internalConnection) {
+ final ClusterConnectionMode clusterConnectionMode, @Nullable final ServerApi serverApi,
+ final InternalConnection internalConnection, final OperationContext operationContext) {
try {
- return sendAndReceive(database, command, clusterConnectionMode, serverApi, internalConnection);
+ return executeCommand(database, command, clusterConnectionMode, serverApi, internalConnection, operationContext);
} catch (MongoServerException e) {
return new BsonDocument();
}
}
- static void executeCommandAsync(final String database, final BsonDocument command, final ClusterConnectionMode clusterConnectionMode,
- @Nullable final ServerApi serverApi, final InternalConnection internalConnection,
+ static void executeCommandAsync(final String database,
+ final BsonDocument command,
+ final ClusterConnectionMode clusterConnectionMode,
+ @Nullable final ServerApi serverApi,
+ final InternalConnection internalConnection,
+ final OperationContext operationContext,
final SingleResultCallback callback) {
internalConnection.sendAndReceiveAsync(
getCommandMessage(database, command, internalConnection, clusterConnectionMode, serverApi),
- new BsonDocumentCodec(),
- NoOpSessionContext.INSTANCE, IgnorableRequestContext.INSTANCE, new OperationContext(), (result, t) -> {
+ new BsonDocumentCodec(), operationContext, (result, t) -> {
if (t != null) {
callback.onResult(null, t);
} else {
@@ -88,11 +90,15 @@ static boolean isCommandOk(final BsonDocument response) {
}
private static BsonDocument sendAndReceive(final String database, final BsonDocument command,
- final ClusterConnectionMode clusterConnectionMode, @Nullable final ServerApi serverApi,
- final InternalConnection internalConnection) {
- return assertNotNull(internalConnection.sendAndReceive(getCommandMessage(database, command, internalConnection,
- clusterConnectionMode, serverApi), new BsonDocumentCodec(), NoOpSessionContext.INSTANCE,
- IgnorableRequestContext.INSTANCE, new OperationContext()));
+ final ClusterConnectionMode clusterConnectionMode,
+ @Nullable final ServerApi serverApi,
+ final InternalConnection internalConnection,
+ final OperationContext operationContext) {
+ return assertNotNull(
+ internalConnection.sendAndReceive(
+ getCommandMessage(database, command, internalConnection, clusterConnectionMode, serverApi),
+ new BsonDocumentCodec(), operationContext)
+ );
}
private static CommandMessage getCommandMessage(final String database, final BsonDocument command,
@@ -106,6 +112,7 @@ private static CommandMessage getCommandMessage(final String database, final Bso
// which means OP_MSG will not be used
.maxWireVersion(internalConnection.getDescription().getMaxWireVersion())
.serverType(internalConnection.getDescription().getServerType())
+ .cryptd(internalConnection.getInitialServerDescription().isCryptd())
.build(),
clusterConnectionMode, serverApi);
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/CommandMessage.java b/driver-core/src/main/com/mongodb/internal/connection/CommandMessage.java
index 24b30d60acb..53d869a6b8f 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/CommandMessage.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/CommandMessage.java
@@ -21,6 +21,7 @@
import com.mongodb.ReadPreference;
import com.mongodb.ServerApi;
import com.mongodb.connection.ClusterConnectionMode;
+import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.session.SessionContext;
import com.mongodb.lang.Nullable;
import org.bson.BsonArray;
@@ -142,7 +143,7 @@ MongoNamespace getNamespace() {
}
@Override
- protected EncodingMetadata encodeMessageBodyWithMetadata(final BsonOutput bsonOutput, final SessionContext sessionContext) {
+ protected EncodingMetadata encodeMessageBodyWithMetadata(final BsonOutput bsonOutput, final OperationContext operationContext) {
int messageStartPosition = bsonOutput.getPosition() - MESSAGE_PROLOGUE_LENGTH;
int commandStartPosition;
if (useOpMsg()) {
@@ -151,7 +152,7 @@ protected EncodingMetadata encodeMessageBodyWithMetadata(final BsonOutput bsonOu
bsonOutput.writeByte(0); // payload type
commandStartPosition = bsonOutput.getPosition();
- addDocument(command, bsonOutput, commandFieldNameValidator, getExtraElements(sessionContext));
+ addDocument(command, bsonOutput, commandFieldNameValidator, getExtraElements(operationContext));
if (payload != null) {
bsonOutput.writeByte(1); // payload type
@@ -214,8 +215,16 @@ private boolean useOpMsg() {
return getOpCode().equals(OpCode.OP_MSG);
}
- private List getExtraElements(final SessionContext sessionContext) {
+ private List getExtraElements(final OperationContext operationContext) {
+ SessionContext sessionContext = operationContext.getSessionContext();
+ TimeoutContext timeoutContext = operationContext.getTimeoutContext();
+
List extraElements = new ArrayList<>();
+ if (!getSettings().isCryptd()) {
+ timeoutContext.runMaxTimeMS(maxTimeMS ->
+ extraElements.add(new BsonElement("maxTimeMS", new BsonInt64(maxTimeMS)))
+ );
+ }
extraElements.add(new BsonElement("$db", new BsonString(new MongoNamespace(getCollectionName()).getDatabaseName())));
if (sessionContext.getClusterTime() != null) {
extraElements.add(new BsonElement("$clusterTime", sessionContext.getClusterTime()));
diff --git a/driver-core/src/main/com/mongodb/internal/connection/CommandProtocol.java b/driver-core/src/main/com/mongodb/internal/connection/CommandProtocol.java
index 7fab16b30a3..2cc78497980 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/CommandProtocol.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/CommandProtocol.java
@@ -30,5 +30,5 @@ public interface CommandProtocol {
void executeAsync(InternalConnection connection, SingleResultCallback callback);
- CommandProtocol sessionContext(SessionContext sessionContext);
+ CommandProtocol withSessionContext(SessionContext sessionContext);
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/CommandProtocolImpl.java b/driver-core/src/main/com/mongodb/internal/connection/CommandProtocolImpl.java
index 251b4f21d2d..de9e0666d40 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/CommandProtocolImpl.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/CommandProtocolImpl.java
@@ -18,8 +18,6 @@
import com.mongodb.MongoNamespace;
import com.mongodb.ReadPreference;
-import com.mongodb.RequestContext;
-import com.mongodb.ServerApi;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.session.SessionContext;
@@ -42,16 +40,12 @@ class CommandProtocolImpl implements CommandProtocol {
private final Decoder commandResultDecoder;
private final boolean responseExpected;
private final ClusterConnectionMode clusterConnectionMode;
- private final RequestContext requestContext;
- private SessionContext sessionContext;
- private final ServerApi serverApi;
private final OperationContext operationContext;
CommandProtocolImpl(final String database, final BsonDocument command, final FieldNameValidator commandFieldNameValidator,
@Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final boolean responseExpected,
@Nullable final SplittablePayload payload, @Nullable final FieldNameValidator payloadFieldNameValidator,
- final ClusterConnectionMode clusterConnectionMode, @Nullable final ServerApi serverApi, final RequestContext requestContext,
- final OperationContext operationContext) {
+ final ClusterConnectionMode clusterConnectionMode, final OperationContext operationContext) {
notNull("database", database);
this.namespace = new MongoNamespace(notNull("database", database), MongoNamespace.COMMAND_COLLECTION_NAME);
this.command = notNull("command", command);
@@ -62,8 +56,6 @@ class CommandProtocolImpl implements CommandProtocol {
this.payload = payload;
this.payloadFieldNameValidator = payloadFieldNameValidator;
this.clusterConnectionMode = notNull("clusterConnectionMode", clusterConnectionMode);
- this.serverApi = serverApi;
- this.requestContext = notNull("requestContext", requestContext);
this.operationContext = operationContext;
isTrueArgument("payloadFieldNameValidator cannot be null if there is a payload.",
@@ -73,15 +65,14 @@ class CommandProtocolImpl implements CommandProtocol {
@Nullable
@Override
public T execute(final InternalConnection connection) {
- return connection.sendAndReceive(getCommandMessage(connection), commandResultDecoder, sessionContext, requestContext,
- operationContext);
+ return connection.sendAndReceive(getCommandMessage(connection), commandResultDecoder, operationContext);
}
@Override
public void executeAsync(final InternalConnection connection, final SingleResultCallback callback) {
try {
- connection.sendAndReceiveAsync(getCommandMessage(connection), commandResultDecoder, sessionContext, requestContext,
- operationContext, (result, t) -> {
+ connection.sendAndReceiveAsync(getCommandMessage(connection), commandResultDecoder, operationContext,
+ (result, t) -> {
if (t != null) {
callback.onResult(null, t);
} else {
@@ -94,14 +85,15 @@ public void executeAsync(final InternalConnection connection, final SingleResult
}
@Override
- public CommandProtocolImpl sessionContext(final SessionContext sessionContext) {
- this.sessionContext = sessionContext;
- return this;
+ public CommandProtocolImpl withSessionContext(final SessionContext sessionContext) {
+ return new CommandProtocolImpl<>(namespace.getDatabaseName(), command, commandFieldNameValidator, readPreference,
+ commandResultDecoder, responseExpected, payload, payloadFieldNameValidator, clusterConnectionMode,
+ operationContext.withSessionContext(sessionContext));
}
private CommandMessage getCommandMessage(final InternalConnection connection) {
return new CommandMessage(namespace, command, commandFieldNameValidator, readPreference,
- getMessageSettings(connection.getDescription()), responseExpected, payload,
- payloadFieldNameValidator, clusterConnectionMode, serverApi);
+ getMessageSettings(connection.getDescription(), connection.getInitialServerDescription()), responseExpected, payload,
+ payloadFieldNameValidator, clusterConnectionMode, operationContext.getServerApi());
}
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/CompressedMessage.java b/driver-core/src/main/com/mongodb/internal/connection/CompressedMessage.java
index 698fe2ece9f..9880ef3fb0b 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/CompressedMessage.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/CompressedMessage.java
@@ -16,7 +16,6 @@
package com.mongodb.internal.connection;
-import com.mongodb.internal.session.SessionContext;
import org.bson.ByteBuf;
import org.bson.io.BsonOutput;
@@ -38,7 +37,7 @@ class CompressedMessage extends RequestMessage {
}
@Override
- protected EncodingMetadata encodeMessageBodyWithMetadata(final BsonOutput bsonOutput, final SessionContext sessionContext) {
+ protected EncodingMetadata encodeMessageBodyWithMetadata(final BsonOutput bsonOutput, final OperationContext operationContext) {
bsonOutput.writeInt32(wrappedOpcode.getValue());
bsonOutput.writeInt32(getWrappedMessageSize(wrappedMessageBuffers) - MESSAGE_HEADER_LENGTH);
bsonOutput.writeByte(compressor.getId());
diff --git a/driver-core/src/main/com/mongodb/internal/connection/ConcurrentPool.java b/driver-core/src/main/com/mongodb/internal/connection/ConcurrentPool.java
index c174e828bde..fe3ac129631 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/ConcurrentPool.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/ConcurrentPool.java
@@ -23,8 +23,7 @@
import com.mongodb.MongoTimeoutException;
import com.mongodb.annotations.ThreadSafe;
import com.mongodb.internal.VisibleForTesting;
-import com.mongodb.internal.time.TimePoint;
-import com.mongodb.internal.time.Timeout;
+import com.mongodb.internal.time.StartTime;
import com.mongodb.lang.Nullable;
import java.util.Deque;
@@ -147,7 +146,7 @@ public T get() {
* Gets an object from the pool. Blocks until an object is available, or the specified {@code timeout} expires,
* or the pool is {@linkplain #close() closed}/{@linkplain #pause(Supplier) paused}.
*
- * @param timeout See {@link Timeout#started(long, TimeUnit, TimePoint)}.
+ * @param timeout See {@link StartTime#timeoutAfterOrInfiniteIfNegative(long, TimeUnit)}.
* @param timeUnit the time unit of the timeout
* @return An object from the pool, or null if can't get one in the given waitTime
* @throws MongoTimeoutException if the timeout has been exceeded
@@ -231,7 +230,7 @@ private T createNewAndReleasePermitIfFailure() {
}
/**
- * @param timeout See {@link Timeout#started(long, TimeUnit, TimePoint)}.
+ * @param timeout See {@link StartTime#timeoutAfterOrInfiniteIfNegative(long, TimeUnit)}.
*/
@VisibleForTesting(otherwise = PRIVATE)
boolean acquirePermit(final long timeout, final TimeUnit timeUnit) {
@@ -388,7 +387,7 @@ boolean acquirePermitImmediateUnfair() {
* This method also emulates the eager {@link InterruptedException} behavior of
* {@link java.util.concurrent.Semaphore#tryAcquire(long, TimeUnit)}.
*
- * @param timeout See {@link Timeout#started(long, TimeUnit, TimePoint)}.
+ * @param timeout See {@link StartTime#timeoutAfterOrInfiniteIfNegative(long, TimeUnit)}.
*/
boolean acquirePermit(final long timeout, final TimeUnit unit) throws MongoInterruptedException {
long remainingNanos = unit.toNanos(timeout);
diff --git a/driver-core/src/main/com/mongodb/internal/connection/Connection.java b/driver-core/src/main/com/mongodb/internal/connection/Connection.java
index 6200a626897..95094b240c1 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/Connection.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/Connection.java
@@ -19,7 +19,6 @@
import com.mongodb.ReadPreference;
import com.mongodb.annotations.ThreadSafe;
import com.mongodb.connection.ConnectionDescription;
-import com.mongodb.internal.binding.BindingContext;
import com.mongodb.internal.binding.ReferenceCounted;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
@@ -47,11 +46,11 @@ public interface Connection extends ReferenceCounted {
@Nullable
T command(String database, BsonDocument command, FieldNameValidator fieldNameValidator, @Nullable ReadPreference readPreference,
- Decoder commandResultDecoder, BindingContext context);
+ Decoder commandResultDecoder, OperationContext operationContext);
@Nullable
T command(String database, BsonDocument command, FieldNameValidator commandFieldNameValidator,
- @Nullable ReadPreference readPreference, Decoder commandResultDecoder, BindingContext context,
+ @Nullable ReadPreference readPreference, Decoder commandResultDecoder, OperationContext operationContext,
boolean responseExpected, @Nullable SplittablePayload payload, @Nullable FieldNameValidator payloadFieldNameValidator);
diff --git a/driver-core/src/main/com/mongodb/internal/connection/ConnectionPool.java b/driver-core/src/main/com/mongodb/internal/connection/ConnectionPool.java
index 39a50063163..2129d42b941 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/ConnectionPool.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/ConnectionPool.java
@@ -18,15 +18,11 @@
import com.mongodb.MongoConnectionPoolClearedException;
import com.mongodb.annotations.ThreadSafe;
-import com.mongodb.connection.ConnectionPoolSettings;
import com.mongodb.internal.async.SingleResultCallback;
-import com.mongodb.internal.time.Timeout;
-import com.mongodb.internal.time.TimePoint;
-import org.bson.types.ObjectId;
import com.mongodb.lang.Nullable;
+import org.bson.types.ObjectId;
import java.io.Closeable;
-import java.util.concurrent.TimeUnit;
/**
* An instance of an implementation must be created in the {@linkplain #invalidate(Throwable) paused} state.
@@ -34,19 +30,10 @@
@ThreadSafe
interface ConnectionPool extends Closeable {
/**
- * Is equivalent to {@link #get(OperationContext, long, TimeUnit)} called with {@link ConnectionPoolSettings#getMaxWaitTime(TimeUnit)}.
- */
- InternalConnection get(OperationContext operationContext) throws MongoConnectionPoolClearedException;
-
- /**
- * @param operationContext operation context
- * @param timeout This is not a timeout for the whole {@link #get(OperationContext, long, TimeUnit)},
- * see {@link ConnectionPoolSettings#getMaxWaitTime(TimeUnit)}.
- *
- * See {@link Timeout#started(long, TimeUnit, TimePoint)}.
+ * @param operationContext the operation context
* @throws MongoConnectionPoolClearedException If detects that the pool is {@linkplain #invalidate(Throwable) paused}.
*/
- InternalConnection get(OperationContext operationContext, long timeout, TimeUnit timeUnit) throws MongoConnectionPoolClearedException;
+ InternalConnection get(OperationContext operationContext) throws MongoConnectionPoolClearedException;
/**
* Completes the {@code callback} with a {@link MongoConnectionPoolClearedException}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultAuthenticator.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultAuthenticator.java
index 13e7ec09a16..a9a3525a90a 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultAuthenticator.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultAuthenticator.java
@@ -46,10 +46,11 @@ class DefaultAuthenticator extends Authenticator implements SpeculativeAuthentic
}
@Override
- void authenticate(final InternalConnection connection, final ConnectionDescription connectionDescription) {
+ void authenticate(final InternalConnection connection, final ConnectionDescription connectionDescription,
+ final OperationContext operationContext) {
try {
setDelegate(connectionDescription);
- delegate.authenticate(connection, connectionDescription);
+ delegate.authenticate(connection, connectionDescription, operationContext);
} catch (Exception e) {
throw wrapException(e);
}
@@ -57,9 +58,9 @@ void authenticate(final InternalConnection connection, final ConnectionDescripti
@Override
void authenticateAsync(final InternalConnection connection, final ConnectionDescription connectionDescription,
- final SingleResultCallback callback) {
+ final OperationContext operationContext, final SingleResultCallback callback) {
setDelegate(connectionDescription);
- delegate.authenticateAsync(connection, connectionDescription, callback);
+ delegate.authenticateAsync(connection, connectionDescription, operationContext, callback);
}
@Override
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java
index 0375373c23b..5fb6de6f69a 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterFactory.java
@@ -31,6 +31,7 @@
import com.mongodb.event.CommandListener;
import com.mongodb.event.ServerListener;
import com.mongodb.event.ServerMonitorListener;
+import com.mongodb.internal.TimeoutSettings;
import com.mongodb.internal.VisibleForTesting;
import com.mongodb.internal.diagnostics.logging.Logger;
import com.mongodb.internal.diagnostics.logging.Loggers;
@@ -60,7 +61,10 @@ public final class DefaultClusterFactory {
public Cluster createCluster(final ClusterSettings originalClusterSettings, final ServerSettings originalServerSettings,
final ConnectionPoolSettings connectionPoolSettings,
final InternalConnectionPoolSettings internalConnectionPoolSettings,
- final StreamFactory streamFactory, final StreamFactory heartbeatStreamFactory,
+ final TimeoutSettings clusterTimeoutSettings,
+ final StreamFactory streamFactory,
+ final TimeoutSettings heartbeatTimeoutSettings,
+ final StreamFactory heartbeatStreamFactory,
@Nullable final MongoCredential credential,
final LoggerSettings loggerSettings,
@Nullable final CommandListener commandListener,
@@ -98,17 +102,22 @@ public Cluster createCluster(final ClusterSettings originalClusterSettings, fina
}
DnsSrvRecordMonitorFactory dnsSrvRecordMonitorFactory = new DefaultDnsSrvRecordMonitorFactory(clusterId, serverSettings, dnsClient);
+ InternalOperationContextFactory clusterOperationContextFactory =
+ new InternalOperationContextFactory(clusterTimeoutSettings, serverApi);
+ InternalOperationContextFactory heartBeatOperationContextFactory =
+ new InternalOperationContextFactory(heartbeatTimeoutSettings, serverApi);
if (clusterSettings.getMode() == ClusterConnectionMode.LOAD_BALANCED) {
ClusterableServerFactory serverFactory = new LoadBalancedClusterableServerFactory(serverSettings,
connectionPoolSettings, internalConnectionPoolSettings, streamFactory, credential, loggerSettings, commandListener,
applicationName, mongoDriverInformation != null ? mongoDriverInformation : MongoDriverInformation.builder().build(),
- compressorList, serverApi);
+ compressorList, serverApi, clusterOperationContextFactory);
return new LoadBalancedCluster(clusterId, clusterSettings, serverFactory, dnsSrvRecordMonitorFactory);
} else {
ClusterableServerFactory serverFactory = new DefaultClusterableServerFactory(serverSettings,
connectionPoolSettings, internalConnectionPoolSettings,
- streamFactory, heartbeatStreamFactory, credential, loggerSettings, commandListener, applicationName,
+ clusterOperationContextFactory, streamFactory, heartBeatOperationContextFactory, heartbeatStreamFactory, credential,
+ loggerSettings, commandListener, applicationName,
mongoDriverInformation != null ? mongoDriverInformation : MongoDriverInformation.builder().build(), compressorList,
serverApi, FaasEnvironment.getFaasEnvironment() != FaasEnvironment.UNKNOWN);
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java
index 7d0f5b62e51..880e1db8521 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultClusterableServerFactory.java
@@ -43,9 +43,11 @@ public class DefaultClusterableServerFactory implements ClusterableServerFactory
private final ServerSettings serverSettings;
private final ConnectionPoolSettings connectionPoolSettings;
private final InternalConnectionPoolSettings internalConnectionPoolSettings;
+ private final InternalOperationContextFactory clusterOperationContextFactory;
private final StreamFactory streamFactory;
- private final MongoCredentialWithCache credential;
+ private final InternalOperationContextFactory heartbeatOperationContextFactory;
private final StreamFactory heartbeatStreamFactory;
+ private final MongoCredentialWithCache credential;
private final LoggerSettings loggerSettings;
private final CommandListener commandListener;
private final String applicationName;
@@ -58,18 +60,20 @@ public class DefaultClusterableServerFactory implements ClusterableServerFactory
public DefaultClusterableServerFactory(
final ServerSettings serverSettings, final ConnectionPoolSettings connectionPoolSettings,
final InternalConnectionPoolSettings internalConnectionPoolSettings,
- final StreamFactory streamFactory, final StreamFactory heartbeatStreamFactory,
- @Nullable final MongoCredential credential,
- final LoggerSettings loggerSettings,
- @Nullable final CommandListener commandListener,
- @Nullable final String applicationName, @Nullable final MongoDriverInformation mongoDriverInformation,
+ final InternalOperationContextFactory clusterOperationContextFactory, final StreamFactory streamFactory,
+ final InternalOperationContextFactory heartbeatOperationContextFactory, final StreamFactory heartbeatStreamFactory,
+ @Nullable final MongoCredential credential, final LoggerSettings loggerSettings,
+ @Nullable final CommandListener commandListener, @Nullable final String applicationName,
+ @Nullable final MongoDriverInformation mongoDriverInformation,
final List compressorList, @Nullable final ServerApi serverApi, final boolean isFunctionAsAServiceEnvironment) {
this.serverSettings = serverSettings;
this.connectionPoolSettings = connectionPoolSettings;
this.internalConnectionPoolSettings = internalConnectionPoolSettings;
+ this.clusterOperationContextFactory = clusterOperationContextFactory;
this.streamFactory = streamFactory;
- this.credential = credential == null ? null : new MongoCredentialWithCache(credential);
+ this.heartbeatOperationContextFactory = heartbeatOperationContextFactory;
this.heartbeatStreamFactory = heartbeatStreamFactory;
+ this.credential = credential == null ? null : new MongoCredentialWithCache(credential);
this.loggerSettings = loggerSettings;
this.commandListener = commandListener;
this.applicationName = applicationName;
@@ -88,11 +92,11 @@ public ClusterableServer create(final Cluster cluster, final ServerAddress serve
// no credentials, compressor list, or command listener for the server monitor factory
new InternalStreamConnectionFactory(clusterMode, true, heartbeatStreamFactory, null, applicationName,
mongoDriverInformation, emptyList(), loggerSettings, null, serverApi),
- clusterMode, serverApi, isFunctionAsAServiceEnvironment, sdamProvider);
+ clusterMode, serverApi, isFunctionAsAServiceEnvironment, sdamProvider, heartbeatOperationContextFactory);
ConnectionPool connectionPool = new DefaultConnectionPool(serverId,
new InternalStreamConnectionFactory(clusterMode, streamFactory, credential, applicationName,
mongoDriverInformation, compressorList, loggerSettings, commandListener, serverApi),
- connectionPoolSettings, internalConnectionPoolSettings, sdamProvider);
+ connectionPoolSettings, internalConnectionPoolSettings, sdamProvider, clusterOperationContextFactory);
ServerListener serverListener = singleServerListener(serverSettings);
SdamServerDescriptionManager sdam = new DefaultSdamServerDescriptionManager(cluster, serverId, serverListener, serverMonitor,
connectionPool, clusterMode);
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java
index 26676718d41..78db18db2dc 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java
@@ -21,7 +21,6 @@
import com.mongodb.MongoInterruptedException;
import com.mongodb.MongoServerUnavailableException;
import com.mongodb.MongoTimeoutException;
-import com.mongodb.RequestContext;
import com.mongodb.annotations.NotThreadSafe;
import com.mongodb.annotations.ThreadSafe;
import com.mongodb.connection.ClusterId;
@@ -52,9 +51,8 @@
import com.mongodb.internal.inject.OptionalProvider;
import com.mongodb.internal.logging.LogMessage;
import com.mongodb.internal.logging.StructuredLogger;
-import com.mongodb.internal.session.SessionContext;
import com.mongodb.internal.thread.DaemonThreadFactory;
-import com.mongodb.internal.time.TimePoint;
+import com.mongodb.internal.time.StartTime;
import com.mongodb.internal.time.Timeout;
import com.mongodb.lang.NonNull;
import com.mongodb.lang.Nullable;
@@ -120,18 +118,17 @@
import static com.mongodb.internal.logging.LogMessage.Entry.Name.SERVICE_ID;
import static com.mongodb.internal.logging.LogMessage.Entry.Name.WAIT_QUEUE_TIMEOUT_MS;
import static com.mongodb.internal.logging.LogMessage.Level.DEBUG;
-import static com.mongodb.internal.thread.InterruptionUtil.interruptAndCreateMongoInterruptedException;
import static java.lang.String.format;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
-@SuppressWarnings("deprecation")
@ThreadSafe
final class DefaultConnectionPool implements ConnectionPool {
private static final Logger LOGGER = Loggers.getLogger("connection");
private static final StructuredLogger STRUCTURED_LOGGER = new StructuredLogger("connection");
private final ConcurrentPool pool;
private final ConnectionPoolSettings settings;
+ private final InternalOperationContextFactory operationContextFactory;
private final BackgroundMaintenanceManager backgroundMaintenance;
private final AsyncWorkManager asyncWorkManager;
private final ConnectionPoolListener connectionPoolListener;
@@ -145,8 +142,10 @@ final class DefaultConnectionPool implements ConnectionPool {
@VisibleForTesting(otherwise = PRIVATE)
DefaultConnectionPool(final ServerId serverId, final InternalConnectionFactory internalConnectionFactory,
- final ConnectionPoolSettings settings, final OptionalProvider sdamProvider) {
- this(serverId, internalConnectionFactory, settings, InternalConnectionPoolSettings.builder().build(), sdamProvider);
+ final ConnectionPoolSettings settings, final OptionalProvider sdamProvider,
+ final InternalOperationContextFactory operationContextFactory) {
+ this(serverId, internalConnectionFactory, settings, InternalConnectionPoolSettings.builder().build(), sdamProvider,
+ operationContextFactory);
}
/**
@@ -160,13 +159,15 @@ final class DefaultConnectionPool implements ConnectionPool {
*/
DefaultConnectionPool(final ServerId serverId, final InternalConnectionFactory internalConnectionFactory,
final ConnectionPoolSettings settings, final InternalConnectionPoolSettings internalSettings,
- final OptionalProvider sdamProvider) {
+ final OptionalProvider sdamProvider,
+ final InternalOperationContextFactory operationContextFactory) {
this.serverId = notNull("serverId", serverId);
this.settings = notNull("settings", settings);
UsageTrackingInternalConnectionItemFactory connectionItemFactory =
new UsageTrackingInternalConnectionItemFactory(internalConnectionFactory);
pool = new ConcurrentPool<>(maxSize(settings), connectionItemFactory, format("The server at %s is no longer available",
serverId.getAddress()));
+ this.operationContextFactory = assertNotNull(operationContextFactory);
this.sdamProvider = assertNotNull(sdamProvider);
this.connectionPoolListener = getConnectionPoolListener(settings);
backgroundMaintenance = new BackgroundMaintenanceManager();
@@ -189,18 +190,13 @@ public int getGeneration(@NonNull final ObjectId serviceId) {
@Override
public InternalConnection get(final OperationContext operationContext) {
- return get(operationContext, settings.getMaxWaitTime(MILLISECONDS), MILLISECONDS);
- }
-
- @Override
- public InternalConnection get(final OperationContext operationContext, final long timeoutValue, final TimeUnit timeUnit) {
- TimePoint checkoutStart = connectionCheckoutStarted(operationContext);
- Timeout timeout = Timeout.started(timeoutValue, timeUnit, checkoutStart);
+ StartTime checkoutStart = connectionCheckoutStarted(operationContext);
+ Timeout waitQueueTimeout = operationContext.getTimeoutContext().startWaitQueueTimeout(checkoutStart);
try {
stateAndGeneration.throwIfClosedOrPaused();
- PooledConnection connection = getPooledConnection(timeout);
+ PooledConnection connection = getPooledConnection(waitQueueTimeout, checkoutStart);
if (!connection.opened()) {
- connection = openConcurrencyLimiter.openOrGetAvailable(connection, timeout);
+ connection = openConcurrencyLimiter.openOrGetAvailable(operationContext, connection, waitQueueTimeout, checkoutStart);
}
connection.checkedOutForOperation(operationContext);
connectionCheckedOut(operationContext, connection, checkoutStart);
@@ -212,12 +208,12 @@ public InternalConnection get(final OperationContext operationContext, final lon
@Override
public void getAsync(final OperationContext operationContext, final SingleResultCallback callback) {
- TimePoint checkoutStart = connectionCheckoutStarted(operationContext);
- Timeout timeout = Timeout.started(settings.getMaxWaitTime(NANOSECONDS), checkoutStart);
+ StartTime checkoutStart = connectionCheckoutStarted(operationContext);
+ Timeout maxWaitTimeout = checkoutStart.timeoutAfterOrInfiniteIfNegative(settings.getMaxWaitTime(NANOSECONDS), NANOSECONDS);
SingleResultCallback eventSendingCallback = (connection, failure) -> {
SingleResultCallback errHandlingCallback = errorHandlingCallback(callback, LOGGER);
if (failure == null) {
- connection.checkedOutForOperation(operationContext);
+ assertNotNull(connection).checkedOutForOperation(operationContext);
connectionCheckedOut(operationContext, connection, checkoutStart);
errHandlingCallback.onResult(connection, null);
} else {
@@ -230,13 +226,13 @@ public void getAsync(final OperationContext operationContext, final SingleResult
eventSendingCallback.onResult(null, e);
return;
}
- asyncWorkManager.enqueue(new Task(timeout, t -> {
+ asyncWorkManager.enqueue(new Task(maxWaitTimeout, checkoutStart, t -> {
if (t != null) {
eventSendingCallback.onResult(null, t);
} else {
PooledConnection connection;
try {
- connection = getPooledConnection(timeout);
+ connection = getPooledConnection(maxWaitTimeout, checkoutStart);
} catch (Exception e) {
eventSendingCallback.onResult(null, e);
return;
@@ -244,7 +240,8 @@ public void getAsync(final OperationContext operationContext, final SingleResult
if (connection.opened()) {
eventSendingCallback.onResult(connection, null);
} else {
- openConcurrencyLimiter.openAsyncWithConcurrencyLimit(connection, timeout, eventSendingCallback);
+ openConcurrencyLimiter.openWithConcurrencyLimitAsync(
+ operationContext, connection, maxWaitTimeout, checkoutStart, eventSendingCallback);
}
}
}));
@@ -255,7 +252,7 @@ public void getAsync(final OperationContext operationContext, final SingleResult
* and returns {@code t} if it is not {@link MongoOpenConnectionInternalException},
* or returns {@code t.}{@linkplain MongoOpenConnectionInternalException#getCause() getCause()} otherwise.
*/
- private Throwable checkOutFailed(final Throwable t, final OperationContext operationContext, final TimePoint checkoutStart) {
+ private Throwable checkOutFailed(final Throwable t, final OperationContext operationContext, final StartTime checkoutStart) {
Throwable result = t;
Reason reason;
if (t instanceof MongoTimeoutException) {
@@ -334,16 +331,22 @@ public int getGeneration() {
return stateAndGeneration.generation();
}
- private PooledConnection getPooledConnection(final Timeout timeout) throws MongoTimeoutException {
+ private PooledConnection getPooledConnection(final Timeout waitQueueTimeout, final StartTime startTime) throws MongoTimeoutException {
try {
- UsageTrackingInternalConnection internalConnection = pool.get(timeout.remainingOrInfinite(NANOSECONDS), NANOSECONDS);
+ UsageTrackingInternalConnection internalConnection = waitQueueTimeout.call(NANOSECONDS,
+ () -> pool.get(-1L, NANOSECONDS),
+ (ns) -> pool.get(ns, NANOSECONDS),
+ () -> pool.get(0L, NANOSECONDS));
while (shouldPrune(internalConnection)) {
pool.release(internalConnection, true);
- internalConnection = pool.get(timeout.remainingOrInfinite(NANOSECONDS), NANOSECONDS);
+ internalConnection = waitQueueTimeout.call(NANOSECONDS,
+ () -> pool.get(-1L, NANOSECONDS),
+ (ns) -> pool.get(ns, NANOSECONDS),
+ () -> pool.get(0L, NANOSECONDS));
}
return new PooledConnection(internalConnection);
} catch (MongoTimeoutException e) {
- throw createTimeoutException(timeout);
+ throw createTimeoutException(startTime);
}
}
@@ -357,12 +360,13 @@ private PooledConnection getPooledConnectionImmediateUnfair() {
return internalConnection == null ? null : new PooledConnection(internalConnection);
}
- private MongoTimeoutException createTimeoutException(final Timeout timeout) {
+ private MongoTimeoutException createTimeoutException(final StartTime startTime) {
+ long elapsedMs = startTime.elapsed().toMillis();
int numPinnedToCursor = pinnedStatsManager.getNumPinnedToCursor();
int numPinnedToTransaction = pinnedStatsManager.getNumPinnedToTransaction();
if (numPinnedToCursor == 0 && numPinnedToTransaction == 0) {
- return new MongoTimeoutException(format("Timed out after %s while waiting for a connection to server %s.",
- timeout.toUserString(), serverId.getAddress()));
+ return new MongoTimeoutException(format("Timed out after %d ms while waiting for a connection to server %s.",
+ elapsedMs, serverId.getAddress()));
} else {
int maxSize = pool.getMaxSize();
int numInUse = pool.getInUseCount();
@@ -391,10 +395,10 @@ private MongoTimeoutException createTimeoutException(final Timeout timeout) {
int numOtherInUse = numInUse - numPinnedToCursor - numPinnedToTransaction;
assertTrue(numOtherInUse >= 0);
assertTrue(numPinnedToCursor + numPinnedToTransaction + numOtherInUse <= maxSize);
- return new MongoTimeoutException(format("Timed out after %s while waiting for a connection to server %s. Details: "
+ return new MongoTimeoutException(format("Timed out after %d ms while waiting for a connection to server %s. Details: "
+ "maxPoolSize: %s, connections in use by cursors: %d, connections in use by transactions: %d, "
+ "connections in use by other operations: %d",
- timeout.toUserString(), serverId.getAddress(),
+ elapsedMs, serverId.getAddress(),
sizeToString(maxSize), numPinnedToCursor, numPinnedToTransaction,
numOtherInUse));
}
@@ -418,7 +422,8 @@ void doMaintenance() {
if (shouldEnsureMinSize()) {
pool.ensureMinSize(settings.getMinSize(), newConnection -> {
try {
- openConcurrencyLimiter.openImmediatelyAndTryHandOverOrRelease(new PooledConnection(newConnection));
+ OperationContext operationContext = operationContextFactory.createMaintenanceContext();
+ openConcurrencyLimiter.openImmediatelyAndTryHandOverOrRelease(operationContext, new PooledConnection(newConnection));
} catch (MongoException | MongoOpenConnectionInternalException e) {
RuntimeException actualException = e instanceof MongoOpenConnectionInternalException
? (RuntimeException) e.getCause()
@@ -504,13 +509,14 @@ private void connectionPoolCreated(final ConnectionPoolListener connectionPoolLi
* Send both current and deprecated events in order to preserve backwards compatibility.
* Must not throw {@link Exception}s.
*
- * @return A {@link TimePoint} before executing {@link ConnectionPoolListener#connectionCreated(ConnectionCreatedEvent)}
+ * @return A {@link StartTime} before executing {@link ConnectionPoolListener#connectionCreated(ConnectionCreatedEvent)}
* and logging the event. This order is required by
+
* CMAP
* and {@link ConnectionReadyEvent#getElapsedTime(TimeUnit)}.
*/
- private TimePoint connectionCreated(final ConnectionPoolListener connectionPoolListener, final ConnectionId connectionId) {
- TimePoint openStart = TimePoint.now();
+ private StartTime connectionCreated(final ConnectionPoolListener connectionPoolListener, final ConnectionId connectionId) {
+ StartTime openStart = StartTime.now();
logEventMessage("Connection created",
"Connection created: address={}:{}, driver-generated ID={}",
connectionId.getLocalValue());
@@ -545,7 +551,7 @@ private void connectionClosed(final ConnectionPoolListener connectionPoolListene
private void connectionCheckedOut(
final OperationContext operationContext,
final PooledConnection connection,
- final TimePoint checkoutStart) {
+ final StartTime checkoutStart) {
Duration checkoutDuration = checkoutStart.elapsed();
ConnectionId connectionId = getId(connection);
ClusterId clusterId = serverId.getClusterId();
@@ -562,18 +568,19 @@ private void connectionCheckedOut(
}
/**
- * @return A {@link TimePoint} before executing
+ * @return A {@link StartTime} before executing
* {@link ConnectionPoolListener#connectionCheckOutStarted(ConnectionCheckOutStartedEvent)} and logging the event.
* This order is required by
* CMAP
* and {@link ConnectionCheckedOutEvent#getElapsedTime(TimeUnit)}, {@link ConnectionCheckOutFailedEvent#getElapsedTime(TimeUnit)}.
*/
- private TimePoint connectionCheckoutStarted(final OperationContext operationContext) {
- TimePoint checkoutStart = TimePoint.now();
+ private StartTime connectionCheckoutStarted(final OperationContext operationContext) {
+ StartTime checkoutStart = StartTime.now();
logEventMessage("Connection checkout started", "Checkout started for connection to {}:{}");
connectionPoolListener.connectionCheckOutStarted(new ConnectionCheckOutStartedEvent(serverId, operationContext.getId()));
return checkoutStart;
+
}
/**
@@ -598,7 +605,7 @@ private class PooledConnection implements InternalConnection {
private final UsageTrackingInternalConnection wrapped;
private final AtomicBoolean isClosed = new AtomicBoolean();
private Connection.PinningMode pinningMode;
- private OperationContext operationContext;
+ private long operationId;
PooledConnection(final UsageTrackingInternalConnection wrapped) {
this.wrapped = notNull("wrapped", wrapped);
@@ -610,19 +617,19 @@ public int getGeneration() {
}
/**
- * Associates this with the operation context and establishes the checked out start time
+ * Associates this with the operation id and establishes the checked out start time
*/
public void checkedOutForOperation(final OperationContext operationContext) {
- this.operationContext = operationContext;
+ this.operationId = operationContext.getId();
}
@Override
- public void open() {
+ public void open(final OperationContext operationContext) {
assertFalse(isClosed.get());
- TimePoint openStart;
+ StartTime openStart;
try {
openStart = connectionCreated(connectionPoolListener, wrapped.getDescription().getConnectionId());
- wrapped.open();
+ wrapped.open(operationContext);
} catch (Exception e) {
closeAndHandleOpenFailure();
throw new MongoOpenConnectionInternalException(e);
@@ -631,10 +638,10 @@ public void open() {
}
@Override
- public void openAsync(final SingleResultCallback callback) {
+ public void openAsync(final OperationContext operationContext, final SingleResultCallback callback) {
assertFalse(isClosed.get());
- TimePoint openStart = connectionCreated(connectionPoolListener, wrapped.getDescription().getConnectionId());
- wrapped.openAsync((nullResult, failure) -> {
+ StartTime openStart = connectionCreated(connectionPoolListener, wrapped.getDescription().getConnectionId());
+ wrapped.openAsync(operationContext, (nullResult, failure) -> {
if (failure != null) {
closeAndHandleOpenFailure();
callback.onResult(null, new MongoOpenConnectionInternalException(failure));
@@ -664,8 +671,7 @@ private void connectionCheckedIn() {
logEventMessage("Connection checked in",
"Connection checked in: address={}:{}, driver-generated ID={}",
connectionId.getLocalValue());
-
- connectionPoolListener.connectionCheckedIn(new ConnectionCheckedInEvent(connectionId, operationContext.getId()));
+ connectionPoolListener.connectionCheckedIn(new ConnectionCheckedInEvent(connectionId, operationId));
}
void release() {
@@ -701,7 +707,7 @@ private void closeAndHandleOpenFailure() {
/**
* Must not throw {@link Exception}s.
*/
- private void handleOpenSuccess(final TimePoint openStart) {
+ private void handleOpenSuccess(final StartTime openStart) {
Duration openDuration = openStart.elapsed();
ConnectionId connectionId = getId(this);
ClusterId clusterId = serverId.getClusterId();
@@ -731,34 +737,27 @@ public ByteBuf getBuffer(final int capacity) {
}
@Override
- public void sendMessage(final List byteBuffers, final int lastRequestId) {
+ public void sendMessage(final List byteBuffers, final int lastRequestId, final OperationContext operationContext) {
isTrue("open", !isClosed.get());
- wrapped.sendMessage(byteBuffers, lastRequestId);
+ wrapped.sendMessage(byteBuffers, lastRequestId, operationContext);
}
@Override
- public T sendAndReceive(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext,
- final RequestContext requestContext, final OperationContext operationContext) {
+ public T sendAndReceive(final CommandMessage message, final Decoder decoder, final OperationContext operationContext) {
isTrue("open", !isClosed.get());
- return wrapped.sendAndReceive(message, decoder, sessionContext, requestContext, operationContext);
+ return wrapped.sendAndReceive(message, decoder, operationContext);
}
@Override
- public void send(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext) {
+ public void send(final CommandMessage message, final Decoder decoder, final OperationContext operationContext) {
isTrue("open", !isClosed.get());
- wrapped.send(message, decoder, sessionContext);
+ wrapped.send(message, decoder, operationContext);
}
@Override
- public T receive(final Decoder decoder, final SessionContext sessionContext) {
+ public T receive(final Decoder decoder, final OperationContext operationContext) {
isTrue("open", !isClosed.get());
- return wrapped.receive(decoder, sessionContext);
- }
-
- @Override
- public T receive(final Decoder decoder, final SessionContext sessionContext, final int additionalTimeout) {
- isTrue("open", !isClosed.get());
- return wrapped.receive(decoder, sessionContext, additionalTimeout);
+ return wrapped.receive(decoder, operationContext);
}
@Override
@@ -768,28 +767,30 @@ public boolean hasMoreToCome() {
}
@Override
- public void sendAndReceiveAsync(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext,
- final RequestContext requestContext, final OperationContext operationContext, final SingleResultCallback callback) {
+ public void sendAndReceiveAsync(final CommandMessage message, final Decoder decoder,
+ final OperationContext operationContext, final SingleResultCallback callback) {
isTrue("open", !isClosed.get());
- wrapped.sendAndReceiveAsync(message, decoder, sessionContext, requestContext, operationContext, (result, t) -> callback.onResult(result, t));
+ wrapped.sendAndReceiveAsync(message, decoder, operationContext, callback);
}
@Override
- public ResponseBuffers receiveMessage(final int responseTo) {
+ public ResponseBuffers receiveMessage(final int responseTo, final OperationContext operationContext) {
isTrue("open", !isClosed.get());
- return wrapped.receiveMessage(responseTo);
+ return wrapped.receiveMessage(responseTo, operationContext);
}
@Override
- public void sendMessageAsync(final List byteBuffers, final int lastRequestId, final SingleResultCallback callback) {
+ public void sendMessageAsync(final List byteBuffers, final int lastRequestId, final OperationContext operationContext,
+ final SingleResultCallback callback) {
isTrue("open", !isClosed.get());
- wrapped.sendMessageAsync(byteBuffers, lastRequestId, (result, t) -> callback.onResult(null, t));
+ wrapped.sendMessageAsync(byteBuffers, lastRequestId, operationContext, (result, t) -> callback.onResult(null, t));
}
@Override
- public void receiveMessageAsync(final int responseTo, final SingleResultCallback callback) {
+ public void receiveMessageAsync(final int responseTo, final OperationContext operationContext,
+ final SingleResultCallback callback) {
isTrue("open", !isClosed.get());
- wrapped.receiveMessageAsync(responseTo, (result, t) -> callback.onResult(result, t));
+ wrapped.receiveMessageAsync(responseTo, operationContext, callback);
}
@Override
@@ -825,7 +826,7 @@ public ServerDescription getInitialServerDescription() {
/**
* This internal exception is used to express an exceptional situation encountered when opening a connection.
* It exists because it allows consolidating the code that sends events for exceptional situations in a
- * {@linkplain #checkOutFailed(Throwable, OperationContext, TimePoint) single place}, it must not be observable by an external code.
+ * {@linkplain #checkOutFailed(Throwable, OperationContext, StartTime) single place}, it must not be observable by an external code.
*/
private static final class MongoOpenConnectionInternalException extends RuntimeException {
private static final long serialVersionUID = 1;
@@ -902,19 +903,29 @@ private final class OpenConcurrencyLimiter {
desiredConnectionSlots = new LinkedList<>();
}
- PooledConnection openOrGetAvailable(final PooledConnection connection, final Timeout timeout) throws MongoTimeoutException {
- PooledConnection result = openWithConcurrencyLimit(connection, OpenWithConcurrencyLimitMode.TRY_GET_AVAILABLE, timeout);
+ PooledConnection openOrGetAvailable(final OperationContext operationContext, final PooledConnection connection,
+ final Timeout waitQueueTimeout, final StartTime startTime)
+ throws MongoTimeoutException {
+ PooledConnection result = openWithConcurrencyLimit(
+ operationContext, connection, OpenWithConcurrencyLimitMode.TRY_GET_AVAILABLE,
+ waitQueueTimeout, startTime);
return assertNotNull(result);
}
- void openImmediatelyAndTryHandOverOrRelease(final PooledConnection connection) throws MongoTimeoutException {
- assertNull(openWithConcurrencyLimit(connection, OpenWithConcurrencyLimitMode.TRY_HAND_OVER_OR_RELEASE, Timeout.immediate()));
+ void openImmediatelyAndTryHandOverOrRelease(final OperationContext operationContext,
+ final PooledConnection connection) throws MongoTimeoutException {
+ StartTime startTime = StartTime.now();
+ Timeout timeout = startTime.asTimeout();
+ assertNull(openWithConcurrencyLimit(
+ operationContext,
+ connection, OpenWithConcurrencyLimitMode.TRY_HAND_OVER_OR_RELEASE,
+ timeout, startTime));
}
/**
- * This method can be thought of as operating in two phases.
- * In the first phase it tries to synchronously acquire a permit to open the {@code connection}
- * or get a different {@linkplain PooledConnection#opened() opened} connection if {@code mode} is
+ * This method can be thought of as operating in two phases. In the first phase it tries to synchronously
+ * acquire a permit to open the {@code connection} or get a different
+ * {@linkplain PooledConnection#opened() opened} connection if {@code mode} is
* {@link OpenWithConcurrencyLimitMode#TRY_GET_AVAILABLE} and one becomes available while waiting for a permit.
* The first phase has one of the following outcomes:
*
@@ -925,7 +936,7 @@ void openImmediatelyAndTryHandOverOrRelease(final PooledConnection connection) t
* This outcome is possible only if {@code mode} is {@link OpenWithConcurrencyLimitMode#TRY_GET_AVAILABLE}.
* - A permit is acquired, {@link #connectionCreated(ConnectionPoolListener, ConnectionId)} is reported
* and an attempt to open the specified {@code connection} is made. This is the second phase in which
- * the {@code connection} is {@linkplain PooledConnection#open() opened synchronously}.
+ * the {@code connection} is {@linkplain InternalConnection#open(OperationContext) opened synchronously}.
* The attempt to open the {@code connection} has one of the following outcomes
* combined with releasing the acquired permit:
*
@@ -939,20 +950,23 @@ void openImmediatelyAndTryHandOverOrRelease(final PooledConnection connection) t
*
*
*
- * @param timeout Applies only to the first phase.
- * @return An {@linkplain PooledConnection#opened() opened} connection which is
- * either the specified {@code connection},
- * or potentially a different one if {@code mode} is {@link OpenWithConcurrencyLimitMode#TRY_GET_AVAILABLE},
- * or {@code null} if {@code mode} is {@link OpenWithConcurrencyLimitMode#TRY_HAND_OVER_OR_RELEASE}.
+ * @param operationContext the operation context
+ * @param waitQueueTimeout Applies only to the first phase.
+ * @return An {@linkplain PooledConnection#opened() opened} connection which is either the specified
+ * {@code connection}, or potentially a different one if {@code mode} is
+ * {@link OpenWithConcurrencyLimitMode#TRY_GET_AVAILABLE}, or {@code null} if {@code mode} is
+ * {@link OpenWithConcurrencyLimitMode#TRY_HAND_OVER_OR_RELEASE}.
* @throws MongoTimeoutException If the first phase timed out.
*/
@Nullable
- private PooledConnection openWithConcurrencyLimit(final PooledConnection connection, final OpenWithConcurrencyLimitMode mode,
- final Timeout timeout) throws MongoTimeoutException {
+ private PooledConnection openWithConcurrencyLimit(final OperationContext operationContext,
+ final PooledConnection connection, final OpenWithConcurrencyLimitMode mode,
+ final Timeout waitQueueTimeout, final StartTime startTime)
+ throws MongoTimeoutException {
PooledConnection availableConnection;
try {//phase one
availableConnection = acquirePermitOrGetAvailableOpenedConnection(
- mode == OpenWithConcurrencyLimitMode.TRY_GET_AVAILABLE, timeout);
+ mode == OpenWithConcurrencyLimitMode.TRY_GET_AVAILABLE, waitQueueTimeout, startTime);
} catch (Exception e) {
connection.closeSilently();
throw e;
@@ -962,7 +976,7 @@ private PooledConnection openWithConcurrencyLimit(final PooledConnection connect
return availableConnection;
} else {//acquired a permit, phase two
try {
- connection.open();
+ connection.open(operationContext);
if (mode == OpenWithConcurrencyLimitMode.TRY_HAND_OVER_OR_RELEASE) {
tryHandOverOrRelease(connection.wrapped);
return null;
@@ -976,23 +990,25 @@ private PooledConnection openWithConcurrencyLimit(final PooledConnection connect
}
/**
- * This method is similar to {@link #openWithConcurrencyLimit(PooledConnection, OpenWithConcurrencyLimitMode, Timeout)}
+ * This method is similar to {@link #openWithConcurrencyLimit(OperationContext, PooledConnection, OpenWithConcurrencyLimitMode, Timeout, StartTime)}
* with the following differences:
*
* - It does not have the {@code mode} parameter and acts as if this parameter were
* {@link OpenWithConcurrencyLimitMode#TRY_GET_AVAILABLE}.
* - While the first phase is still synchronous, the {@code connection} is
- * {@linkplain PooledConnection#openAsync(SingleResultCallback) opened asynchronously} in the second phase.
+ * {@linkplain InternalConnection#openAsync(OperationContext, SingleResultCallback) opened asynchronously} in the second phase.
* - Instead of returning a result or throwing an exception via Java {@code return}/{@code throw} statements,
* it calls {@code callback.}{@link SingleResultCallback#onResult(Object, Throwable) onResult(result, failure)}
* and passes either a {@link PooledConnection} or an {@link Exception}.
*
*/
- void openAsyncWithConcurrencyLimit(
- final PooledConnection connection, final Timeout timeout, final SingleResultCallback callback) {
+ void openWithConcurrencyLimitAsync(
+ final OperationContext operationContext, final PooledConnection connection,
+ final Timeout maxWaitTimeout, final StartTime startTime,
+ final SingleResultCallback callback) {
PooledConnection availableConnection;
try {//phase one
- availableConnection = acquirePermitOrGetAvailableOpenedConnection(true, timeout);
+ availableConnection = acquirePermitOrGetAvailableOpenedConnection(true, maxWaitTimeout, startTime);
} catch (Exception e) {
connection.closeSilently();
callback.onResult(null, e);
@@ -1002,7 +1018,7 @@ void openAsyncWithConcurrencyLimit(
connection.closeSilently();
callback.onResult(availableConnection, null);
} else {//acquired a permit, phase two
- connection.openAsync((nullResult, failure) -> {
+ connection.openAsync(operationContext, (nullResult, failure) -> {
releasePermit();
if (failure != null) {
callback.onResult(null, failure);
@@ -1022,7 +1038,8 @@ void openAsyncWithConcurrencyLimit(
* set on entry to this method or is interrupted while waiting to get an available opened connection.
*/
@Nullable
- private PooledConnection acquirePermitOrGetAvailableOpenedConnection(final boolean tryGetAvailable, final Timeout timeout)
+ private PooledConnection acquirePermitOrGetAvailableOpenedConnection(final boolean tryGetAvailable,
+ final Timeout waitQueueTimeout, final StartTime startTime)
throws MongoTimeoutException, MongoInterruptedException {
PooledConnection availableConnection = null;
boolean expressedDesireToGetAvailableConnection = false;
@@ -1048,15 +1065,16 @@ private PooledConnection acquirePermitOrGetAvailableOpenedConnection(final boole
expressDesireToGetAvailableConnection();
expressedDesireToGetAvailableConnection = true;
}
- long remainingNanos = timeout.remainingOrInfinite(NANOSECONDS);
while (permits == 0
// the absence of short-circuiting is of importance
& !stateAndGeneration.throwIfClosedOrPaused()
& (availableConnection = tryGetAvailable ? tryGetAvailableConnection() : null) == null) {
- if (Timeout.expired(remainingNanos)) {
- throw createTimeoutException(timeout);
- }
- remainingNanos = awaitNanos(permitAvailableOrHandedOverOrClosedOrPausedCondition, remainingNanos);
+
+ Timeout.onExistsAndExpired(waitQueueTimeout, () -> {
+ throw createTimeoutException(startTime);
+ });
+ waitQueueTimeout.awaitOn(permitAvailableOrHandedOverOrClosedOrPausedCondition,
+ () -> "acquiring permit or getting available opened connection");
}
if (availableConnection == null) {
assertTrue(permits > 0);
@@ -1129,28 +1147,10 @@ void tryHandOverOrRelease(final UsageTrackingInternalConnection openConnection)
void signalClosedOrPaused() {
withUnfairLock(lock, permitAvailableOrHandedOverOrClosedOrPausedCondition::signalAll);
}
-
- /**
- * @param timeoutNanos See {@link Timeout#started(long, TimePoint)}.
- * @return The remaining duration as per {@link Timeout#remainingOrInfinite(TimeUnit)} if waiting ended early either
- * spuriously or because of receiving a signal.
- */
- private long awaitNanos(final Condition condition, final long timeoutNanos) throws MongoInterruptedException {
- try {
- if (timeoutNanos < 0 || timeoutNanos == Long.MAX_VALUE) {
- condition.await();
- return -1;
- } else {
- return Math.max(0, condition.awaitNanos(timeoutNanos));
- }
- } catch (InterruptedException e) {
- throw interruptAndCreateMongoInterruptedException(null, e);
- }
- }
}
/**
- * @see OpenConcurrencyLimiter#openWithConcurrencyLimit(PooledConnection, OpenWithConcurrencyLimitMode, Timeout)
+ * @see OpenConcurrencyLimiter#openWithConcurrencyLimit(OperationContext, PooledConnection, OpenWithConcurrencyLimitMode, Timeout, StartTime)
*/
private enum OpenWithConcurrencyLimitMode {
TRY_GET_AVAILABLE,
@@ -1341,11 +1341,11 @@ private void workerRun() {
while (state != State.CLOSED) {
try {
Task task = tasks.take();
- if (task.timeout().expired()) {
- task.failAsTimedOut();
- } else {
- task.execute();
- }
+
+ task.timeout().run(NANOSECONDS,
+ () -> task.execute(),
+ (ns) -> task.execute(),
+ () -> task.failAsTimedOut());
} catch (InterruptedException closed) {
// fail the rest of the tasks and stop
} catch (Exception e) {
@@ -1391,11 +1391,13 @@ private enum State {
@NotThreadSafe
final class Task {
private final Timeout timeout;
+ private final StartTime startTime;
private final Consumer action;
private boolean completed;
- Task(final Timeout timeout, final Consumer action) {
+ Task(final Timeout timeout, final StartTime startTime, final Consumer action) {
this.timeout = timeout;
+ this.startTime = startTime;
this.action = action;
}
@@ -1408,7 +1410,7 @@ void failAsClosed() {
}
void failAsTimedOut() {
- doComplete(() -> createTimeoutException(timeout));
+ doComplete(() -> createTimeoutException(startTime));
}
private void doComplete(final Supplier failureSupplier) {
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultServer.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultServer.java
index 2b300cdfa50..8f3d0f09fd9 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultServer.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultServer.java
@@ -18,7 +18,6 @@
import com.mongodb.MongoException;
import com.mongodb.MongoServerUnavailableException;
-import com.mongodb.MongoSocketException;
import com.mongodb.ReadPreference;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ConnectionDescription;
@@ -29,7 +28,6 @@
import com.mongodb.event.ServerOpeningEvent;
import com.mongodb.internal.VisibleForTesting;
import com.mongodb.internal.async.SingleResultCallback;
-import com.mongodb.internal.binding.BindingContext;
import com.mongodb.internal.connection.SdamServerDescriptionManager.SdamIssue;
import com.mongodb.internal.diagnostics.logging.Logger;
import com.mongodb.internal.diagnostics.logging.Loggers;
@@ -198,15 +196,16 @@ ServerId serverId() {
return serverId;
}
- private class DefaultServerProtocolExecutor implements ProtocolExecutor {
+ private class DefaultServerProtocolExecutor extends AbstractProtocolExecutor {
@SuppressWarnings("unchecked")
@Override
public T execute(final CommandProtocol protocol, final InternalConnection connection,
final SessionContext sessionContext) {
try {
- protocol.sessionContext(new ClusterClockAdvancingSessionContext(sessionContext, clusterClock));
- return protocol.execute(connection);
+ return protocol
+ .withSessionContext(new ClusterClockAdvancingSessionContext(sessionContext, clusterClock))
+ .execute(connection);
} catch (MongoException e) {
try {
sdam.handleExceptionAfterHandshake(SdamIssue.specific(e, sdam.context(connection)));
@@ -216,9 +215,9 @@ public T execute(final CommandProtocol protocol, final InternalConnection
if (e instanceof MongoWriteConcernWithResponseException) {
return (T) ((MongoWriteConcernWithResponseException) e).getResponse();
} else {
- if (e instanceof MongoSocketException && sessionContext.hasSession()) {
+ if (shouldMarkSessionDirty(e, sessionContext)) {
sessionContext.markSessionDirty();
- }
+ }
throw e;
}
}
@@ -228,8 +227,8 @@ public T execute(final CommandProtocol protocol, final InternalConnection
@Override
public void executeAsync(final CommandProtocol protocol, final InternalConnection connection,
final SessionContext sessionContext, final SingleResultCallback callback) {
- protocol.sessionContext(new ClusterClockAdvancingSessionContext(sessionContext, clusterClock));
- protocol.executeAsync(connection, errorHandlingCallback((result, t) -> {
+ protocol.withSessionContext(new ClusterClockAdvancingSessionContext(sessionContext, clusterClock))
+ .executeAsync(connection, errorHandlingCallback((result, t) -> {
if (t != null) {
try {
sdam.handleExceptionAfterHandshake(SdamIssue.specific(t, sdam.context(connection)));
@@ -239,7 +238,7 @@ public void executeAsync(final CommandProtocol protocol, final InternalCo
if (t instanceof MongoWriteConcernWithResponseException) {
callback.onResult((T) ((MongoWriteConcernWithResponseException) t).getResponse(), null);
} else {
- if (t instanceof MongoSocketException && sessionContext.hasSession()) {
+ if (shouldMarkSessionDirty(t, sessionContext)) {
sessionContext.markSessionDirty();
}
callback.onResult(null, t);
@@ -295,16 +294,16 @@ public ConnectionDescription getDescription() {
@Override
public T command(final String database, final BsonDocument command, final FieldNameValidator fieldNameValidator,
@Nullable final ReadPreference readPreference, final Decoder commandResultDecoder,
- final BindingContext context) {
- return wrapped.command(database, command, fieldNameValidator, readPreference, commandResultDecoder, context);
+ final OperationContext operationContext) {
+ return wrapped.command(database, command, fieldNameValidator, readPreference, commandResultDecoder, operationContext);
}
@Override
public T command(final String database, final BsonDocument command, final FieldNameValidator commandFieldNameValidator,
@Nullable final ReadPreference readPreference, final Decoder commandResultDecoder,
- final BindingContext context, final boolean responseExpected,
+ final OperationContext operationContext, final boolean responseExpected,
@Nullable final SplittablePayload payload, @Nullable final FieldNameValidator payloadFieldNameValidator) {
- return wrapped.command(database, command, commandFieldNameValidator, readPreference, commandResultDecoder, context,
+ return wrapped.command(database, command, commandFieldNameValidator, readPreference, commandResultDecoder, operationContext,
responseExpected, payload, payloadFieldNameValidator);
}
@@ -356,19 +355,19 @@ public ConnectionDescription getDescription() {
@Override
public void commandAsync(final String database, final BsonDocument command, final FieldNameValidator fieldNameValidator,
- @Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final BindingContext context,
- final SingleResultCallback callback) {
+ @Nullable final ReadPreference readPreference, final Decoder commandResultDecoder,
+ final OperationContext operationContext, final SingleResultCallback callback) {
wrapped.commandAsync(database, command, fieldNameValidator, readPreference, commandResultDecoder,
- context, callback);
+ operationContext, callback);
}
@Override
public void commandAsync(final String database, final BsonDocument command, final FieldNameValidator commandFieldNameValidator,
- @Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final BindingContext context,
- final boolean responseExpected, @Nullable final SplittablePayload payload,
+ @Nullable final ReadPreference readPreference, final Decoder commandResultDecoder,
+ final OperationContext operationContext, final boolean responseExpected, @Nullable final SplittablePayload payload,
@Nullable final FieldNameValidator payloadFieldNameValidator, final SingleResultCallback callback) {
wrapped.commandAsync(database, command, commandFieldNameValidator, readPreference, commandResultDecoder,
- context, responseExpected, payload, payloadFieldNameValidator, callback);
+ operationContext, responseExpected, payload, payloadFieldNameValidator, callback);
}
@Override
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultServerConnection.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultServerConnection.java
index 3b053490464..01d5f587fdc 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultServerConnection.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultServerConnection.java
@@ -20,7 +20,6 @@
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ConnectionDescription;
import com.mongodb.internal.async.SingleResultCallback;
-import com.mongodb.internal.binding.BindingContext;
import com.mongodb.internal.diagnostics.logging.Logger;
import com.mongodb.internal.diagnostics.logging.Loggers;
import com.mongodb.internal.session.SessionContext;
@@ -70,39 +69,38 @@ public ConnectionDescription getDescription() {
@Nullable
@Override
public T command(final String database, final BsonDocument command, final FieldNameValidator fieldNameValidator,
- @Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final BindingContext context) {
- return command(database, command, fieldNameValidator, readPreference, commandResultDecoder, context, true, null, null);
+ @Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final OperationContext operationContext) {
+ return command(database, command, fieldNameValidator, readPreference, commandResultDecoder, operationContext, true, null, null);
}
@Nullable
@Override
public T command(final String database, final BsonDocument command, final FieldNameValidator commandFieldNameValidator,
@Nullable final ReadPreference readPreference, final Decoder commandResultDecoder,
- final BindingContext context, final boolean responseExpected,
+ final OperationContext operationContext, final boolean responseExpected,
@Nullable final SplittablePayload payload, @Nullable final FieldNameValidator payloadFieldNameValidator) {
- return executeProtocol(new CommandProtocolImpl<>(database, command, commandFieldNameValidator, readPreference,
- commandResultDecoder, responseExpected, payload, payloadFieldNameValidator, clusterConnectionMode,
- context.getServerApi(), context.getRequestContext(), context.getOperationContext()),
- context.getSessionContext());
+ return executeProtocol(
+ new CommandProtocolImpl<>(database, command, commandFieldNameValidator, readPreference, commandResultDecoder,
+ responseExpected, payload, payloadFieldNameValidator, clusterConnectionMode, operationContext),
+ operationContext.getSessionContext());
}
@Override
public void commandAsync(final String database, final BsonDocument command, final FieldNameValidator fieldNameValidator,
- @Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final BindingContext context,
+ @Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final OperationContext operationContext,
final SingleResultCallback callback) {
commandAsync(database, command, fieldNameValidator, readPreference, commandResultDecoder,
- context, true, null, null, callback);
+ operationContext, true, null, null, callback);
}
@Override
public void commandAsync(final String database, final BsonDocument command, final FieldNameValidator commandFieldNameValidator,
- @Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final BindingContext context,
+ @Nullable final ReadPreference readPreference, final Decoder commandResultDecoder, final OperationContext operationContext,
final boolean responseExpected, @Nullable final SplittablePayload payload,
@Nullable final FieldNameValidator payloadFieldNameValidator, final SingleResultCallback callback) {
executeProtocolAsync(new CommandProtocolImpl<>(database, command, commandFieldNameValidator, readPreference,
- commandResultDecoder, responseExpected, payload, payloadFieldNameValidator, clusterConnectionMode,
- context.getServerApi(), context.getRequestContext(), context.getOperationContext()),
- context.getSessionContext(), callback);
+ commandResultDecoder, responseExpected, payload, payloadFieldNameValidator, clusterConnectionMode, operationContext),
+ operationContext.getSessionContext(), callback);
}
@Override
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultServerMonitor.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultServerMonitor.java
index 55030a6db34..656c9bc7779 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultServerMonitor.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultServerMonitor.java
@@ -29,10 +29,10 @@
import com.mongodb.event.ServerHeartbeatStartedEvent;
import com.mongodb.event.ServerHeartbeatSucceededEvent;
import com.mongodb.event.ServerMonitorListener;
+import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.diagnostics.logging.Logger;
import com.mongodb.internal.diagnostics.logging.Loggers;
import com.mongodb.internal.inject.Provider;
-import com.mongodb.internal.session.SessionContext;
import com.mongodb.internal.validator.NoOpFieldNameValidator;
import com.mongodb.lang.Nullable;
import org.bson.BsonBoolean;
@@ -73,6 +73,7 @@ class DefaultServerMonitor implements ServerMonitor {
private final ServerId serverId;
private final ServerMonitorListener serverMonitorListener;
private final Provider sdamProvider;
+ private final InternalOperationContextFactory operationContextFactory;
private final InternalConnectionFactory internalConnectionFactory;
private final ClusterConnectionMode clusterConnectionMode;
@Nullable
@@ -85,22 +86,24 @@ class DefaultServerMonitor implements ServerMonitor {
*/
@Nullable
private RoundTripTimeMonitor roundTripTimeMonitor;
- private final ExponentiallyWeightedMovingAverage averageRoundTripTime = new ExponentiallyWeightedMovingAverage(0.2);
+ private final RoundTripTimeSampler roundTripTimeSampler = new RoundTripTimeSampler();
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private volatile boolean isClosed;
DefaultServerMonitor(final ServerId serverId, final ServerSettings serverSettings,
final InternalConnectionFactory internalConnectionFactory,
- final ClusterConnectionMode clusterConnectionMode,
- @Nullable final ServerApi serverApi,
- final boolean isFunctionAsAServiceEnvironment,
- final Provider sdamProvider) {
+ final ClusterConnectionMode clusterConnectionMode,
+ @Nullable final ServerApi serverApi,
+ final boolean isFunctionAsAServiceEnvironment,
+ final Provider sdamProvider,
+ final InternalOperationContextFactory operationContextFactory) {
this.serverSettings = notNull("serverSettings", serverSettings);
this.serverId = notNull("serverId", serverId);
this.serverMonitorListener = singleServerMonitorListener(serverSettings);
this.internalConnectionFactory = notNull("internalConnectionFactory", internalConnectionFactory);
this.clusterConnectionMode = notNull("clusterConnectionMode", clusterConnectionMode);
+ this.operationContextFactory = assertNotNull(operationContextFactory);
this.serverApi = serverApi;
this.isFunctionAsAServiceEnvironment = isFunctionAsAServiceEnvironment;
this.sdamProvider = sdamProvider;
@@ -135,7 +138,7 @@ public void close() {
isClosed = true;
//noinspection EmptyTryBlock
try (ServerMonitor ignoredAutoClosed = monitor;
- RoundTripTimeMonitor ignoredAutoClose2 = roundTripTimeMonitor) {
+ RoundTripTimeMonitor ignoredAutoClose2 = roundTripTimeMonitor) {
// we are automatically closing resources here
}
});
@@ -213,9 +216,9 @@ private ServerDescription lookupServerDescription(final ServerDescription curren
if (connection == null || connection.isClosed()) {
currentCheckCancelled = false;
InternalConnection newConnection = internalConnectionFactory.create(serverId);
- newConnection.open();
+ newConnection.open(operationContextFactory.create());
connection = newConnection;
- averageRoundTripTime.addSample(connection.getInitialServerDescription().getRoundTripTimeNanos());
+ roundTripTimeSampler.addSample(connection.getInitialServerDescription().getRoundTripTimeNanos());
return connection.getInitialServerDescription();
}
@@ -228,7 +231,7 @@ private ServerDescription lookupServerDescription(final ServerDescription curren
long start = System.nanoTime();
try {
- SessionContext sessionContext = NoOpSessionContext.INSTANCE;
+ OperationContext operationContext = operationContextFactory.create();
if (!connection.hasMoreToCome()) {
BsonDocument helloDocument = new BsonDocument(getHandshakeCommandName(currentServerDescription), new BsonInt32(1))
.append("helloOk", BsonBoolean.TRUE);
@@ -238,26 +241,26 @@ private ServerDescription lookupServerDescription(final ServerDescription curren
}
connection.send(createCommandMessage(helloDocument, connection, currentServerDescription), new BsonDocumentCodec(),
- sessionContext);
+ operationContext);
}
BsonDocument helloResult;
if (shouldStreamResponses) {
- helloResult = connection.receive(new BsonDocumentCodec(), sessionContext,
- Math.toIntExact(serverSettings.getHeartbeatFrequency(MILLISECONDS)));
+ helloResult = connection.receive(new BsonDocumentCodec(), operationContextWithAdditionalTimeout(operationContext));
} else {
- helloResult = connection.receive(new BsonDocumentCodec(), sessionContext);
+ helloResult = connection.receive(new BsonDocumentCodec(), operationContext);
}
long elapsedTimeNanos = System.nanoTime() - start;
if (!shouldStreamResponses) {
- averageRoundTripTime.addSample(elapsedTimeNanos);
+ roundTripTimeSampler.addSample(elapsedTimeNanos);
}
serverMonitorListener.serverHeartbeatSucceeded(
new ServerHeartbeatSucceededEvent(connection.getDescription().getConnectionId(), helloResult,
elapsedTimeNanos, shouldStreamResponses));
- return createServerDescription(serverId.getAddress(), helloResult, averageRoundTripTime.getAverage());
+ return createServerDescription(serverId.getAddress(), helloResult, roundTripTimeSampler.getAverage(),
+ roundTripTimeSampler.getMin());
} catch (Exception e) {
serverMonitorListener.serverHeartbeatFailed(
new ServerHeartbeatFailedEvent(connection.getDescription().getConnectionId(), System.nanoTime() - start,
@@ -265,7 +268,7 @@ private ServerDescription lookupServerDescription(final ServerDescription curren
throw e;
}
} catch (Throwable t) {
- averageRoundTripTime.reset();
+ roundTripTimeSampler.reset();
InternalConnection localConnection = withLock(lock, () -> {
InternalConnection result = connection;
connection = null;
@@ -278,6 +281,12 @@ private ServerDescription lookupServerDescription(final ServerDescription curren
}
}
+ private OperationContext operationContextWithAdditionalTimeout(final OperationContext originalOperationContext) {
+ TimeoutContext newTimeoutContext = originalOperationContext.getTimeoutContext()
+ .withAdditionalReadTimeout(Math.toIntExact(serverSettings.getHeartbeatFrequency(MILLISECONDS)));
+ return originalOperationContext.withTimeoutContext(newTimeoutContext);
+ }
+
private boolean shouldStreamResponses(final ServerDescription currentServerDescription) {
boolean serverSupportsStreaming = currentServerDescription.getTopologyVersion() != null;
switch (serverSettings.getServerMonitoringMode()) {
@@ -297,7 +306,7 @@ private boolean shouldStreamResponses(final ServerDescription currentServerDescr
}
private CommandMessage createCommandMessage(final BsonDocument command, final InternalConnection connection,
- final ServerDescription currentServerDescription) {
+ final ServerDescription currentServerDescription) {
return new CommandMessage(new MongoNamespace("admin", COMMAND_COLLECTION_NAME), command,
new NoOpFieldNameValidator(), primary(),
MessageSettings.builder()
@@ -307,7 +316,7 @@ private CommandMessage createCommandMessage(final BsonDocument command, final In
}
private void logStateChange(final ServerDescription previousServerDescription,
- final ServerDescription currentServerDescription) {
+ final ServerDescription currentServerDescription) {
if (shouldLogStageChange(previousServerDescription, currentServerDescription)) {
if (currentServerDescription.getException() != null) {
LOGGER.info(format("Exception in monitor thread while connecting to server %s", serverId.getAddress()),
@@ -395,12 +404,12 @@ static boolean shouldLogStageChange(final ServerDescription previous, final Serv
}
ObjectId previousElectionId = previous.getElectionId();
if (previousElectionId != null
- ? !previousElectionId.equals(current.getElectionId()) : current.getElectionId() != null) {
+ ? !previousElectionId.equals(current.getElectionId()) : current.getElectionId() != null) {
return true;
}
Integer setVersion = previous.getSetVersion();
if (setVersion != null
- ? !setVersion.equals(current.getSetVersion()) : current.getSetVersion() != null) {
+ ? !setVersion.equals(current.getSetVersion()) : current.getSetVersion() != null) {
return true;
}
@@ -470,17 +479,18 @@ public void run() {
private void initialize() {
connection = null;
connection = internalConnectionFactory.create(serverId);
- connection.open();
- averageRoundTripTime.addSample(connection.getInitialServerDescription().getRoundTripTimeNanos());
+ connection.open(operationContextFactory.create());
+ roundTripTimeSampler.addSample(connection.getInitialServerDescription().getRoundTripTimeNanos());
}
private void pingServer(final InternalConnection connection) {
long start = System.nanoTime();
+ OperationContext operationContext = operationContextFactory.create();
executeCommand("admin",
new BsonDocument(getHandshakeCommandName(connection.getInitialServerDescription()), new BsonInt32(1)),
- clusterConnectionMode, serverApi, connection);
+ clusterConnectionMode, serverApi, connection, operationContext);
long elapsedTimeNanos = System.nanoTime() - start;
- averageRoundTripTime.addSample(elapsedTimeNanos);
+ roundTripTimeSampler.addSample(elapsedTimeNanos);
}
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DescriptionHelper.java b/driver-core/src/main/com/mongodb/internal/connection/DescriptionHelper.java
index e220d88bb31..26f73bcee9c 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DescriptionHelper.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DescriptionHelper.java
@@ -87,11 +87,12 @@ static ConnectionDescription createConnectionDescription(final ClusterConnection
}
public static ServerDescription createServerDescription(final ServerAddress serverAddress, final BsonDocument helloResult,
- final long roundTripTime) {
+ final long roundTripTime, final long minRoundTripTime) {
return ServerDescription.builder()
.state(CONNECTED)
.address(serverAddress)
.type(getServerType(helloResult))
+ .cryptd(helloResult.getBoolean("iscryptd", BsonBoolean.FALSE).getValue())
.canonicalAddress(helloResult.containsKey("me") ? helloResult.getString("me").getValue() : null)
.hosts(listToSet(helloResult.getArray("hosts", new BsonArray())))
.passives(listToSet(helloResult.getArray("passives", new BsonArray())))
@@ -107,6 +108,7 @@ public static ServerDescription createServerDescription(final ServerAddress serv
.topologyVersion(getTopologyVersion(helloResult))
.lastWriteDate(getLastWriteDate(helloResult))
.roundTripTime(roundTripTime, NANOSECONDS)
+ .minRoundTripTime(minRoundTripTime, NANOSECONDS)
.logicalSessionTimeoutMinutes(getLogicalSessionTimeoutMinutes(helloResult))
.helloOk(helloResult.getBoolean("helloOk", BsonBoolean.FALSE).getValue())
.ok(CommandHelper.isCommandOk(helloResult)).build();
diff --git a/driver-core/src/main/com/mongodb/internal/connection/ExtendedAsynchronousByteChannel.java b/driver-core/src/main/com/mongodb/internal/connection/ExtendedAsynchronousByteChannel.java
index 3831d2bfa35..ed5e55b822a 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/ExtendedAsynchronousByteChannel.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/ExtendedAsynchronousByteChannel.java
@@ -171,7 +171,7 @@ void read(
void write(
ByteBuffer src,
long timeout, TimeUnit unit,
- A attach, CompletionHandler handler);
+ @Nullable A attach, CompletionHandler handler);
/**
* Writes a sequence of bytes to this channel from a subsequence of the given
@@ -233,5 +233,5 @@ void write(
void write(
ByteBuffer[] srcs, int offset, int length,
long timeout, TimeUnit unit,
- A attach, CompletionHandler handler);
+ @Nullable A attach, CompletionHandler handler);
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalConnection.java b/driver-core/src/main/com/mongodb/internal/connection/InternalConnection.java
index e2b0188572e..792c33570b7 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/InternalConnection.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/InternalConnection.java
@@ -16,11 +16,9 @@
package com.mongodb.internal.connection;
-import com.mongodb.RequestContext;
import com.mongodb.connection.ConnectionDescription;
import com.mongodb.connection.ServerDescription;
import com.mongodb.internal.async.SingleResultCallback;
-import com.mongodb.internal.session.SessionContext;
import com.mongodb.lang.Nullable;
import org.bson.ByteBuf;
import org.bson.codecs.Decoder;
@@ -50,15 +48,18 @@ public interface InternalConnection extends BufferProvider {
/**
* Opens the connection so its ready for use. Will perform a handshake.
+ *
+ * @param operationContext the operation context
*/
- void open();
+ void open(OperationContext operationContext);
/**
* Opens the connection so its ready for use
*
- * @param callback the callback to be called once the connection has been opened
+ * @param operationContext the operation context
+ * @param callback the callback to be called once the connection has been opened
*/
- void openAsync(SingleResultCallback callback);
+ void openAsync(OperationContext operationContext, SingleResultCallback callback);
/**
* Closes the connection.
@@ -90,22 +91,14 @@ public interface InternalConnection extends BufferProvider {
* Send a command message to the server.
*
* @param message the command message to send
- * @param sessionContext the session context
- * @param requestContext the request context
* @param operationContext the operation context
*/
@Nullable
- T sendAndReceive(CommandMessage message, Decoder decoder, SessionContext sessionContext, RequestContext requestContext,
- OperationContext operationContext);
+ T sendAndReceive(CommandMessage message, Decoder decoder, OperationContext operationContext);
- void send(CommandMessage message, Decoder decoder, SessionContext sessionContext);
+ void send(CommandMessage message, Decoder decoder, OperationContext operationContext);
- T receive(Decoder decoder, SessionContext sessionContext);
-
-
- default T receive(Decoder decoder, SessionContext sessionContext, int additionalTimeout) {
- throw new UnsupportedOperationException();
- }
+ T receive(Decoder decoder, OperationContext operationContext);
boolean hasMoreToCome();
@@ -113,45 +106,47 @@ default T receive(Decoder decoder, SessionContext sessionContext, int add
* Send a command message to the server.
*
* @param message the command message to send
- * @param sessionContext the session context
- * @param operationContext the operation context
* @param callback the callback
*/
- void sendAndReceiveAsync(CommandMessage message, Decoder decoder, SessionContext sessionContext, RequestContext requestContext,
- OperationContext operationContext, SingleResultCallback callback);
+ void sendAndReceiveAsync(CommandMessage message, Decoder decoder, OperationContext operationContext, SingleResultCallback callback);
/**
* Send a message to the server. The connection may not make any attempt to validate the integrity of the message.
*
* @param byteBuffers the list of byte buffers to send.
* @param lastRequestId the request id of the last message in byteBuffers
+ * @param operationContext the operation context
*/
- void sendMessage(List byteBuffers, int lastRequestId);
+ void sendMessage(List byteBuffers, int lastRequestId, OperationContext operationContext);
/**
* Receive a response to a sent message from the server.
*
* @param responseTo the request id that this message is a response to
+ * @param operationContext the operation context
* @return the response
*/
- ResponseBuffers receiveMessage(int responseTo);
+ ResponseBuffers receiveMessage(int responseTo, OperationContext operationContext);
/**
* Asynchronously send a message to the server. The connection may not make any attempt to validate the integrity of the message.
*
* @param byteBuffers the list of byte buffers to send
* @param lastRequestId the request id of the last message in byteBuffers
+ * @param operationContext the operation context
* @param callback the callback to invoke on completion
*/
- void sendMessageAsync(List byteBuffers, int lastRequestId, SingleResultCallback callback);
+ void sendMessageAsync(List byteBuffers, int lastRequestId, OperationContext operationContext,
+ SingleResultCallback callback);
/**
* Asynchronously receive a response to a sent message from the server.
*
* @param responseTo the request id that this message is a response to
+ * @param operationContext the operation context
* @param callback the callback to invoke on completion
*/
- void receiveMessageAsync(int responseTo, SingleResultCallback callback);
+ void receiveMessageAsync(int responseTo, OperationContext operationContext, SingleResultCallback callback);
default void markAsPinned(Connection.PinningMode pinningMode) {
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalConnectionInitializer.java b/driver-core/src/main/com/mongodb/internal/connection/InternalConnectionInitializer.java
index 9826f20b69b..077e2c68254 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/InternalConnectionInitializer.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/InternalConnectionInitializer.java
@@ -20,14 +20,19 @@
interface InternalConnectionInitializer {
- InternalConnectionInitializationDescription startHandshake(InternalConnection internalConnection);
+ InternalConnectionInitializationDescription startHandshake(InternalConnection internalConnection,
+ OperationContext operationContext);
InternalConnectionInitializationDescription finishHandshake(InternalConnection internalConnection,
- InternalConnectionInitializationDescription description);
+ InternalConnectionInitializationDescription description,
+ OperationContext operationContext);
void startHandshakeAsync(InternalConnection internalConnection,
+ OperationContext operationContext,
SingleResultCallback callback);
- void finishHandshakeAsync(InternalConnection internalConnection, InternalConnectionInitializationDescription description,
+ void finishHandshakeAsync(InternalConnection internalConnection,
+ InternalConnectionInitializationDescription description,
+ OperationContext operationContext,
SingleResultCallback callback);
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalOperationContextFactory.java b/driver-core/src/main/com/mongodb/internal/connection/InternalOperationContextFactory.java
new file mode 100644
index 00000000000..4653c90050b
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/internal/connection/InternalOperationContextFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.mongodb.internal.connection;
+
+import com.mongodb.ServerApi;
+import com.mongodb.internal.TimeoutContext;
+import com.mongodb.internal.TimeoutSettings;
+import com.mongodb.lang.Nullable;
+
+import static com.mongodb.internal.connection.OperationContext.simpleOperationContext;
+
+public final class InternalOperationContextFactory {
+
+ private final TimeoutSettings timeoutSettings;
+ @Nullable
+ private final ServerApi serverApi;
+
+ public InternalOperationContextFactory(final TimeoutSettings timeoutSettings, @Nullable final ServerApi serverApi) {
+ this.timeoutSettings = timeoutSettings;
+ this.serverApi = serverApi;
+ }
+
+ /**
+ * @return a simple operation context without timeoutMS
+ */
+ OperationContext create() {
+ return simpleOperationContext(timeoutSettings.connectionOnly(), serverApi);
+ }
+
+ /**
+ * @return a simple operation context with timeoutMS if set at the MongoClientSettings level
+ */
+
+ OperationContext createMaintenanceContext() {
+ return create().withTimeoutContext(TimeoutContext.createMaintenanceTimeoutContext(timeoutSettings));
+ }
+}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java
index fc90ce81bef..8c1b273c52b 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java
@@ -23,11 +23,12 @@
import com.mongodb.MongoException;
import com.mongodb.MongoInternalException;
import com.mongodb.MongoInterruptedException;
+import com.mongodb.MongoOperationTimeoutException;
import com.mongodb.MongoSocketClosedException;
import com.mongodb.MongoSocketReadException;
import com.mongodb.MongoSocketReadTimeoutException;
import com.mongodb.MongoSocketWriteException;
-import com.mongodb.RequestContext;
+import com.mongodb.MongoSocketWriteTimeoutException;
import com.mongodb.ServerAddress;
import com.mongodb.annotations.NotThreadSafe;
import com.mongodb.connection.AsyncCompletionHandler;
@@ -41,6 +42,7 @@
import com.mongodb.connection.ServerType;
import com.mongodb.event.CommandListener;
import com.mongodb.internal.ResourceUtil;
+import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.VisibleForTesting;
import com.mongodb.internal.async.AsyncSupplier;
import com.mongodb.internal.async.SingleResultCallback;
@@ -48,6 +50,7 @@
import com.mongodb.internal.diagnostics.logging.Loggers;
import com.mongodb.internal.logging.StructuredLogger;
import com.mongodb.internal.session.SessionContext;
+import com.mongodb.internal.time.Timeout;
import com.mongodb.lang.Nullable;
import org.bson.BsonBinaryReader;
import org.bson.BsonDocument;
@@ -73,6 +76,7 @@
import static com.mongodb.assertions.Assertions.isTrue;
import static com.mongodb.assertions.Assertions.notNull;
import static com.mongodb.internal.async.AsyncRunnable.beginAsync;
+import static com.mongodb.internal.TimeoutContext.createMongoTimeoutException;
import static com.mongodb.internal.async.ErrorHandlingResultCallback.errorHandlingCallback;
import static com.mongodb.internal.connection.Authenticator.shouldAuthenticate;
import static com.mongodb.internal.connection.CommandHelper.HELLO;
@@ -219,16 +223,19 @@ public int getGeneration() {
}
@Override
- public void open() {
+ public void open(final OperationContext originalOperationContext) {
isTrue("Open already called", stream == null);
stream = streamFactory.create(serverId.getAddress());
try {
- stream.open();
+ OperationContext operationContext = originalOperationContext
+ .withTimeoutContext(originalOperationContext.getTimeoutContext().withComputedServerSelectionTimeoutContext());
- InternalConnectionInitializationDescription initializationDescription = connectionInitializer.startHandshake(this);
+ stream.open(operationContext);
+
+ InternalConnectionInitializationDescription initializationDescription = connectionInitializer.startHandshake(this, operationContext);
initAfterHandshakeStart(initializationDescription);
- initializationDescription = connectionInitializer.finishHandshake(this, initializationDescription);
+ initializationDescription = connectionInitializer.finishHandshake(this, initializationDescription, operationContext);
initAfterHandshakeFinish(initializationDescription);
} catch (Throwable t) {
close();
@@ -241,14 +248,18 @@ public void open() {
}
@Override
- public void openAsync(final SingleResultCallback callback) {
+ public void openAsync(final OperationContext originalOperationContext, final SingleResultCallback callback) {
assertNull(stream);
try {
+ OperationContext operationContext = originalOperationContext
+ .withTimeoutContext(originalOperationContext.getTimeoutContext().withComputedServerSelectionTimeoutContext());
+
stream = streamFactory.create(serverId.getAddress());
- stream.openAsync(new AsyncCompletionHandler() {
+ stream.openAsync(operationContext, new AsyncCompletionHandler() {
+
@Override
public void completed(@Nullable final Void aVoid) {
- connectionInitializer.startHandshakeAsync(InternalStreamConnection.this,
+ connectionInitializer.startHandshakeAsync(InternalStreamConnection.this, operationContext,
(initialResult, initialException) -> {
if (initialException != null) {
close();
@@ -257,7 +268,7 @@ public void completed(@Nullable final Void aVoid) {
assertNotNull(initialResult);
initAfterHandshakeStart(initialResult);
connectionInitializer.finishHandshakeAsync(InternalStreamConnection.this,
- initialResult, (completedResult, completedException) -> {
+ initialResult, operationContext, (completedResult, completedException) -> {
if (completedException != null) {
close();
callback.onResult(null, completedException);
@@ -360,46 +371,46 @@ public boolean isClosed() {
@Nullable
@Override
- public T sendAndReceive(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext,
- final RequestContext requestContext, final OperationContext operationContext) {
-
+ public T sendAndReceive(final CommandMessage message, final Decoder decoder, final OperationContext operationContext) {
Supplier sendAndReceiveInternal = () -> sendAndReceiveInternal(
- message, decoder, sessionContext, requestContext, operationContext);
+ message, decoder, operationContext);
try {
return sendAndReceiveInternal.get();
} catch (MongoCommandException e) {
if (reauthenticationIsTriggered(e)) {
- return reauthenticateAndRetry(sendAndReceiveInternal);
+ return reauthenticateAndRetry(sendAndReceiveInternal, operationContext);
}
throw e;
}
}
@Override
- public void sendAndReceiveAsync(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext,
- final RequestContext requestContext, final OperationContext operationContext, final SingleResultCallback callback) {
+ public void sendAndReceiveAsync(final CommandMessage message, final Decoder decoder,
+ final OperationContext operationContext,
+ final SingleResultCallback callback) {
AsyncSupplier sendAndReceiveAsyncInternal = c -> sendAndReceiveAsyncInternal(
- message, decoder, sessionContext, requestContext, operationContext, c);
+ message, decoder, operationContext, c);
beginAsync().thenSupply(c -> {
sendAndReceiveAsyncInternal.getAsync(c);
}).onErrorIf(e -> reauthenticationIsTriggered(e), (t, c) -> {
- reauthenticateAndRetryAsync(sendAndReceiveAsyncInternal, c);
+ reauthenticateAndRetryAsync(sendAndReceiveAsyncInternal, operationContext, c);
}).finish(callback);
}
- private T reauthenticateAndRetry(final Supplier operation) {
+ private T reauthenticateAndRetry(final Supplier operation, final OperationContext operationContext) {
authenticated.set(false);
- assertNotNull(authenticator).reauthenticate(this);
+ assertNotNull(authenticator).reauthenticate(this, operationContext);
authenticated.set(true);
return operation.get();
}
private void reauthenticateAndRetryAsync(final AsyncSupplier operation,
+ final OperationContext operationContext,
final SingleResultCallback callback) {
beginAsync().thenRun(c -> {
authenticated.set(false);
- assertNotNull(authenticator).reauthenticateAsync(this, c);
+ assertNotNull(authenticator).reauthenticateAsync(this, operationContext, c);
}).thenSupply((c) -> {
authenticated.set(true);
operation.getAsync(c);
@@ -419,15 +430,14 @@ public boolean reauthenticationIsTriggered(@Nullable final Throwable t) {
@Nullable
private T sendAndReceiveInternal(final CommandMessage message, final Decoder decoder,
- final SessionContext sessionContext, final RequestContext requestContext,
final OperationContext operationContext) {
CommandEventSender commandEventSender;
try (ByteBufferBsonOutput bsonOutput = new ByteBufferBsonOutput(this)) {
- message.encode(bsonOutput, sessionContext);
- commandEventSender = createCommandEventSender(message, bsonOutput, requestContext, operationContext);
+ message.encode(bsonOutput, operationContext);
+ commandEventSender = createCommandEventSender(message, bsonOutput, operationContext);
commandEventSender.sendStartedEvent();
try {
- sendCommandMessage(message, bsonOutput, sessionContext);
+ sendCommandMessage(message, bsonOutput, operationContext);
} catch (Exception e) {
commandEventSender.sendFailedEvent(e);
throw e;
@@ -435,7 +445,7 @@ private T sendAndReceiveInternal(final CommandMessage message, final Decoder
}
if (message.isResponseExpected()) {
- return receiveCommandMessageResponse(decoder, commandEventSender, sessionContext, 0);
+ return receiveCommandMessageResponse(decoder, commandEventSender, operationContext);
} else {
commandEventSender.sendSucceededEventForOneWayCommand();
return null;
@@ -443,10 +453,10 @@ private T sendAndReceiveInternal(final CommandMessage message, final Decoder
}
@Override
- public void send(final CommandMessage message, final Decoder decoder, final SessionContext sessionContext) {
+ public void send(final CommandMessage message, final Decoder decoder, final OperationContext operationContext) {
try (ByteBufferBsonOutput bsonOutput = new ByteBufferBsonOutput(this)) {
- message.encode(bsonOutput, sessionContext);
- sendCommandMessage(message, bsonOutput, sessionContext);
+ message.encode(bsonOutput, operationContext);
+ sendCommandMessage(message, bsonOutput, operationContext);
if (message.isResponseExpected()) {
hasMoreToCome = true;
}
@@ -454,15 +464,9 @@ public void send(final CommandMessage message, final Decoder decoder, fin
}
@Override
- public T receive(final Decoder decoder, final SessionContext sessionContext) {
+ public T receive(final Decoder decoder, final OperationContext operationContext) {
isTrue("Response is expected", hasMoreToCome);
- return receiveCommandMessageResponse(decoder, new NoOpCommandEventSender(), sessionContext, 0);
- }
-
- @Override
- public T receive(final Decoder decoder, final SessionContext sessionContext, final int additionalTimeout) {
- isTrue("Response is expected", hasMoreToCome);
- return receiveCommandMessageResponse(decoder, new NoOpCommandEventSender(), sessionContext, additionalTimeout);
+ return receiveCommandMessageResponse(decoder, new NoOpCommandEventSender(), operationContext);
}
@Override
@@ -470,56 +474,57 @@ public boolean hasMoreToCome() {
return hasMoreToCome;
}
- private void sendCommandMessage(final CommandMessage message,
- final ByteBufferBsonOutput bsonOutput, final SessionContext sessionContext) {
+ private void sendCommandMessage(final CommandMessage message, final ByteBufferBsonOutput bsonOutput,
+ final OperationContext operationContext) {
Compressor localSendCompressor = sendCompressor;
if (localSendCompressor == null || SECURITY_SENSITIVE_COMMANDS.contains(message.getCommandDocument(bsonOutput).getFirstKey())) {
- List byteBuffers = bsonOutput.getByteBuffers();
- try {
- sendMessage(byteBuffers, message.getId());
- } finally {
- ResourceUtil.release(byteBuffers);
- bsonOutput.close();
- }
+ trySendMessage(message, bsonOutput, operationContext);
} else {
ByteBufferBsonOutput compressedBsonOutput;
List byteBuffers = bsonOutput.getByteBuffers();
try {
CompressedMessage compressedMessage = new CompressedMessage(message.getOpCode(), byteBuffers, localSendCompressor,
- getMessageSettings(description));
+ getMessageSettings(description, initialServerDescription));
compressedBsonOutput = new ByteBufferBsonOutput(this);
- compressedMessage.encode(compressedBsonOutput, sessionContext);
+ compressedMessage.encode(compressedBsonOutput, operationContext);
} finally {
ResourceUtil.release(byteBuffers);
bsonOutput.close();
}
- List compressedByteBuffers = compressedBsonOutput.getByteBuffers();
- try {
- sendMessage(compressedByteBuffers, message.getId());
- } finally {
- ResourceUtil.release(compressedByteBuffers);
- compressedBsonOutput.close();
- }
+ trySendMessage(message, compressedBsonOutput, operationContext);
}
responseTo = message.getId();
}
- private T receiveCommandMessageResponse(final Decoder decoder,
- final CommandEventSender commandEventSender, final SessionContext sessionContext,
- final int additionalTimeout) {
+ private void trySendMessage(final CommandMessage message, final ByteBufferBsonOutput bsonOutput,
+ final OperationContext operationContext) {
+ Timeout.onExistsAndExpired(operationContext.getTimeoutContext().timeoutIncludingRoundTrip(), () -> {
+ throw TimeoutContext.createMongoRoundTripTimeoutException();
+ });
+ List byteBuffers = bsonOutput.getByteBuffers();
+ try {
+ sendMessage(byteBuffers, message.getId(), operationContext);
+ } finally {
+ ResourceUtil.release(byteBuffers);
+ bsonOutput.close();
+ }
+ }
+
+ private T receiveCommandMessageResponse(final Decoder decoder, final CommandEventSender commandEventSender,
+ final OperationContext operationContext) {
boolean commandSuccessful = false;
- try (ResponseBuffers responseBuffers = receiveMessageWithAdditionalTimeout(additionalTimeout)) {
- updateSessionContext(sessionContext, responseBuffers);
+ try (ResponseBuffers responseBuffers = receiveResponseBuffers(operationContext)) {
+ updateSessionContext(operationContext.getSessionContext(), responseBuffers);
if (!isCommandOk(responseBuffers)) {
throw getCommandFailureException(responseBuffers.getResponseDocument(responseTo,
- new BsonDocumentCodec()), description.getServerAddress());
+ new BsonDocumentCodec()), description.getServerAddress(), operationContext.getTimeoutContext());
}
commandSuccessful = true;
commandEventSender.sendSucceededEvent(responseBuffers);
- T commandResult = getCommandResult(decoder, responseBuffers, responseTo);
+ T commandResult = getCommandResult(decoder, responseBuffers, responseTo, operationContext.getTimeoutContext());
hasMoreToCome = responseBuffers.getReplyHeader().hasMoreToCome();
if (hasMoreToCome) {
responseTo = responseBuffers.getReplyHeader().getRequestId();
@@ -536,8 +541,8 @@ private T receiveCommandMessageResponse(final Decoder decoder,
}
}
- private void sendAndReceiveAsyncInternal(final CommandMessage message, final Decoder