diff --git a/driver-core/src/main/com/mongodb/ConnectionString.java b/driver-core/src/main/com/mongodb/ConnectionString.java index 659e8fd02aa..36ab59d469f 100644 --- a/driver-core/src/main/com/mongodb/ConnectionString.java +++ b/driver-core/src/main/com/mongodb/ConnectionString.java @@ -17,6 +17,7 @@ package com.mongodb; import com.mongodb.annotations.Alpha; +import com.mongodb.annotations.Beta; import com.mongodb.annotations.Reason; import com.mongodb.connection.ClusterSettings; import com.mongodb.connection.ConnectionPoolSettings; @@ -264,14 +265,17 @@ *
SRV configuration:
*General configuration:
*Gets whether writes should be retried if they fail due to a network error
- * + * Gets whether attempts to execute write commands should be retried if they fail due to a retryable error. + * See {@link MongoClientSettings.Builder#retryWrites(boolean)} for more information. + ** The name of this method differs from others in this class so as not to conflict with the now removed * getRetryWrites() method, which returned a primitive {@code boolean} value, and didn't allow callers to differentiate * between a false value and an unset value. * - * @return the retryWrites value, or null if unset + * @return the {@code retryWrites} value, or {@code null} if unset + * @see #getMaxAdaptiveRetries() * @since 3.9 * @mongodb.server.release 3.6 */ @@ -1471,9 +1485,11 @@ public Boolean getRetryWritesValue() { } /** - *
Gets whether reads should be retried if they fail due to a network error
+ * Gets whether attempts to execute read commands should be retried if they fail due to a retryable error. + * See {@link MongoClientSettings.Builder#retryReads(boolean)} for more information. * - * @return the retryWrites value + * @return the {@code retryReads} value, or {@code null} if unset + * @see #getMaxAdaptiveRetries() * @since 3.11 * @mongodb.server.release 3.6 */ @@ -1482,6 +1498,19 @@ public Boolean getRetryReads() { return retryReads; } + /** + * Gets the maximum number of retry attempts when encountering a retryable overload error. + * See {@link MongoClientSettings.Builder#maxAdaptiveRetries(Integer)} for more information. + * + * @return The {@code maxAdaptiveRetries} value, or {@code null} if unset. + * @since 5.7 + */ + @Beta(Reason.CLIENT) + @Nullable + public Integer getMaxAdaptiveRetries() { + return maxAdaptiveRetries; + } + /** * Gets the minimum connection pool size specified in the connection string. * @return the minimum connection pool size @@ -1795,6 +1824,7 @@ public boolean equals(final Object o) { && Objects.equals(writeConcern, that.writeConcern) && Objects.equals(retryWrites, that.retryWrites) && Objects.equals(retryReads, that.retryReads) + && Objects.equals(maxAdaptiveRetries, that.maxAdaptiveRetries) && Objects.equals(readConcern, that.readConcern) && Objects.equals(minConnectionPoolSize, that.minConnectionPoolSize) && Objects.equals(maxConnectionPoolSize, that.maxConnectionPoolSize) @@ -1826,7 +1856,7 @@ public boolean equals(final Object o) { @Override public int hashCode() { return Objects.hash(credential, isSrvProtocol, hosts, database, collection, directConnection, readPreference, - writeConcern, retryWrites, retryReads, readConcern, minConnectionPoolSize, maxConnectionPoolSize, maxWaitTime, + writeConcern, retryWrites, retryReads, maxAdaptiveRetries, readConcern, minConnectionPoolSize, maxConnectionPoolSize, maxWaitTime, maxConnectionIdleTime, maxConnectionLifeTime, maxConnecting, connectTimeout, timeout, socketTimeout, sslEnabled, sslInvalidHostnameAllowed, requiredReplicaSetName, serverSelectionTimeout, localThreshold, heartbeatFrequency, serverMonitoringMode, applicationName, compressorList, uuidRepresentation, srvServiceName, srvMaxHosts, proxyHost, diff --git a/driver-core/src/main/com/mongodb/MongoClientSettings.java b/driver-core/src/main/com/mongodb/MongoClientSettings.java index 41c5f73a1d7..c1b3c4a069a 100644 --- a/driver-core/src/main/com/mongodb/MongoClientSettings.java +++ b/driver-core/src/main/com/mongodb/MongoClientSettings.java @@ -17,6 +17,7 @@ package com.mongodb; import com.mongodb.annotations.Alpha; +import com.mongodb.annotations.Beta; import com.mongodb.annotations.Immutable; import com.mongodb.annotations.NotThreadSafe; import com.mongodb.annotations.Reason; @@ -93,6 +94,8 @@ public final class MongoClientSettings { private final WriteConcern writeConcern; private final boolean retryWrites; private final boolean retryReads; + @Nullable + private final Integer maxAdaptiveRetries; private final ReadConcern readConcern; private final MongoCredential credential; private final TransportSettings transportSettings; @@ -214,6 +217,8 @@ public static final class Builder { private WriteConcern writeConcern = WriteConcern.ACKNOWLEDGED; private boolean retryWrites = true; private boolean retryReads = true; + @Nullable + private Integer maxAdaptiveRetries; private ReadConcern readConcern = ReadConcern.DEFAULT; private CodecRegistry codecRegistry = MongoClientSettings.getDefaultCodecRegistry(); private TransportSettings transportSettings; @@ -255,6 +260,7 @@ private Builder(final MongoClientSettings settings) { writeConcern = settings.getWriteConcern(); retryWrites = settings.getRetryWrites(); retryReads = settings.getRetryReads(); + maxAdaptiveRetries = settings.getMaxAdaptiveRetries(); readConcern = settings.getReadConcern(); credential = settings.getCredential(); uuidRepresentation = settings.getUuidRepresentation(); @@ -314,6 +320,9 @@ public Builder applyConnectionString(final ConnectionString connectionString) { if (retryReadsValue != null) { retryReads = retryReadsValue; } + if (connectionString.getMaxAdaptiveRetries() != null) { + maxAdaptiveRetries = connectionString.getMaxAdaptiveRetries(); + } if (connectionString.getUuidRepresentation() != null) { uuidRepresentation = connectionString.getUuidRepresentation(); } @@ -428,13 +437,24 @@ public Builder writeConcern(final WriteConcern writeConcern) { } /** - * Sets whether writes should be retried if they fail due to a network error. + * Sets whether attempts to execute write commands should be retried if they fail due to a retryable error. + *+ * The errors {@linkplain MongoException#hasErrorLabel(String) having} + * the {@value MongoException#RETRYABLE_ERROR_LABEL} label are not the only ones considered retryable here: + * unlike applications, which may retry operations, the driver retries commands, which gives it more control + * and allows it to safely retry attempts failed due to a broader set of errors + * than what applications may {@linkplain MongoException#RETRYABLE_ERROR_LABEL safely retry}. + *
+ * For more information on how transactions affect retries, + * see the documentation of the {@value MongoException#TRANSIENT_TRANSACTION_ERROR_LABEL}, + * {@value MongoException#UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL} error labels. * *
Starting with the 3.11.0 release, the default value is true
* - * @param retryWrites sets if writes should be retried if they fail due to a network error. + * @param retryWrites sets if write commands should be retried if they fail due to a retryable error. * @return this * @see #getRetryWrites() + * @see #maxAdaptiveRetries(Integer) * @mongodb.server.release 3.6 */ public Builder retryWrites(final boolean retryWrites) { @@ -443,11 +463,24 @@ public Builder retryWrites(final boolean retryWrites) { } /** - * Sets whether reads should be retried if they fail due to a network error. + * Sets whether attempts to execute read commands should be retried if they fail due to a retryable error. + *+ * The errors {@linkplain MongoException#hasErrorLabel(String) having} + * the {@value MongoException#RETRYABLE_ERROR_LABEL} label are not the only ones considered retryable here: + * unlike applications, which may retry operations, the driver retries commands, which gives it more control + * and allows it to safely retry attempts failed due to a broader set of errors + * than what applications may {@linkplain MongoException#RETRYABLE_ERROR_LABEL safely retry}. + *
+ * For more information on how transactions affect retries, + * see the documentation of the {@value MongoException#TRANSIENT_TRANSACTION_ERROR_LABEL}, + * {@value MongoException#UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL} error labels. + *
+ * Default is {@code true}. * - * @param retryReads sets if reads should be retried if they fail due to a network error. + * @param retryReads sets if read commands should be retried if they fail due to a retryable error. * @return this * @see #getRetryReads() + * @see #maxAdaptiveRetries(Integer) * @since 3.11 * @mongodb.server.release 3.6 */ @@ -456,6 +489,76 @@ public Builder retryReads(final boolean retryReads) { return this; } + /** + * Sets the maximum number of retry attempts when executing a command and encountering + * an error {@linkplain MongoException#hasErrorLabel(String) having} + * the {@value MongoException#SYSTEM_OVERLOADED_ERROR_LABEL} and {@value MongoException#RETRYABLE_ERROR_LABEL} labels. + * Such errors are referred to as retryable overload errors. + *
+ * Default is {@code null}, implies the value 2 and the above retry behavior. The implied value and behavior may change in + * the future in a minor version. + * This means, there is no guarantee that not setting a value is equivalent to setting the value 2. + * The value 0 results in not retrying the attempts failed due to retryable overload errors. + * + *
| Command kind | + *Interaction | + *
|---|---|
| write | + *+ * The attempts failed due to retryable overload errors are retried only if + * {@link #retryWrites(boolean)} is {@code true}. + * | + *
| read | + *
+ * The attempts failed due to retryable overload errors are retried only if
+ * {@link #retryReads(boolean)} is {@code true}.
+ * + * Executing a write operation, for example, {@code MongoCluster.bulkWrite}, + * may involve executing not only write commands, but also read commands. In such a situation, + * just like in other situations, the behavior related to retries depends on + * the known kind of command, not on the kind of operation. + * |
+ *
| unknown | + *
+ * The attempts failed due to retryable overload errors are retried only if
+ * {@link #retryWrites(boolean)} is {@code true} and {@link #retryReads(boolean)} is {@code true}.
+ * + * The command kind is unknown when a command is executed via the {@code MongoDatabase.runCommand} operation. + * |
+ *
Starting with the 3.11.0 release, the default value is true
* * @return the retryWrites value + * @see Builder#retryWrites(boolean) + * @see #getMaxAdaptiveRetries() * @mongodb.server.release 3.6 */ public boolean getRetryWrites() { @@ -797,9 +903,14 @@ public boolean getRetryWrites() { } /** - * Returns true if reads should be retried if they fail due to a network error or other retryable error. The default value is true. + * Returns whether attempts to execute read commands should be retried if they fail due to a retryable error. + * See {@link Builder#retryReads(boolean)} for more information. + *+ * Default is {@code true}. * * @return the retryReads value + * @see Builder#retryReads(boolean) + * @see #getMaxAdaptiveRetries() * @since 3.11 * @mongodb.server.release 3.6 */ @@ -807,6 +918,21 @@ public boolean getRetryReads() { return retryReads; } + /** + * Returns the maximum number of retry attempts when encountering a retryable overload error. + * See {@link Builder#maxAdaptiveRetries(Integer)} for more information. + * + * @return The maximum number of retry attempts when encountering a retryable overload error. + * @see Builder#maxAdaptiveRetries(Integer) + * @since 5.7 + */ + @Beta(Reason.CLIENT) + @Nullable + // TODO-BACKPRESSURE Valentin Use the `maxAdaptiveRetries` setting when retrying. + public Integer getMaxAdaptiveRetries() { + return maxAdaptiveRetries; + } + /** * The read concern to use. * @@ -819,7 +945,7 @@ public ReadConcern getReadConcern() { } /** - * The codec registry to use, or null if not set. + * The codec registry to use. * * @return the codec registry */ @@ -1080,6 +1206,7 @@ public boolean equals(final Object o) { MongoClientSettings that = (MongoClientSettings) o; return retryWrites == that.retryWrites && retryReads == that.retryReads + && Objects.equals(maxAdaptiveRetries, that.maxAdaptiveRetries) && heartbeatSocketTimeoutSetExplicitly == that.heartbeatSocketTimeoutSetExplicitly && heartbeatConnectTimeoutSetExplicitly == that.heartbeatConnectTimeoutSetExplicitly && Objects.equals(readPreference, that.readPreference) @@ -1109,7 +1236,7 @@ public boolean equals(final Object o) { @Override public int hashCode() { - return Objects.hash(readPreference, writeConcern, retryWrites, retryReads, readConcern, credential, transportSettings, + return Objects.hash(readPreference, writeConcern, retryWrites, retryReads, maxAdaptiveRetries, readConcern, credential, transportSettings, commandListeners, codecRegistry, loggerSettings, clusterSettings, socketSettings, heartbeatSocketSettings, connectionPoolSettings, serverSettings, sslSettings, applicationName, compressorList, uuidRepresentation, serverApi, autoEncryptionSettings, heartbeatSocketTimeoutSetExplicitly, @@ -1124,6 +1251,7 @@ public String toString() { + ", writeConcern=" + writeConcern + ", retryWrites=" + retryWrites + ", retryReads=" + retryReads + + ", maxAdaptiveRetries=" + maxAdaptiveRetries + ", readConcern=" + readConcern + ", credential=" + credential + ", transportSettings=" + transportSettings @@ -1153,6 +1281,7 @@ private MongoClientSettings(final Builder builder) { readPreference = builder.readPreference; writeConcern = builder.writeConcern; retryWrites = builder.retryWrites; + maxAdaptiveRetries = builder.maxAdaptiveRetries; retryReads = builder.retryReads; readConcern = builder.readConcern; credential = builder.credential; diff --git a/driver-core/src/main/com/mongodb/MongoException.java b/driver-core/src/main/com/mongodb/MongoException.java index 2c585c93cf4..b15023b2374 100644 --- a/driver-core/src/main/com/mongodb/MongoException.java +++ b/driver-core/src/main/com/mongodb/MongoException.java @@ -36,42 +36,52 @@ public class MongoException extends RuntimeException { /** * An error label indicating that the exception can be treated as a transient transaction error. + * See the documentation linked below for more information. * * @see #hasErrorLabel(String) + * @mongodb.driver.manual core/transactions-in-applications/#std-label-transient-transaction-error TransientTransactionError * @since 3.8 - * @mongodb.driver.manual core/transactions-in-applications/#std-label-transient-transaction-error */ public static final String TRANSIENT_TRANSACTION_ERROR_LABEL = "TransientTransactionError"; /** * An error label indicating that the exception can be treated as an unknown transaction commit result. + * See the documentation linked below for more information. * * @see #hasErrorLabel(String) + * @mongodb.driver.manual core/transactions-in-applications/#std-label-unknown-transaction-commit-result UnknownTransactionCommitResult * @since 3.8 - * @mongodb.driver.manual core/transactions-in-applications/#std-label-unknown-transaction-commit-result */ public static final String UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL = "UnknownTransactionCommitResult"; /** * Server is overloaded and shedding load. - * If you retry, use exponential backoff because the server has indicated overload. - * This label on its own does not mean that the operation can be safely retried. + * If an application retries explicitly, it should use exponential backoff because the server has indicated overload. + * This label on its own does not mean that the operation can be {@linkplain #RETRYABLE_ERROR_LABEL safely retried}. * * @see #hasErrorLabel(String) + * @see MongoClientSettings.Builder#maxAdaptiveRetries(Integer) + * @mongodb.atlas.manual overload-errors/ Overload errors * @since 5.7 * @mongodb.server.release 8.3 */ - // TODO-BACKPRESSURE Valentin Add a @mongodb.driver.manual link or something similar, see `content/atlas/source/overload-errors.txt` in https://github.com/10gen/docs-mongodb-internal/pull/17281 public static final String SYSTEM_OVERLOADED_ERROR_LABEL = "SystemOverloadedError"; /** - * The operation was not executed and is safe to retry. + * The operation is safe to retry, that is, + * retry without rereading the relevant data or considering the semantics of the operation. + *
+ * For more information on how transactions affect retries,
+ * see the documentation of the {@value #TRANSIENT_TRANSACTION_ERROR_LABEL}, {@value #UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL}
+ * error labels.
*
* @see #hasErrorLabel(String)
+ * @see MongoClientSettings.Builder#retryWrites(boolean)
+ * @see MongoClientSettings.Builder#retryReads(boolean)
+ * @mongodb.atlas.manual overload-errors/ Overload errors
* @since 5.7
* @mongodb.server.release 8.3
*/
- // TODO-BACKPRESSURE Valentin Add a @mongodb.driver.manual link or something similar, see `content/atlas/source/overload-errors.txt` in https://github.com/10gen/docs-mongodb-internal/pull/17281
public static final String RETRYABLE_ERROR_LABEL = "RetryableError";
private static final long serialVersionUID = -4415279469780082174L;
diff --git a/driver-core/src/test/unit/com/mongodb/AbstractConnectionStringTest.java b/driver-core/src/test/unit/com/mongodb/AbstractConnectionStringTest.java
index d511d2750eb..8aa0b7d5a9e 100644
--- a/driver-core/src/test/unit/com/mongodb/AbstractConnectionStringTest.java
+++ b/driver-core/src/test/unit/com/mongodb/AbstractConnectionStringTest.java
@@ -122,6 +122,9 @@ protected void testValidOptions() {
} else if (option.getKey().equalsIgnoreCase("retrywrites")) {
boolean expected = option.getValue().asBoolean().getValue();
assertEquals(expected, connectionString.getRetryWritesValue().booleanValue());
+ } else if (option.getKey().equalsIgnoreCase("maxadaptiveretries")) {
+ int expected = option.getValue().asInt32().getValue();
+ assertEquals(expected, connectionString.getMaxAdaptiveRetries().intValue());
} else if (option.getKey().equalsIgnoreCase("replicaset")) {
String expected = option.getValue().asString().getValue();
assertEquals(expected, connectionString.getRequiredReplicaSetName());
diff --git a/driver-core/src/test/unit/com/mongodb/ConnectionStringUnitTest.java b/driver-core/src/test/unit/com/mongodb/ConnectionStringUnitTest.java
index 0b3dd1a0814..c40ea5a0ab6 100644
--- a/driver-core/src/test/unit/com/mongodb/ConnectionStringUnitTest.java
+++ b/driver-core/src/test/unit/com/mongodb/ConnectionStringUnitTest.java
@@ -37,7 +37,30 @@ final class ConnectionStringUnitTest {
@Test
void defaults() {
ConnectionString connectionStringDefault = new ConnectionString(DEFAULT_OPTIONS);
- assertAll(() -> assertNull(connectionStringDefault.getServerMonitoringMode()));
+ assertAll(
+ () -> assertNull(connectionStringDefault.getServerMonitoringMode()),
+ () -> assertNull(connectionStringDefault.getMaxAdaptiveRetries())
+ );
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {
+ "serverMonitoringMode=stream",
+ "maxAdaptiveRetries=42"
+ })
+ void equalAndHashCode(final String connectionStringOptions) {
+ ConnectionString default1 = new ConnectionString(DEFAULT_OPTIONS);
+ ConnectionString default2 = new ConnectionString(DEFAULT_OPTIONS);
+ String connectionString = DEFAULT_OPTIONS + connectionStringOptions;
+ ConnectionString actual1 = new ConnectionString(connectionString);
+ ConnectionString actual2 = new ConnectionString(connectionString);
+ assertAll(
+ () -> assertEquals(default1, default2),
+ () -> assertEquals(default1.hashCode(), default2.hashCode()),
+ () -> assertEquals(actual1, actual2),
+ () -> assertEquals(actual1.hashCode(), actual2.hashCode()),
+ () -> assertNotEquals(default1, actual1)
+ );
}
@Test
@@ -68,22 +91,6 @@ private static String encode(final String string) {
}
}
- @ParameterizedTest
- @ValueSource(strings = {DEFAULT_OPTIONS + "serverMonitoringMode=stream"})
- void equalAndHashCode(final String connectionString) {
- ConnectionString default1 = new ConnectionString(DEFAULT_OPTIONS);
- ConnectionString default2 = new ConnectionString(DEFAULT_OPTIONS);
- ConnectionString actual1 = new ConnectionString(connectionString);
- ConnectionString actual2 = new ConnectionString(connectionString);
- assertAll(
- () -> assertEquals(default1, default2),
- () -> assertEquals(default1.hashCode(), default2.hashCode()),
- () -> assertEquals(actual1, actual2),
- () -> assertEquals(actual1.hashCode(), actual2.hashCode()),
- () -> assertNotEquals(default1, actual1)
- );
- }
-
@Test
void serverMonitoringMode() {
assertAll(
@@ -94,7 +101,6 @@ void serverMonitoringMode() {
);
}
-
@ParameterizedTest
@ValueSource(strings = {"mongodb://foo:bar/@hostname/java?", "mongodb://foo:bar?@hostname/java/",
"mongodb+srv://foo:bar/@hostname/java?", "mongodb+srv://foo:bar?@hostname/java/",
@@ -109,4 +115,18 @@ void unescapedPasswordsShouldNotBeLeakedInExceptionMessages(final String input)
assertFalse(exception.getMessage().contains("bar"));
assertFalse(exception.getMessage().contains("12345678"));
}
+
+ @Test
+ void maxAdaptiveRetries() {
+ assertAll(
+ () -> assertEquals(42,
+ new ConnectionString(DEFAULT_OPTIONS + "maxAdaptiveRetries=42").getMaxAdaptiveRetries()),
+ () -> assertEquals(0,
+ new ConnectionString(DEFAULT_OPTIONS + "maxAdaptiveRetries=0").getMaxAdaptiveRetries()),
+ () -> assertThrows(IllegalArgumentException.class,
+ () -> new ConnectionString(DEFAULT_OPTIONS + "maxAdaptiveRetries=-1")),
+ () -> assertThrows(IllegalArgumentException.class,
+ () -> new ConnectionString(DEFAULT_OPTIONS + "maxAdaptiveRetries=invalid"))
+ );
+ }
}
diff --git a/driver-core/src/test/unit/com/mongodb/MongoClientSettingsSpecification.groovy b/driver-core/src/test/unit/com/mongodb/MongoClientSettingsSpecification.groovy
index c8910751552..d4b59f0cb52 100644
--- a/driver-core/src/test/unit/com/mongodb/MongoClientSettingsSpecification.groovy
+++ b/driver-core/src/test/unit/com/mongodb/MongoClientSettingsSpecification.groovy
@@ -46,6 +46,7 @@ class MongoClientSettingsSpecification extends Specification {
settings.getWriteConcern() == WriteConcern.ACKNOWLEDGED
settings.getRetryWrites()
settings.getRetryReads()
+ settings.getMaxAdaptiveRetries() == null
settings.getReadConcern() == ReadConcern.DEFAULT
settings.getReadPreference() == ReadPreference.primary()
settings.getCommandListeners().isEmpty()
@@ -82,6 +83,11 @@ class MongoClientSettingsSpecification extends Specification {
then:
thrown(IllegalArgumentException)
+ when:
+ builder.maxAdaptiveRetries(-1)
+ then:
+ thrown(IllegalArgumentException)
+
when:
builder.credential(null)
then:
@@ -135,6 +141,7 @@ class MongoClientSettingsSpecification extends Specification {
.writeConcern(WriteConcern.JOURNALED)
.retryWrites(true)
.retryReads(true)
+ .maxAdaptiveRetries(42)
.readConcern(ReadConcern.LOCAL)
.applicationName('app1')
.addCommandListener(commandListener)
@@ -160,6 +167,7 @@ class MongoClientSettingsSpecification extends Specification {
settings.getWriteConcern() == WriteConcern.JOURNALED
settings.getRetryWrites()
settings.getRetryReads()
+ settings.getMaxAdaptiveRetries() == 42
settings.getReadConcern() == ReadConcern.LOCAL
settings.getApplicationName() == 'app1'
settings.getSocketSettings() == SocketSettings.builder().build()
@@ -200,6 +208,7 @@ class MongoClientSettingsSpecification extends Specification {
.writeConcern(WriteConcern.JOURNALED)
.retryWrites(true)
.retryReads(true)
+ .maxAdaptiveRetries(42)
.readConcern(ReadConcern.LOCAL)
.applicationName('app1')
.addCommandListener(commandListener)
@@ -330,6 +339,7 @@ class MongoClientSettingsSpecification extends Specification {
+ '&replicaSet=test'
+ '&retryWrites=true'
+ '&retryReads=true'
+ + '&maxAdaptiveRetries=42'
+ '&ssl=true&sslInvalidHostNameAllowed=true'
+ '&w=majority&wTimeoutMS=2500'
+ '&readPreference=secondary'
@@ -398,6 +408,7 @@ class MongoClientSettingsSpecification extends Specification {
.compressorList([MongoCompressor.createZlibCompressor().withProperty(MongoCompressor.LEVEL, 5)])
.retryWrites(true)
.retryReads(true)
+ .maxAdaptiveRetries(42)
.uuidRepresentation(UuidRepresentation.STANDARD)
.timeout(10000, TimeUnit.MILLISECONDS)
.build()
@@ -462,6 +473,7 @@ class MongoClientSettingsSpecification extends Specification {
.compressorList([MongoCompressor.createZlibCompressor().withProperty(MongoCompressor.LEVEL, 5)])
.retryWrites(true)
.retryReads(true)
+ .maxAdaptiveRetries(null)
def expectedSettings = builder.build()
def settingsWithDefaultConnectionStringApplied = builder
@@ -546,6 +558,18 @@ class MongoClientSettingsSpecification extends Specification {
.build()
}
+ def 'should allow null, 0 maxAdaptiveRetries'() {
+ when:
+ def settings = MongoClientSettings.builder().maxAdaptiveRetries(null).build()
+ then:
+ settings.getMaxAdaptiveRetries() == null
+
+ when:
+ settings = MongoClientSettings.builder().maxAdaptiveRetries(0).build()
+ then:
+ settings.getMaxAdaptiveRetries() == 0
+ }
+
def 'should only have the following fields in the builder'() {
when:
// A regression test so that if anymore fields are added then the builder(final MongoClientSettings settings) should be updated
@@ -553,7 +577,7 @@ class MongoClientSettingsSpecification extends Specification {
def expected = ['applicationName', 'autoEncryptionSettings', 'clusterSettingsBuilder', 'codecRegistry', 'commandListeners',
'compressorList', 'connectionPoolSettingsBuilder', 'contextProvider', 'credential', 'dnsClient',
'heartbeatConnectTimeoutMS', 'heartbeatSocketTimeoutMS', 'inetAddressResolver', 'loggerSettingsBuilder',
- 'observabilitySettings',
+ 'maxAdaptiveRetries', 'observabilitySettings',
'readConcern', 'readPreference', 'retryReads',
'retryWrites', 'serverApi', 'serverSettingsBuilder', 'socketSettingsBuilder', 'sslSettingsBuilder',
'timeoutMS', 'transportSettings', 'uuidRepresentation',
@@ -572,7 +596,7 @@ class MongoClientSettingsSpecification extends Specification {
'applyToSslSettings', 'autoEncryptionSettings', 'build', 'codecRegistry', 'commandListenerList',
'compressorList', 'contextProvider', 'credential', 'dnsClient',
'heartbeatConnectTimeoutMS',
- 'heartbeatSocketTimeoutMS', 'inetAddressResolver', 'observabilitySettings', 'readConcern',
+ 'heartbeatSocketTimeoutMS', 'inetAddressResolver', 'maxAdaptiveRetries', 'observabilitySettings', 'readConcern',
'readPreference',
'retryReads', 'retryWrites',
'serverApi', 'timeout', 'transportSettings',
diff --git a/driver-kotlin-coroutine/src/main/kotlin/com/mongodb/kotlin/client/coroutine/ClientSession.kt b/driver-kotlin-coroutine/src/main/kotlin/com/mongodb/kotlin/client/coroutine/ClientSession.kt
index 6c53a1faf47..a1192285da2 100644
--- a/driver-kotlin-coroutine/src/main/kotlin/com/mongodb/kotlin/client/coroutine/ClientSession.kt
+++ b/driver-kotlin-coroutine/src/main/kotlin/com/mongodb/kotlin/client/coroutine/ClientSession.kt
@@ -184,6 +184,8 @@ public class ClientSession(public val wrapped: reactiveClientSession) : jClientS
/**
* Start a transaction in the context of this session with default transaction options. A transaction can not be
* started if there is already an active transaction on this session.
+ *
+ * @see com.mongodb.MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL
*/
public fun startTransaction(): Unit = wrapped.startTransaction()
@@ -192,15 +194,17 @@ public class ClientSession(public val wrapped: reactiveClientSession) : jClientS
* started if there is already an active transaction on this session.
*
* @param transactionOptions the options to apply to the transaction
+ * @see com.mongodb.MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL
*/
public fun startTransaction(transactionOptions: TransactionOptions): Unit =
wrapped.startTransaction(transactionOptions)
/**
- * Commit a transaction in the context of this session. A transaction can only be commmited if one has first been
+ * Commit a transaction in the context of this session. A transaction can only be committed if one has first been
* started.
*
* @return an empty publisher that indicates when the operation has completed
+ * @see com.mongodb.MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL
*/
public suspend fun commitTransaction() {
wrapped.commitTransaction().awaitFirstOrNull()
diff --git a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ClientSession.kt b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ClientSession.kt
index 5656feb4523..0f49d667995 100644
--- a/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ClientSession.kt
+++ b/driver-kotlin-sync/src/main/kotlin/com/mongodb/kotlin/client/ClientSession.kt
@@ -50,6 +50,8 @@ public class ClientSession(public val wrapped: JClientSession) : Closeable {
/**
* Start a transaction in the context of this session with default transaction options. A transaction can not be
* started if there is already an active transaction on this session.
+ *
+ * @see com.mongodb.MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL
*/
public fun startTransaction(): Unit = wrapped.startTransaction()
@@ -58,13 +60,16 @@ public class ClientSession(public val wrapped: JClientSession) : Closeable {
* started if there is already an active transaction on this session.
*
* @param transactionOptions the options to apply to the transaction
+ * @see com.mongodb.MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL
*/
public fun startTransaction(transactionOptions: TransactionOptions): Unit =
wrapped.startTransaction(transactionOptions)
/**
- * Commit a transaction in the context of this session. A transaction can only be commmited if one has first been
+ * Commit a transaction in the context of this session. A transaction can only be committed if one has first been
* started.
+ *
+ * @see com.mongodb.MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL
*/
public fun commitTransaction(): Unit = wrapped.commitTransaction()
@@ -82,6 +87,8 @@ public class ClientSession(public val wrapped: JClientSession) : Closeable {
* @param transactionBody the body of the transaction
* @param options the transaction options
* @return the return value of the transaction body
+ * @see com.mongodb.MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL
+ * @see com.mongodb.MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL
*/
public fun Starting with the 3.11.0 release, the default value is true
+ * Default is {@code true}.
*
* @return the retryReads value
+ * @see Builder#retryReads(boolean)
+ * @see #getMaxAdaptiveRetries()
* @mongodb.server.release 3.6
* @since 3.11
*/
@@ -465,6 +474,20 @@ public boolean getRetryReads() {
return wrapped.getRetryReads();
}
+ /**
+ * Returns the maximum number of retry attempts when encountering a retryable overload error.
+ * See {@link MongoClientSettings.Builder#maxAdaptiveRetries(Integer)} for more information.
+ *
+ * @return The maximum number of retry attempts when encountering a retryable overload error.
+ * @see Builder#maxAdaptiveRetries(Integer)
+ * @since 5.7
+ */
+ @Beta(Reason.CLIENT)
+ @Nullable
+ public Integer getMaxAdaptiveRetries() {
+ return wrapped.getMaxAdaptiveRetries();
+ }
+
/**
* The read concern to use. Starting with the 3.11.0 release, the default value is true
+ * Default is {@code true}.
*
- * @param retryReads sets if reads should be retried if they fail due to a network error.
+ * @param retryReads sets if read commands should be retried if they fail due to a retryable error.
* @return {@code this}
* @mongodb.server.release 3.6
* @see #getRetryReads()
+ * @see #maxAdaptiveRetries(Integer)
* @since 3.11
*/
public Builder retryReads(final boolean retryReads) {
@@ -1049,6 +1078,21 @@ public Builder retryReads(final boolean retryReads) {
return this;
}
+ /**
+ * Sets the maximum number of retry attempts when encountering a retryable overload error.
+ * See {@link MongoClientSettings.Builder#maxAdaptiveRetries(Integer)} for more information.
+ *
+ * @param maxAdaptiveRetries Sets the maximum number of retry attempts when encountering a retryable overload error.
+ * @return {@code this}.
+ * @see #getMaxAdaptiveRetries()
+ * @since 5.7
+ */
+ @Beta(Reason.CLIENT)
+ public Builder maxAdaptiveRetries(@Nullable final Integer maxAdaptiveRetries) {
+ wrapped.maxAdaptiveRetries(maxAdaptiveRetries);
+ return this;
+ }
+
/**
* Sets the read concern.
*
diff --git a/driver-legacy/src/main/com/mongodb/MongoClientURI.java b/driver-legacy/src/main/com/mongodb/MongoClientURI.java
index e471bbf1686..5d129bbd07f 100644
--- a/driver-legacy/src/main/com/mongodb/MongoClientURI.java
+++ b/driver-legacy/src/main/com/mongodb/MongoClientURI.java
@@ -16,6 +16,7 @@
package com.mongodb;
+import com.mongodb.annotations.Beta;
import com.mongodb.lang.Nullable;
import org.bson.UuidRepresentation;
@@ -147,10 +148,6 @@
*
General configuration:
*