summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zhang <jason.zhang@mongodb.com>2022-02-03 16:02:58 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-02-03 17:13:45 +0000
commit0e4e2c4a28fee3358ff2f0f0f102f0e8b59185d0 (patch)
tree847a267aa8f93f167bd8d52da2078e35ab4a670b
parent882ebf1ad73b1c39af8b0cd82de7c648b8a860d2 (diff)
downloadmongo-0e4e2c4a28fee3358ff2f0f0f102f0e8b59185d0.tar.gz
SERVER-61088 Make transaction participants check if txnRetryCounter is supported during startTransaction and have mongos use it
-rw-r--r--jstests/multiVersion/targetedTestsLastLtsFeatures/internal_transactions_retry_on_transient_transaction_error.js18
-rw-r--r--jstests/sharding/cancel_coordinate_txn_commit_with_tickets_exhausted.js5
-rw-r--r--jstests/sharding/txn_recover_decision_using_recovery_router.js2
-rw-r--r--src/mongo/base/error_codes.yml2
-rw-r--r--src/mongo/db/commands/txn_cmds.idl1
-rw-r--r--src/mongo/db/error_labels.cpp2
-rw-r--r--src/mongo/db/initialize_operation_session_info.cpp4
-rw-r--r--src/mongo/db/op_observer_impl.cpp14
-rw-r--r--src/mongo/db/s/transaction_coordinator_service.cpp5
-rw-r--r--src/mongo/db/s/transaction_coordinator_test.cpp14
-rw-r--r--src/mongo/db/s/transaction_coordinator_util.cpp33
-rw-r--r--src/mongo/db/transaction_participant.cpp25
-rw-r--r--src/mongo/db/transaction_participant_test.cpp45
-rw-r--r--src/mongo/s/transaction_router.cpp7
-rw-r--r--src/mongo/s/transaction_router_test.cpp86
15 files changed, 161 insertions, 102 deletions
diff --git a/jstests/multiVersion/targetedTestsLastLtsFeatures/internal_transactions_retry_on_transient_transaction_error.js b/jstests/multiVersion/targetedTestsLastLtsFeatures/internal_transactions_retry_on_transient_transaction_error.js
index 1ee491794b1..ff1ea1baffc 100644
--- a/jstests/multiVersion/targetedTestsLastLtsFeatures/internal_transactions_retry_on_transient_transaction_error.js
+++ b/jstests/multiVersion/targetedTestsLastLtsFeatures/internal_transactions_retry_on_transient_transaction_error.js
@@ -55,15 +55,6 @@ function runTest(downgradeFCV) {
jsTest.log("Verify retries only work in FCV latest");
const lsid1 = {id: UUID()};
const txnNumber1 = NumberLong(1);
- configureFailPoint(shard0Primary,
- "failCommand",
- {
- failInternalCommands: true,
- failCommands: ["insert"],
- errorCode: ErrorCodes.LockBusy,
- namespace: kNs
- },
- {times: 1});
const insertCmdObj = {
insert: kCollName,
documents: [{x: 1}],
@@ -71,14 +62,13 @@ function runTest(downgradeFCV) {
txnNumber: txnNumber1,
startTransaction: true,
autocommit: false,
- txnRetryCounter: NumberInt(0)
+ txnRetryCounter: NumberInt(1)
};
- assert.commandFailedWithCode(testDB.runCommand(insertCmdObj), ErrorCodes.InvalidOptions);
+ assert.commandFailedWithCode(testDB.runCommand(insertCmdObj),
+ ErrorCodes.TxnRetryCounterNotSupported);
assert.commandWorked(st.s.adminCommand({setFeatureCompatibilityVersion: latestFCV}));
-
- assert.commandFailedWithCode(testDB.runCommand(insertCmdObj), ErrorCodes.LockBusy);
- insertCmdObj.txnRetryCounter = NumberInt(1);
+ insertCmdObj.txnRetryCounter = NumberInt(2);
assert.commandWorked(testDB.runCommand(insertCmdObj));
assert.commandWorked(testDB.adminCommand({
commitTransaction: 1,
diff --git a/jstests/sharding/cancel_coordinate_txn_commit_with_tickets_exhausted.js b/jstests/sharding/cancel_coordinate_txn_commit_with_tickets_exhausted.js
index a905759b2a8..9c43cd181ef 100644
--- a/jstests/sharding/cancel_coordinate_txn_commit_with_tickets_exhausted.js
+++ b/jstests/sharding/cancel_coordinate_txn_commit_with_tickets_exhausted.js
@@ -13,6 +13,11 @@
*
* Step 5. Turn off the `hangWithLockDuringBatchRemoveFp`
* and join the parallel remove operations and transaction thread.
+ *
+ * @tags: [
+ * uses_multi_shard_transaction,
+ * uses_transactions,
+ * ]
*/
(function() {
diff --git a/jstests/sharding/txn_recover_decision_using_recovery_router.js b/jstests/sharding/txn_recover_decision_using_recovery_router.js
index 8a320444b82..ef154f4a7fd 100644
--- a/jstests/sharding/txn_recover_decision_using_recovery_router.js
+++ b/jstests/sharding/txn_recover_decision_using_recovery_router.js
@@ -2,7 +2,7 @@
* Tests that the coordinateCommitTransaction command falls back to recovering the decision from
* the local participant.
*
- * @tags: [uses_transactions, uses_prepare_transaction]
+ * @tags: [uses_transactions, uses_prepare_transaction, uses_multi_shard_transaction]
*/
(function() {
"use strict";
diff --git a/src/mongo/base/error_codes.yml b/src/mongo/base/error_codes.yml
index 8cfd049fb92..0a8b08802cd 100644
--- a/src/mongo/base/error_codes.yml
+++ b/src/mongo/base/error_codes.yml
@@ -467,6 +467,8 @@ error_codes:
- {code: 363, name: RetryableTransactionInProgress}
+ - {code: 364, name: TxnRetryCounterNotSupported}
+
# Error codes 4000-8999 are reserved.
# Non-sequential error codes for compatibility only)
diff --git a/src/mongo/db/commands/txn_cmds.idl b/src/mongo/db/commands/txn_cmds.idl
index 61cc3afa783..626d61c75d7 100644
--- a/src/mongo/db/commands/txn_cmds.idl
+++ b/src/mongo/db/commands/txn_cmds.idl
@@ -43,7 +43,6 @@ structs:
write for it"
type: bool
-
TxnRecoveryToken:
description: "Contains info for retrying the commit of a sharded transaction"
fields:
diff --git a/src/mongo/db/error_labels.cpp b/src/mongo/db/error_labels.cpp
index ecb1e499168..b66d0df2d74 100644
--- a/src/mongo/db/error_labels.cpp
+++ b/src/mongo/db/error_labels.cpp
@@ -199,6 +199,8 @@ bool isTransientTransactionError(ErrorCodes::Error code,
case ErrorCodes::StaleDbVersion:
case ErrorCodes::TenantMigrationAborted:
case ErrorCodes::TenantMigrationCommitted:
+ // TODO: (SERVER-62375): Remove upgrade/downgrade code for internal transactions
+ case ErrorCodes::TxnRetryCounterNotSupported:
return true;
default:
isTransient = false;
diff --git a/src/mongo/db/initialize_operation_session_info.cpp b/src/mongo/db/initialize_operation_session_info.cpp
index 659d429ef7f..4750869478f 100644
--- a/src/mongo/db/initialize_operation_session_info.cpp
+++ b/src/mongo/db/initialize_operation_session_info.cpp
@@ -135,10 +135,6 @@ OperationSessionInfoFromClient initializeOperationSessionInfo(OperationContext*
if (auto txnRetryCounter = osi.getTxnRetryCounter()) {
uassert(ErrorCodes::InvalidOptions,
- "txnRetryCounter is not enabled",
- feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility));
- uassert(ErrorCodes::InvalidOptions,
"txnRetryCounter is only allowed for internal clients",
isAuthorizedForInternalClusterAction);
uassert(ErrorCodes::InvalidOptions,
diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp
index 08cf284929c..895845a0fde 100644
--- a/src/mongo/db/op_observer_impl.cpp
+++ b/src/mongo/db/op_observer_impl.cpp
@@ -1346,16 +1346,13 @@ OpTimeBundle logApplyOpsForTransaction(OperationContext* opCtx,
invariant(isInternalSessionForRetryableWrite(*opCtx->getLogicalSessionId()));
}
- const bool areInternalTransactionsEnabled =
- feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility);
const auto txnRetryCounter = *opCtx->getTxnRetryCounter();
oplogEntry->setOpType(repl::OpTypeEnum::kCommand);
oplogEntry->setNss({"admin", "$cmd"});
oplogEntry->setSessionId(opCtx->getLogicalSessionId());
oplogEntry->setTxnNumber(opCtx->getTxnNumber());
- if (areInternalTransactionsEnabled && !isDefaultTxnRetryCounter(txnRetryCounter)) {
+ if (!isDefaultTxnRetryCounter(txnRetryCounter)) {
oplogEntry->getOperationSessionInfo().setTxnRetryCounter(txnRetryCounter);
}
@@ -1369,7 +1366,7 @@ OpTimeBundle logApplyOpsForTransaction(OperationContext* opCtx,
sessionTxnRecord.setLastWriteDate(times.wallClockTime);
sessionTxnRecord.setState(txnState);
sessionTxnRecord.setStartOpTime(startOpTime);
- if (areInternalTransactionsEnabled && !isDefaultTxnRetryCounter(txnRetryCounter)) {
+ if (!isDefaultTxnRetryCounter(txnRetryCounter)) {
sessionTxnRecord.setTxnRetryCounter(txnRetryCounter);
}
onWriteOpCompleted(opCtx, std::move(stmtIdsWritten), sessionTxnRecord);
@@ -1587,16 +1584,13 @@ int logOplogEntriesForTransaction(
void logCommitOrAbortForPreparedTransaction(OperationContext* opCtx,
MutableOplogEntry* oplogEntry,
DurableTxnStateEnum durableState) {
- const bool areInternalTransactionsEnabled =
- feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility);
const auto txnRetryCounter = *opCtx->getTxnRetryCounter();
oplogEntry->setOpType(repl::OpTypeEnum::kCommand);
oplogEntry->setNss({"admin", "$cmd"});
oplogEntry->setSessionId(opCtx->getLogicalSessionId());
oplogEntry->setTxnNumber(opCtx->getTxnNumber());
- if (areInternalTransactionsEnabled && !isDefaultTxnRetryCounter(txnRetryCounter)) {
+ if (!isDefaultTxnRetryCounter(txnRetryCounter)) {
oplogEntry->getOperationSessionInfo().setTxnRetryCounter(txnRetryCounter);
}
oplogEntry->setPrevWriteOpTimeInTransaction(
@@ -1624,7 +1618,7 @@ void logCommitOrAbortForPreparedTransaction(OperationContext* opCtx,
sessionTxnRecord.setLastWriteOpTime(oplogOpTime);
sessionTxnRecord.setLastWriteDate(oplogEntry->getWallClockTime());
sessionTxnRecord.setState(durableState);
- if (areInternalTransactionsEnabled && !isDefaultTxnRetryCounter(txnRetryCounter)) {
+ if (!isDefaultTxnRetryCounter(txnRetryCounter)) {
sessionTxnRecord.setTxnRetryCounter(txnRetryCounter);
}
onWriteOpCompleted(opCtx, {}, sessionTxnRecord);
diff --git a/src/mongo/db/s/transaction_coordinator_service.cpp b/src/mongo/db/s/transaction_coordinator_service.cpp
index 7deb69f13d4..bdc4cc373d9 100644
--- a/src/mongo/db/s/transaction_coordinator_service.cpp
+++ b/src/mongo/db/s/transaction_coordinator_service.cpp
@@ -243,11 +243,6 @@ void TransactionCoordinatorService::onStepUp(OperationContext* opCtx,
const auto txnNumber = *doc.getId().getTxnNumber();
const auto txnRetryCounter = [&] {
if (auto optTxnRetryCounter = doc.getId().getTxnRetryCounter()) {
- uassert(ErrorCodes::InvalidOptions,
- "TxnRetryCounter is only supported when internal "
- "transactions are enabled",
- feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility));
return *optTxnRetryCounter;
}
return 0;
diff --git a/src/mongo/db/s/transaction_coordinator_test.cpp b/src/mongo/db/s/transaction_coordinator_test.cpp
index 2d1187bc215..f650004e3b8 100644
--- a/src/mongo/db/s/transaction_coordinator_test.cpp
+++ b/src/mongo/db/s/transaction_coordinator_test.cpp
@@ -640,7 +640,7 @@ TEST_F(TransactionCoordinatorDriverTest,
}
TEST_F(TransactionCoordinatorDriverTest,
- SendPrepareAndDecisionDoesNotAttachTxnRetryCounterIfFeatureFlagIsNotEnabled) {
+ SendPrepareAndDecisionContinuesToUseTxnRetryCounterIfNotDefault) {
RAIIServerParameterControllerForTest controller{"featureFlagInternalTransactions", false};
txn::AsyncWorkScheduler aws(getServiceContext());
auto prepareFuture = txn::sendPrepare(getServiceContext(),
@@ -650,7 +650,9 @@ TEST_F(TransactionCoordinatorDriverTest,
APIParameters(),
kOneShardIdList);
onCommands({[&](const executor::RemoteCommandRequest& request) {
- ASSERT_FALSE(request.cmdObj.hasField("txnRetryCounter"));
+ ASSERT_TRUE(request.cmdObj.hasField("txnRetryCounter"));
+ ASSERT_EQUALS(request.cmdObj.getIntField("txnRetryCounter"),
+ *_txnNumberAndRetryCounter.getTxnRetryCounter());
return kNoSuchTransaction;
}});
prepareFuture.get();
@@ -663,7 +665,9 @@ TEST_F(TransactionCoordinatorDriverTest,
kOneShardIdList,
{});
onCommands({[&](const executor::RemoteCommandRequest& request) {
- ASSERT_FALSE(request.cmdObj.hasField("txnRetryCounter"));
+ ASSERT_TRUE(request.cmdObj.hasField("txnRetryCounter"));
+ ASSERT_EQUALS(request.cmdObj.getIntField("txnRetryCounter"),
+ *_txnNumberAndRetryCounter.getTxnRetryCounter());
return kNoSuchTransaction;
}});
commitFuture.get();
@@ -675,7 +679,9 @@ TEST_F(TransactionCoordinatorDriverTest,
APIParameters(),
kOneShardIdList);
onCommands({[&](const executor::RemoteCommandRequest& request) {
- ASSERT_FALSE(request.cmdObj.hasField("txnRetryCounter"));
+ ASSERT_TRUE(request.cmdObj.hasField("txnRetryCounter"));
+ ASSERT_EQUALS(request.cmdObj.getIntField("txnRetryCounter"),
+ *_txnNumberAndRetryCounter.getTxnRetryCounter());
return kNoSuchTransaction;
}});
abortFuture.get();
diff --git a/src/mongo/db/s/transaction_coordinator_util.cpp b/src/mongo/db/s/transaction_coordinator_util.cpp
index 26e7039c8d6..6c71a3071cb 100644
--- a/src/mongo/db/s/transaction_coordinator_util.cpp
+++ b/src/mongo/db/s/transaction_coordinator_util.cpp
@@ -126,10 +126,6 @@ repl::OpTime persistParticipantListBlocking(
sessionInfo.setTxnNumber(txnNumberAndRetryCounter.getTxnNumber());
if (auto txnRetryCounter = txnNumberAndRetryCounter.getTxnRetryCounter();
txnRetryCounter && !isDefaultTxnRetryCounter(*txnRetryCounter)) {
- uassert(ErrorCodes::InvalidOptions,
- "TxnRetryCounter is only supported when internal transactions are enabled",
- feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility));
sessionInfo.setTxnRetryCounter(*txnNumberAndRetryCounter.getTxnRetryCounter());
}
@@ -258,10 +254,9 @@ Future<PrepareVoteConsensus> sendPrepare(ServiceContext* service,
<< txnNumberAndRetryCounter.getTxnNumber() << "autocommit"
<< false << WriteConcernOptions::kWriteConcernField
<< WriteConcernOptions::Majority));
- if (feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility)) {
- bob.append(OperationSessionInfo::kTxnRetryCounterFieldName,
- *txnNumberAndRetryCounter.getTxnRetryCounter());
+ if (auto txnRetryCounter = txnNumberAndRetryCounter.getTxnRetryCounter();
+ txnRetryCounter && !isDefaultTxnRetryCounter(*txnRetryCounter)) {
+ bob.append(OperationSessionInfo::kTxnRetryCounterFieldName, *txnRetryCounter);
}
apiParams.appendInfo(&bob);
auto prepareObj = prepareTransaction.toBSON(bob.obj());
@@ -353,10 +348,6 @@ repl::OpTime persistDecisionBlocking(OperationContext* opCtx,
sessionInfo.setTxnNumber(txnNumberAndRetryCounter.getTxnNumber());
if (auto txnRetryCounter = txnNumberAndRetryCounter.getTxnRetryCounter();
txnRetryCounter && !isDefaultTxnRetryCounter(*txnRetryCounter)) {
- uassert(ErrorCodes::InvalidOptions,
- "TxnRetryCounter is only supported when internal transactions are enabled",
- feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility));
sessionInfo.setTxnRetryCounter(*txnNumberAndRetryCounter.getTxnRetryCounter());
}
@@ -469,10 +460,9 @@ Future<void> sendCommit(ServiceContext* service,
<< txnNumberAndRetryCounter.getTxnNumber() << "autocommit"
<< false << WriteConcernOptions::kWriteConcernField
<< WriteConcernOptions::Majority));
- if (feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility)) {
- bob.append(OperationSessionInfo::kTxnRetryCounterFieldName,
- *txnNumberAndRetryCounter.getTxnRetryCounter());
+ if (auto txnRetryCounter = txnNumberAndRetryCounter.getTxnRetryCounter();
+ txnRetryCounter && !isDefaultTxnRetryCounter(*txnRetryCounter)) {
+ bob.append(OperationSessionInfo::kTxnRetryCounterFieldName, *txnRetryCounter);
}
apiParams.appendInfo(&bob);
auto commitObj = commitTransaction.toBSON(bob.obj());
@@ -514,10 +504,9 @@ Future<void> sendAbort(ServiceContext* service,
<< txnNumberAndRetryCounter.getTxnNumber() << "autocommit"
<< false << WriteConcernOptions::kWriteConcernField
<< WriteConcernOptions::Majority));
- if (feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility)) {
- bob.append(OperationSessionInfo::kTxnRetryCounterFieldName,
- *txnNumberAndRetryCounter.getTxnRetryCounter());
+ if (auto txnRetryCounter = txnNumberAndRetryCounter.getTxnRetryCounter();
+ txnRetryCounter && !isDefaultTxnRetryCounter(*txnRetryCounter)) {
+ bob.append(OperationSessionInfo::kTxnRetryCounterFieldName, *txnRetryCounter);
}
apiParams.appendInfo(&bob);
auto abortObj = abortTransaction.toBSON(bob.obj());
@@ -568,10 +557,6 @@ void deleteCoordinatorDocBlocking(OperationContext* opCtx,
sessionInfo.setTxnNumber(txnNumberAndRetryCounter.getTxnNumber());
if (auto txnRetryCounter = txnNumberAndRetryCounter.getTxnRetryCounter();
txnRetryCounter && !isDefaultTxnRetryCounter(*txnRetryCounter)) {
- uassert(ErrorCodes::InvalidOptions,
- "TxnRetryCounter is only supported when internal transactions are enabled",
- feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility));
sessionInfo.setTxnRetryCounter(*txnNumberAndRetryCounter.getTxnRetryCounter());
}
diff --git a/src/mongo/db/transaction_participant.cpp b/src/mongo/db/transaction_participant.cpp
index d864b9e8a1c..dac6f597f7b 100644
--- a/src/mongo/db/transaction_participant.cpp
+++ b/src/mongo/db/transaction_participant.cpp
@@ -860,11 +860,21 @@ void TransactionParticipant::Participant::beginOrContinue(
TxnNumberAndRetryCounter txnNumberAndRetryCounter,
boost::optional<bool> autocommit,
boost::optional<bool> startTransaction) {
- if (_isInternalSession() && startTransaction) {
- uassert(ErrorCodes::InternalTransactionNotSupported,
- "Internal transactions are not enabled",
- feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility));
+ if (startTransaction) {
+ if (_isInternalSession()) {
+ uassert(ErrorCodes::InternalTransactionNotSupported,
+ "Internal transactions are not enabled",
+ feature_flags::gFeatureFlagInternalTransactions.isEnabled(
+ serverGlobalParams.featureCompatibility));
+ }
+ if (auto txnRetryCounter = txnNumberAndRetryCounter.getTxnRetryCounter();
+ txnRetryCounter && !isDefaultTxnRetryCounter(*txnRetryCounter)) {
+ // TODO: (SERVER-62375): Remove upgrade/downgrade code for internal transactions
+ uassert(ErrorCodes::TxnRetryCounterNotSupported,
+ "TxnRetryCounter support is not enabled",
+ feature_flags::gFeatureFlagInternalTransactions.isEnabled(
+ serverGlobalParams.featureCompatibility));
+ }
}
if (_isInternalSessionForRetryableWrite()) {
@@ -2709,11 +2719,6 @@ void TransactionParticipant::Participant::_refreshSelfFromStorageIfNeeded(Operat
o(lg).activeTxnNumberAndRetryCounter.setTxnRetryCounter([&] {
if (lastTxnRecord->getState()) {
if (lastTxnRecord->getTxnRetryCounter().has_value()) {
- uassert(
- ErrorCodes::InvalidOptions,
- "TxnRetryCounter is only supported when internal transactions are enabled",
- feature_flags::gFeatureFlagInternalTransactions.isEnabled(
- serverGlobalParams.featureCompatibility));
return *lastTxnRecord->getTxnRetryCounter();
}
return 0;
diff --git a/src/mongo/db/transaction_participant_test.cpp b/src/mongo/db/transaction_participant_test.cpp
index dfcac1a329c..6d2df9f6ec5 100644
--- a/src/mongo/db/transaction_participant_test.cpp
+++ b/src/mongo/db/transaction_participant_test.cpp
@@ -5147,5 +5147,50 @@ TEST_F(ShardTxnParticipantTest, CannotModifyParentLsidOfNonChildSession) {
}
}
+TEST_F(TxnParticipantTest,
+ ThrowIfTxnRetryCounterIsSpecifiedOnStartTransactionWithFeatureFlagDisabled) {
+ MongoDOperationContextSession opCtxSession(opCtx());
+ auto txnParticipant = TransactionParticipant::get(opCtx());
+ ASSERT_THROWS_CODE(txnParticipant.beginOrContinue(opCtx(),
+ {*opCtx()->getTxnNumber(), 1},
+ false /* autocommit */,
+ true /* startTransaction */),
+ AssertionException,
+ ErrorCodes::TxnRetryCounterNotSupported);
+}
+
+TEST_F(ShardTxnParticipantTest,
+ TxnRetryCounterShouldNotThrowIfWeContinueATransactionAfterDisablingFeatureFlag) {
+ // We swap in a new opCtx in order to set a new active txnRetryCounter for this test.
+ auto newClientOwned = getServiceContext()->makeClient("newClient");
+ AlternativeClientRegion acr(newClientOwned);
+ auto newOpCtx = cc().makeOperationContext();
+
+ const auto newSessionId = makeLogicalSessionIdForTest();
+
+ newOpCtx.get()->setLogicalSessionId(newSessionId);
+ newOpCtx.get()->setTxnNumber(20);
+ newOpCtx.get()->setTxnRetryCounter(1);
+ newOpCtx.get()->setInMultiDocumentTransaction();
+ MongoDOperationContextSession newOpCtxSession(newOpCtx.get());
+ auto txnParticipant = TransactionParticipant::get(newOpCtx.get());
+
+ txnParticipant.beginOrContinue(newOpCtx.get(),
+ {*newOpCtx.get()->getTxnNumber(), 1},
+ false /* autocommit */,
+ true /* startTransaction */);
+
+ // We need to unstash and stash transaction resources so that we can continue the transaction in
+ // the following statements.
+ txnParticipant.unstashTransactionResources(newOpCtx.get(), "insert");
+ txnParticipant.stashTransactionResources(newOpCtx.get());
+
+ RAIIServerParameterControllerForTest controller{"featureFlagInternalTransactions", false};
+
+ txnParticipant.beginOrContinue(newOpCtx.get(),
+ {*newOpCtx.get()->getTxnNumber(), 1},
+ false /* autocommit */,
+ boost::none /* startTransaction */);
+}
} // namespace
} // namespace mongo
diff --git a/src/mongo/s/transaction_router.cpp b/src/mongo/s/transaction_router.cpp
index fc9b2da5255..578d979b10c 100644
--- a/src/mongo/s/transaction_router.cpp
+++ b/src/mongo/s/transaction_router.cpp
@@ -475,8 +475,11 @@ BSONObj TransactionRouter::Participant::attachTxnFieldsIfNeeded(
if (feature_flags::gFeatureFlagInternalTransactions.isEnabled(
serverGlobalParams.featureCompatibility)) {
- newCmd.append(OperationSessionInfoFromClient::kTxnRetryCounterFieldName,
- *sharedOptions.txnNumberAndRetryCounter.getTxnRetryCounter());
+ if (auto txnRetryCounter = sharedOptions.txnNumberAndRetryCounter.getTxnRetryCounter();
+ txnRetryCounter && !isDefaultTxnRetryCounter(*txnRetryCounter)) {
+ newCmd.append(OperationSessionInfoFromClient::kTxnRetryCounterFieldName,
+ *sharedOptions.txnNumberAndRetryCounter.getTxnRetryCounter());
+ }
}
return newCmd.obj();
diff --git a/src/mongo/s/transaction_router_test.cpp b/src/mongo/s/transaction_router_test.cpp
index 6d27a481839..f0d0ab36264 100644
--- a/src/mongo/s/transaction_router_test.cpp
+++ b/src/mongo/s/transaction_router_test.cpp
@@ -247,8 +247,7 @@ TEST_F(TransactionRouterTestWithDefaultSession,
<< "snapshot"
<< "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
<< "startTransaction" << true << "coordinator" << true
- << "autocommit" << false << "txnNumber" << txnNum
- << "txnRetryCounter" << 0);
+ << "autocommit" << false << "txnNumber" << txnNum);
{
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
@@ -266,7 +265,7 @@ TEST_F(TransactionRouterTestWithDefaultSession,
ASSERT_BSONOBJ_EQ(BSON("update"
<< "test"
<< "coordinator" << true << "autocommit" << false << "txnNumber"
- << txnNum << "txnRetryCounter" << 0),
+ << txnNum),
newCmd);
}
}
@@ -287,8 +286,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, BasicStartTxnWithAtClusterTime)
<< "snapshot"
<< "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
<< "startTransaction" << true << "coordinator" << true
- << "autocommit" << false << "txnNumber" << txnNum
- << "txnRetryCounter" << 0);
+ << "autocommit" << false << "txnNumber" << txnNum);
{
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
@@ -306,7 +304,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, BasicStartTxnWithAtClusterTime)
ASSERT_BSONOBJ_EQ(BSON("update"
<< "test"
<< "coordinator" << true << "autocommit" << false << "txnNumber"
- << txnNum << "txnRetryCounter" << 0),
+ << txnNum),
newCmd);
}
}
@@ -339,8 +337,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, NewParticipantMustAttachTxnAndRe
<< "snapshot"
<< "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
<< "startTransaction" << true << "coordinator" << true
- << "autocommit" << false << "txnNumber" << txnNum
- << "txnRetryCounter" << 0);
+ << "autocommit" << false << "txnNumber" << txnNum);
{
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
@@ -358,7 +355,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, NewParticipantMustAttachTxnAndRe
ASSERT_BSONOBJ_EQ(BSON("update"
<< "test"
<< "coordinator" << true << "autocommit" << false << "txnNumber"
- << txnNum << "txnRetryCounter" << 0),
+ << txnNum),
newCmd);
}
@@ -369,7 +366,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, NewParticipantMustAttachTxnAndRe
<< "snapshot"
<< "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
<< "startTransaction" << true << "autocommit" << false << "txnNumber"
- << txnNum << "txnRetryCounter" << 0);
+ << txnNum);
{
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
@@ -386,8 +383,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, NewParticipantMustAttachTxnAndRe
<< "test"));
ASSERT_BSONOBJ_EQ(BSON("update"
<< "test"
- << "autocommit" << false << "txnNumber" << txnNum
- << "txnRetryCounter" << 0),
+ << "autocommit" << false << "txnNumber" << txnNum),
newCmd);
}
}
@@ -413,8 +409,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, StartingNewTxnShouldClearState)
<< "snapshot"
<< "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
<< "startTransaction" << true << "coordinator" << true
- << "autocommit" << false << "txnNumber" << txnNum
- << "txnRetryCounter" << 0),
+ << "autocommit" << false << "txnNumber" << txnNum),
newCmd);
}
@@ -431,8 +426,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, StartingNewTxnShouldClearState)
<< "snapshot"
<< "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
<< "startTransaction" << true << "coordinator" << true
- << "autocommit" << false << "txnNumber" << txnNum2
- << "txnRetryCounter" << 0);
+ << "autocommit" << false << "txnNumber" << txnNum2);
{
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
@@ -454,6 +448,46 @@ DEATH_TEST_F(TransactionRouterTestWithDefaultSession,
TransactionRouter::TransactionActions::kStart);
}
+TEST_F(TransactionRouterTestWithDefaultSession,
+ DoNotAttachTxnRetryCounterIfTxnRetryCounterIsDefault) {
+ TxnNumber txnNum{3};
+
+ auto txnRouter = TransactionRouter::get(operationContext());
+ txnRouter.beginOrContinueTxn(operationContext(),
+ TxnNumberAndRetryCounter(txnNum, 0),
+ TransactionRouter::TransactionActions::kStart);
+ txnRouter.setDefaultAtClusterTime(operationContext());
+ txnRouter.attachTxnFieldsIfNeeded(operationContext(), shard1, {});
+ txnRouter.processParticipantResponse(operationContext(), shard1, kOkReadOnlyFalseResponse);
+
+ auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
+ shard1,
+ BSON("insert"
+ << "test"
+ << "txnNumber" << txnNum));
+ ASSERT_EQ(newCmd.hasField("txnRetryCounter"), false);
+}
+
+TEST_F(TransactionRouterTestWithDefaultSession,
+ AttachTxnRetryCounterIfTxnRetryCounterIsNotDefault) {
+ TxnNumber txnNum{3};
+
+ auto txnRouter = TransactionRouter::get(operationContext());
+ txnRouter.beginOrContinueTxn(operationContext(),
+ TxnNumberAndRetryCounter(txnNum, 1),
+ TransactionRouter::TransactionActions::kStart);
+ txnRouter.setDefaultAtClusterTime(operationContext());
+ txnRouter.attachTxnFieldsIfNeeded(operationContext(), shard1, {});
+ txnRouter.processParticipantResponse(operationContext(), shard1, kOkReadOnlyFalseResponse);
+
+ auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
+ shard1,
+ BSON("insert"
+ << "test"
+ << "txnNumber" << txnNum));
+ ASSERT_EQ(newCmd.hasField("txnRetryCounter"), true);
+}
+
TEST_F(TransactionRouterTestWithDefaultSession, NotAttachTxnRetryCounterIfFeatureFlagIsNotEnabled) {
RAIIServerParameterControllerForTest controller{"featureFlagInternalTransactions", false};
@@ -498,7 +532,7 @@ TEST_F(TransactionRouterTestWithDefaultSession,
TxnNumberAndRetryCounter(txnNum, 0),
TransactionRouter::TransactionActions::kStart);
txnRouter.setDefaultAtClusterTime(operationContext());
- ASSERT_BSONOBJ_EQ(expectedCmd.addFields(BSON("txnRetryCounter" << 0)),
+ ASSERT_BSONOBJ_EQ(expectedCmd,
txnRouter.attachTxnFieldsIfNeeded(operationContext(),
shard1,
BSON("insert"
@@ -532,7 +566,7 @@ TEST_F(TransactionRouterTestWithDefaultSession,
TxnNumberAndRetryCounter(txnNum, 0),
TransactionRouter::TransactionActions::kStart);
txnRouter.setDefaultAtClusterTime(operationContext());
- ASSERT_BSONOBJ_EQ(expectedCmd.addFields(BSON("txnRetryCounter" << 0)),
+ ASSERT_BSONOBJ_EQ(expectedCmd,
txnRouter.attachTxnFieldsIfNeeded(operationContext(),
shard1,
BSON("insert"
@@ -1042,7 +1076,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, DoesNotAttachTxnNumIfAlreadyTher
<< "snapshot"
<< "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
<< "startTransaction" << true << "coordinator" << true
- << "autocommit" << false << "txnRetryCounter" << 0);
+ << "autocommit" << false);
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
shard1,
@@ -1094,8 +1128,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, AttachTxnValidatesReadConcernIfA
<< "snapshot"
<< "atClusterTime" << kInMemoryLogicalTime.asTimestamp())
<< "startTransaction" << true << "coordinator" << true
- << "autocommit" << false << "txnNumber" << txnNum
- << "txnRetryCounter" << 0),
+ << "autocommit" << false << "txnNumber" << txnNum),
newCmd);
}
}
@@ -1197,7 +1230,7 @@ TEST_F(TransactionRouterTestWithDefaultSession, PassesThroughEmptyReadConcernToP
<< "test"
<< "readConcern" << BSONObj() << "startTransaction" << true
<< "coordinator" << true << "autocommit" << false << "txnNumber"
- << txnNum << "txnRetryCounter" << 0);
+ << txnNum);
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
shard1,
@@ -1225,8 +1258,7 @@ TEST_F(TransactionRouterTestWithDefaultSession,
<< "readConcern"
<< BSON("afterClusterTime" << kAfterClusterTime.asTimestamp())
<< "startTransaction" << true << "coordinator" << true
- << "autocommit" << false << "txnNumber" << txnNum
- << "txnRetryCounter" << 0);
+ << "autocommit" << false << "txnNumber" << txnNum);
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(operationContext(),
shard1,
@@ -1310,8 +1342,9 @@ void checkSessionDetails(const BSONObj& cmdObj,
ASSERT(osi.getAutocommit());
ASSERT_FALSE(*osi.getAutocommit());
- ASSERT(osi.getTxnRetryCounter());
- ASSERT_EQ(txnRetryCounter, *osi.getTxnRetryCounter());
+ if (osi.getTxnRetryCounter()) {
+ ASSERT_EQ(txnRetryCounter, *osi.getTxnRetryCounter());
+ }
if (isCoordinator) {
ASSERT_EQ(*isCoordinator, cmdObj["coordinator"].trueValue());
@@ -5639,6 +5672,5 @@ TEST_F(TransactionRouterMetricsTest, IsTrackingOverIfTxnImplicitlyAborted) {
implicitAbortInProgress();
ASSERT(txnRouter().isTrackingOver());
}
-
} // unnamed namespace
} // namespace mongo