Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions driver-core/src/main/com/mongodb/MongoException.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class MongoException extends RuntimeException {
*
* @see #hasErrorLabel(String)
* @since 3.8
* @mongodb.driver.manual core/transactions-in-applications/#std-label-transient-transaction-error
*/
public static final String TRANSIENT_TRANSACTION_ERROR_LABEL = "TransientTransactionError";

Expand All @@ -47,9 +48,32 @@ public class MongoException extends RuntimeException {
*
* @see #hasErrorLabel(String)
* @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.
*
* @see #hasErrorLabel(String)
* @since 5.7
* @mongodb.server.release 8.3
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*/
// 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
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TODO-BACKPRESSURE Valentin tag is added in accordance with the TODO label specified in the description of #1918.

public static final String SYSTEM_OVERLOADED_ERROR_LABEL = "SystemOverloadedError";

/**
* The operation was not executed and is safe to retry.
*
* @see #hasErrorLabel(String)
* @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;

private final int code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
import static java.util.Arrays.asList;

@SuppressWarnings("overloads")
final class CommandOperationHelper {
public final class CommandOperationHelper {
static WriteConcern validateAndGetEffectiveWriteConcern(final WriteConcern writeConcernSetting, final SessionContext sessionContext)
throws MongoClientException {
boolean activeTransaction = sessionContext.hasActiveTransaction();
Expand Down Expand Up @@ -223,8 +223,8 @@ static boolean isRetryWritesEnabled(@Nullable final BsonDocument command) {
|| command.getFirstKey().equals("commitTransaction") || command.getFirstKey().equals("abortTransaction")));
}

static final String RETRYABLE_WRITE_ERROR_LABEL = "RetryableWriteError";
private static final String NO_WRITES_PERFORMED_ERROR_LABEL = "NoWritesPerformed";
public static final String RETRYABLE_WRITE_ERROR_LABEL = "RetryableWriteError";
public static final String NO_WRITES_PERFORMED_ERROR_LABEL = "NoWritesPerformed";

private static boolean decideRetryableAndAddRetryableWriteErrorLabel(final Throwable t, @Nullable final Integer maxWireVersion) {
if (!(t instanceof MongoException)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ private void runTransactionWithRetry(final Runnable transactional) {
System.out.println("Transaction aborted. Caught exception during transaction.");

if (e.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL)) {
System.out.println("TransientTransactionError, aborting transaction and retrying ...");
System.out.printf("%s, aborting transaction and retrying ...%n",
MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and in some other places I am just making sure we use the constants for the labels.

} else {
throw e;
}
Expand All @@ -94,7 +95,8 @@ private void commitWithRetry(final ClientSession clientSession) {
} catch (MongoException e) {
// can retry commit
if (e.hasErrorLabel(MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL)) {
System.out.println("UnknownTransactionCommitResult, retrying commit operation ...");
System.out.printf("%s, retrying commit operation ...%n",
MongoException.UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL);
} else {
System.out.println("Exception during commit ...");
throw e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import com.mongodb.event.ConnectionCreatedEvent;
import com.mongodb.event.ConnectionReadyEvent;

import static com.mongodb.MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL;
import static com.mongodb.internal.connection.CommandHelper.HELLO;
import static com.mongodb.internal.connection.CommandHelper.LEGACY_HELLO;

Expand Down Expand Up @@ -687,7 +688,7 @@ public void test10CustomTestWithTransactionUsesASingleTimeoutWithLock() {
+ " blockConnection: true,"
+ " blockTimeMS: " + 25
+ " errorCode: " + 24
+ " errorLabels: [\"TransientTransactionError\"]"
+ " errorLabels: [\"" + TRANSIENT_TRANSACTION_ERROR_LABEL + "\"]"
+ " }"
+ "}");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
import static com.mongodb.ClusterFixture.serverVersionAtLeast;
import static com.mongodb.client.Fixture.getDefaultDatabaseName;
import static com.mongodb.client.Fixture.getMongoClientSettingsBuilder;
import static com.mongodb.internal.operation.CommandOperationHelper.NO_WRITES_PERFORMED_ERROR_LABEL;
import static com.mongodb.internal.operation.CommandOperationHelper.RETRYABLE_WRITE_ERROR_LABEL;
import static java.util.Collections.singletonList;
import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeTrue;
Expand All @@ -69,7 +71,7 @@ public static void doesNotLeak(final Function<MongoClientSettings, MongoClient>
.append("data", new BsonDocument()
.append("writeConcernError", new BsonDocument()
.append("code", new BsonInt32(91))
.append("errorLabels", new BsonArray(Stream.of("RetryableWriteError")
.append("errorLabels", new BsonArray(Stream.of(RETRYABLE_WRITE_ERROR_LABEL)
.map(BsonString::new).collect(Collectors.toList())))
.append("errmsg", new BsonString(""))
)
Expand All @@ -81,7 +83,7 @@ public static void doesNotLeak(final Function<MongoClientSettings, MongoClient>
.append("data", new BsonDocument()
.append("failCommands", new BsonArray(singletonList(new BsonString("insert"))))
.append("errorCode", new BsonInt32(10107))
.append("errorLabels", new BsonArray(Stream.of("RetryableWriteError", "NoWritesPerformed")
.append("errorLabels", new BsonArray(Stream.of(RETRYABLE_WRITE_ERROR_LABEL, NO_WRITES_PERFORMED_ERROR_LABEL)
.map(BsonString::new).collect(Collectors.toList()))));
doesNotLeak(clientCreator, writeConcernErrorFpDoc, true, noWritesPerformedFpDoc);
doesNotLeak(clientCreator, noWritesPerformedFpDoc, false, writeConcernErrorFpDoc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
import static com.mongodb.client.Fixture.getDefaultDatabaseName;
import static com.mongodb.client.Fixture.getMongoClientSettingsBuilder;
import static com.mongodb.client.Fixture.getMultiMongosMongoClientSettingsBuilder;
import static com.mongodb.internal.operation.CommandOperationHelper.NO_WRITES_PERFORMED_ERROR_LABEL;
import static com.mongodb.internal.operation.CommandOperationHelper.RETRYABLE_WRITE_ERROR_LABEL;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
Expand Down Expand Up @@ -135,7 +137,7 @@ public static <R> void poolClearedExceptionMustBeRetryable(
.append("failCommands", new BsonArray(singletonList(new BsonString(operationName))))
.append("errorCode", new BsonInt32(91))
.append("errorLabels", write
? new BsonArray(singletonList(new BsonString("RetryableWriteError")))
? new BsonArray(singletonList(new BsonString(RETRYABLE_WRITE_ERROR_LABEL)))
: new BsonArray())
.append("blockConnection", BsonBoolean.valueOf(true))
.append("blockTimeMS", new BsonInt32(1000)));
Expand Down Expand Up @@ -193,7 +195,7 @@ public void commandSucceeded(final CommandSucceededEvent event) {
.append("data", new BsonDocument()
.append("failCommands", new BsonArray(singletonList(new BsonString("insert"))))
.append("errorCode", new BsonInt32(10107))
.append("errorLabels", new BsonArray(Stream.of("RetryableWriteError", "NoWritesPerformed")
.append("errorLabels", new BsonArray(Stream.of(RETRYABLE_WRITE_ERROR_LABEL, NO_WRITES_PERFORMED_ERROR_LABEL)
.map(BsonString::new).collect(Collectors.toList())))),
primaryServerAddress
)));
Expand All @@ -207,7 +209,7 @@ public void commandSucceeded(final CommandSucceededEvent event) {
.append("data", new BsonDocument()
.append("writeConcernError", new BsonDocument()
.append("code", new BsonInt32(91))
.append("errorLabels", new BsonArray(Stream.of("RetryableWriteError")
.append("errorLabels", new BsonArray(Stream.of(RETRYABLE_WRITE_ERROR_LABEL)
.map(BsonString::new).collect(Collectors.toList())))
.append("errmsg", new BsonString(""))
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

// See https://github.com/mongodb/specifications/blob/master/source/transactions-convenient-api/tests/README.md#prose-tests
/**
* <a href="https://github.com/mongodb/specifications/blob/master/source/transactions-convenient-api/tests/README.md#prose-tests">Prose Tests</a>.
*/
public class WithTransactionProseTest extends DatabaseTestCase {
private static final long START_TIME_MS = 1L;
private static final long ERROR_GENERATING_INTERVAL = 121000L;
Expand All @@ -52,11 +54,10 @@ public void setUp() {
collection.insertOne(Document.parse("{ _id : 0 }"));
}

//
// Test that the callback raises a custom exception or error that does not include either UnknownTransactionCommitResult or
// TransientTransactionError error labels. The callback will execute using withTransaction and assert that the callback's error
// bypasses any retry logic within withTransaction and is propagated to the caller of withTransaction.
//
/**
* <a href="https://github.com/mongodb/specifications/blob/master/source/transactions-convenient-api/tests/README.md#callback-raises-a-custom-error">
* Callback Raises a Custom Error</a>.
*/
@Test
public void testCallbackRaisesCustomError() {
final String exceptionMessage = "NotTransientOrUnknownError";
Expand All @@ -71,10 +72,10 @@ public void testCallbackRaisesCustomError() {
}
}

//
// Test that the callback that returns a custom value (e.g. boolean, string, object). Execute this callback using withTransaction
// and assert that the callback's return value is propagated to the caller of withTransaction.
//
/**
* <a href="https://github.com/mongodb/specifications/blob/master/source/transactions-convenient-api/tests/README.md#callback-returns-a-value">
* Callback Returns a Value</a>.
*/
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This non-documentation-style description of the method was effectively duplicating the text of the linked prose test. Instead of duplicating the test descriptions, we should link to them.

The same applies to other changes in this file.

@Test
public void testCallbackReturnsValue() {
try (ClientSession session = client.startSession()) {
Expand All @@ -87,10 +88,10 @@ public void testCallbackReturnsValue() {
}
}

//
// If the callback raises an error with the TransientTransactionError label and the retry timeout has been exceeded, withTransaction
// should propagate the error to its caller.
//
/**
* <a href="https://github.com/mongodb/specifications/blob/master/source/transactions-convenient-api/tests/README.md#retry-timeout-is-enforced">
* Retry Timeout is Enforced</a>, first scenario on the list.
*/
@Test
public void testRetryTimeoutEnforcedTransientTransactionError() {
final String errorMessage = "transient transaction error";
Expand All @@ -110,10 +111,10 @@ public void testRetryTimeoutEnforcedTransientTransactionError() {
}
}

//
// If committing raises an error with the UnknownTransactionCommitResult label, the error is not a write concern timeout, and the
// retry timeout has been exceeded, withTransaction should propagate the error to its caller.
//
/**
* <a href="https://github.com/mongodb/specifications/blob/master/source/transactions-convenient-api/tests/README.md#retry-timeout-is-enforced">
* Retry Timeout is Enforced</a>, second scenario on the list.
*/
@Test
public void testRetryTimeoutEnforcedUnknownTransactionCommit() {
MongoDatabase failPointAdminDb = client.getDatabase("admin");
Expand All @@ -137,11 +138,10 @@ public void testRetryTimeoutEnforcedUnknownTransactionCommit() {
}
}

//
// If committing raises an error with the TransientTransactionError label and the retry timeout has been exceeded, withTransaction
// should propagate the error to its caller. This case may occur if the commit was internally retried against a new primary after
// a failover and the second primary returned a NoSuchTransaction error response.
//
/**
* <a href="https://github.com/mongodb/specifications/blob/master/source/transactions-convenient-api/tests/README.md#retry-timeout-is-enforced">
* Retry Timeout is Enforced</a>, third scenario on the list.
*/
@Test
public void testRetryTimeoutEnforcedTransientTransactionErrorOnCommit() {
MongoDatabase failPointAdminDb = client.getDatabase("admin");
Expand All @@ -166,9 +166,9 @@ public void testRetryTimeoutEnforcedTransientTransactionErrorOnCommit() {
}
}

//
// Ensure cannot override timeout in transaction
//
/**
* Ensure cannot override timeout in transaction.
*/
@Test
public void testTimeoutMS() {
try (ClientSession session = client.startSession(ClientSessionOptions.builder()
Expand All @@ -182,9 +182,9 @@ public void testTimeoutMS() {
}
}

//
// Ensure legacy settings don't cause issues in sessions
//
/**
* Ensure legacy settings don't cause issues in sessions.
*/
@Test
public void testTimeoutMSAndLegacySettings() {
try (ClientSession session = client.startSession(ClientSessionOptions.builder()
Expand Down