summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAli Mir <ali.mir@mongodb.com>2020-02-18 16:19:33 +0000
committerevergreen <evergreen@mongodb.com>2020-02-18 16:19:33 +0000
commiteb284b042c71edf0eac445d3ceb79f7fdeabc5d1 (patch)
tree3edc885bd11497dd8cca1073f72df3e4909ecd94 /src
parentd9a68d2c084bfed60527aa9aa29e2a843b97e9c2 (diff)
downloadmongo-eb284b042c71edf0eac445d3ceb79f7fdeabc5d1.tar.gz
SERVER-43889 Distinguish between retryable write and transaction when failing a command
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/transaction_participant.cpp24
-rw-r--r--src/mongo/db/transaction_participant_retryable_writes_test.cpp36
-rw-r--r--src/mongo/db/transaction_participant_test.cpp37
3 files changed, 92 insertions, 5 deletions
diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp
index c8180467c20..adf348e6eed 100644
--- a/src/mongo/db/transaction_participant.cpp
+++ b/src/mongo/db/transaction_participant.cpp
@@ -495,11 +495,25 @@ void TransactionParticipant::Participant::beginOrContinue(OperationContext* opCt
getTestCommandsEnabled());
}
- uassert(ErrorCodes::TransactionTooOld,
- str::stream() << "Cannot start transaction " << txnNumber << " on session "
- << _sessionId() << " because a newer transaction " << o().activeTxnNumber
- << " has already started.",
- txnNumber >= o().activeTxnNumber);
+ if (txnNumber < o().activeTxnNumber) {
+ const std::string currOperation =
+ o().txnState.isInRetryableWriteMode() ? "retryable write" : "transaction";
+ if (!autocommit) {
+ uasserted(ErrorCodes::TransactionTooOld,
+ str::stream()
+ << "Retryable write with txnNumber " << txnNumber
+ << " is prohibited on session " << _sessionId() << " because a newer "
+ << currOperation << " with txnNumber " << o().activeTxnNumber
+ << " has already started on this session.");
+ } else {
+ uasserted(ErrorCodes::TransactionTooOld,
+ str::stream() << "Cannot start transaction " << txnNumber << " on session "
+ << _sessionId() << " because a newer " << currOperation
+ << " with txnNumber " << o().activeTxnNumber
+ << " has already started on this session.");
+ }
+ }
+
// Requests without an autocommit field are interpreted as retryable writes. They cannot specify
// startTransaction, which is verified earlier when parsing the request.
diff --git a/src/mongo/db/transaction_participant_retryable_writes_test.cpp b/src/mongo/db/transaction_participant_retryable_writes_test.cpp
index 6e4d4b096e7..36b0a98ab83 100644
--- a/src/mongo/db/transaction_participant_retryable_writes_test.cpp
+++ b/src/mongo/db/transaction_participant_retryable_writes_test.cpp
@@ -379,6 +379,42 @@ TEST_F(TransactionParticipantRetryableWritesTest, StartingOldTxnShouldAssert) {
ASSERT(txnParticipant.getLastWriteOpTime().isNull());
}
+TEST_F(TransactionParticipantRetryableWritesTest,
+ OlderRetryableWriteFailsOnSessionWithNewerRetryableWrite) {
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
+ const TxnNumber txnNum = 22;
+ const auto& sessionId = *opCtx()->getLogicalSessionId();
+
+ StringBuilder sb;
+ sb << "Retryable write with txnNumber 21 is prohibited on session " << sessionId
+ << " because a newer retryable write with txnNumber 22 has already started on this session.";
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
+ ASSERT_THROWS_WHAT(
+ txnParticipant.beginOrContinue(opCtx(), txnNum - 1, boost::none, boost::none),
+ AssertionException,
+ sb.str());
+ ASSERT(txnParticipant.getLastWriteOpTime().isNull());
+}
+
+TEST_F(TransactionParticipantRetryableWritesTest,
+ OldTransactionFailsOnSessionWithNewerRetryableWrite) {
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ txnParticipant.refreshFromStorageIfNeeded(opCtx());
+ const TxnNumber txnNum = 22;
+ auto autocommit = false;
+ const auto& sessionId = *opCtx()->getLogicalSessionId();
+
+ StringBuilder sb;
+ sb << "Cannot start transaction 21 on session " << sessionId
+ << " because a newer retryable write with txnNumber 22 has already started on this session.";
+ txnParticipant.beginOrContinue(opCtx(), txnNum, boost::none, boost::none);
+ ASSERT_THROWS_WHAT(txnParticipant.beginOrContinue(opCtx(), txnNum - 1, autocommit, boost::none),
+ AssertionException,
+ sb.str());
+ ASSERT(txnParticipant.getLastWriteOpTime().isNull());
+}
+
TEST_F(TransactionParticipantRetryableWritesTest, SessionTransactionsCollectionNotDefaultCreated) {
auto txnParticipant = TransactionParticipant::get(opCtx());
txnParticipant.refreshFromStorageIfNeeded(opCtx());
diff --git a/src/mongo/db/transaction_participant_test.cpp b/src/mongo/db/transaction_participant_test.cpp
index 932ef261e88..b02fd45ba4c 100644
--- a/src/mongo/db/transaction_participant_test.cpp
+++ b/src/mongo/db/transaction_participant_test.cpp
@@ -1145,6 +1145,43 @@ TEST_F(TxnParticipantTest, CannotContinueTransactionIfNotPrimary) {
ErrorCodes::NotMaster);
}
+TEST_F(TxnParticipantTest, OlderTransactionFailsOnSessionWithNewerTransaction) {
+ // Will start the transaction.
+ auto sessionCheckout = checkOutSession();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ ASSERT_TRUE(txnParticipant.transactionIsOpen());
+ auto autocommit = false;
+ auto startTransaction = true;
+ const auto& sessionId = *opCtx()->getLogicalSessionId();
+
+ StringBuilder sb;
+ sb << "Cannot start transaction 19 on session " << sessionId
+ << " because a newer transaction with txnNumber 20 has already started on this session.";
+ ASSERT_THROWS_WHAT(txnParticipant.beginOrContinue(
+ opCtx(), *opCtx()->getTxnNumber() - 1, autocommit, startTransaction),
+ AssertionException,
+ sb.str());
+ ASSERT(txnParticipant.getLastWriteOpTime().isNull());
+}
+
+
+TEST_F(TxnParticipantTest, OldRetryableWriteFailsOnSessionWithNewerTransaction) {
+ // Will start the transaction.
+ auto sessionCheckout = checkOutSession();
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ ASSERT_TRUE(txnParticipant.transactionIsOpen());
+ const auto& sessionId = *opCtx()->getLogicalSessionId();
+
+ StringBuilder sb;
+ sb << "Retryable write with txnNumber 19 is prohibited on session " << sessionId
+ << " because a newer transaction with txnNumber 20 has already started on this session.";
+ ASSERT_THROWS_WHAT(txnParticipant.beginOrContinue(
+ opCtx(), *opCtx()->getTxnNumber() - 1, boost::none, boost::none),
+ AssertionException,
+ sb.str());
+ ASSERT(txnParticipant.getLastWriteOpTime().isNull());
+}
+
TEST_F(TxnParticipantTest, CannotStartNewTransactionWhilePreparedTransactionInProgress) {
auto sessionCheckout = checkOutSession();
auto txnParticipant = TransactionParticipant::get(opCtx());