summaryrefslogtreecommitdiff
path: root/src/mongo/s/transaction_router_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/s/transaction_router_test.cpp')
-rw-r--r--src/mongo/s/transaction_router_test.cpp179
1 files changed, 112 insertions, 67 deletions
diff --git a/src/mongo/s/transaction_router_test.cpp b/src/mongo/s/transaction_router_test.cpp
index dd201c37ab3..847ff7d65b2 100644
--- a/src/mongo/s/transaction_router_test.cpp
+++ b/src/mongo/s/transaction_router_test.cpp
@@ -80,7 +80,7 @@ TEST_F(TransactionRouterTest, StartTxnShouldBeAttachedOnlyOnFirstStatementToPart
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
BSONObj expectedNewObj = BSON("insert"
<< "test"
@@ -127,7 +127,7 @@ TEST_F(TransactionRouterTest, BasicStartTxnWithAtClusterTime) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
BSONObj expectedNewObj = BSON("insert"
<< "test"
@@ -184,7 +184,7 @@ TEST_F(TransactionRouterTest, NewParticipantMustAttachTxnAndReadConcern) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
BSONObj expectedNewObj = BSON("insert"
<< "test"
@@ -265,7 +265,7 @@ TEST_F(TransactionRouterTest, StartingNewTxnShouldClearState) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
{
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(shard1,
@@ -291,7 +291,7 @@ TEST_F(TransactionRouterTest, StartingNewTxnShouldClearState) {
TxnNumber txnNum2{5};
txnRouter.beginOrContinueTxn(operationContext(), txnNum2, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
BSONObj expectedNewObj = BSON("insert"
<< "test"
@@ -323,7 +323,7 @@ TEST_F(TransactionRouterTest, FirstParticipantIsCoordinator) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
ASSERT_FALSE(txnRouter.getCoordinatorId());
@@ -346,7 +346,7 @@ TEST_F(TransactionRouterTest, FirstParticipantIsCoordinator) {
TxnNumber txnNum2{5};
txnRouter.beginOrContinueTxn(operationContext(), txnNum2, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
ASSERT_FALSE(txnRouter.getCoordinatorId());
@@ -365,7 +365,7 @@ TEST_F(TransactionRouterTest, DoesNotAttachTxnNumIfAlreadyThere) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
BSONObj expectedNewObj = BSON("insert"
<< "test"
@@ -397,7 +397,7 @@ DEATH_TEST_F(TransactionRouterTest, CrashesIfCmdHasDifferentTxnNumber, "invarian
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
txnRouter.attachTxnFieldsIfNeeded(shard1,
BSON("insert"
@@ -412,7 +412,7 @@ TEST_F(TransactionRouterTest, AttachTxnValidatesReadConcernIfAlreadyOnCmd) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
{
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(shard1,
@@ -446,7 +446,7 @@ TEST_F(TransactionRouterTest, CannotSpecifyReadConcernAfterFirstStatement) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true /* startTransaction */);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
ASSERT_THROWS_CODE(
txnRouter.beginOrContinueTxn(operationContext(), txnNum, false /* startTransaction */),
@@ -461,7 +461,7 @@ TEST_F(TransactionRouterTest, UpconvertToSnapshotIfNoReadConcernLevelGiven) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true /* startTransaction */);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
BSONObj expectedNewObj = BSON("insert"
<< "test"
@@ -494,7 +494,7 @@ TEST_F(TransactionRouterTest, UpconvertToSnapshotIfNoReadConcernLevelButHasAfter
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true /* startTransaction */);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
BSONObj expectedNewObj = BSON("insert"
<< "test"
@@ -597,7 +597,7 @@ TEST_F(TransactionRouterTest, CannotCommitWithoutParticipants) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
ASSERT_THROWS(txnRouter.commitTransaction(operationContext()), AssertionException);
}
@@ -636,7 +636,7 @@ TEST_F(TransactionRouterTest, SendCommitDirectlyForSingleParticipants) {
auto txnRouter = TransactionRouter::get(opCtx);
txnRouter->beginOrContinueTxn(opCtx, txnNum, true);
- txnRouter->setAtClusterTimeToLatestTime(operationContext());
+ txnRouter->setDefaultAtClusterTime(operationContext());
txnRouter->attachTxnFieldsIfNeeded(shard1, {});
auto future = launchAsync([&] { txnRouter->commitTransaction(operationContext()); });
@@ -668,7 +668,7 @@ TEST_F(TransactionRouterTest, SendPrepareAndCoordinateCommitForMultipleParticipa
auto txnRouter = TransactionRouter::get(opCtx);
txnRouter->beginOrContinueTxn(opCtx, txnNum, true);
- txnRouter->setAtClusterTimeToLatestTime(operationContext());
+ txnRouter->setDefaultAtClusterTime(operationContext());
txnRouter->attachTxnFieldsIfNeeded(shard1, {});
txnRouter->attachTxnFieldsIfNeeded(shard2, {});
@@ -716,7 +716,7 @@ TEST_F(TransactionRouterTest, SnapshotErrorsResetAtClusterTime) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
BSONObj expectedReadConcern = BSON("level"
<< "snapshot"
@@ -738,7 +738,7 @@ TEST_F(TransactionRouterTest, SnapshotErrorsResetAtClusterTime) {
// Simulate a snapshot error.
txnRouter.onSnapshotError();
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
expectedReadConcern = BSON("level"
<< "snapshot"
@@ -753,15 +753,13 @@ TEST_F(TransactionRouterTest, SnapshotErrorsResetAtClusterTime) {
}
}
-TEST_F(TransactionRouterTest, CannotChangeAtClusterTimeWithoutSnapshotError) {
+TEST_F(TransactionRouterTest, CannotChangeAtClusterTimeAfterStatementThatSelectedIt) {
TxnNumber txnNum{3};
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
-
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
BSONObj expectedReadConcern = BSON("level"
<< "snapshot"
@@ -775,11 +773,18 @@ TEST_F(TransactionRouterTest, CannotChangeAtClusterTimeWithoutSnapshotError) {
ASSERT_BSONOBJ_EQ(expectedReadConcern, newCmd["readConcern"].Obj());
}
- LogicalTime laterTime(Timestamp(1000, 1));
- ASSERT_GT(laterTime, kInMemoryLogicalTime);
- LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(laterTime);
+ // Changing the atClusterTime during the statement that selected it is allowed.
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ LogicalTime laterTimeSameStmt(Timestamp(100, 1));
+ ASSERT_GT(laterTimeSameStmt, kInMemoryLogicalTime);
+ LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(laterTimeSameStmt);
+
+ txnRouter.setDefaultAtClusterTime(operationContext());
+
+ expectedReadConcern = BSON("level"
+ << "snapshot"
+ << "atClusterTime"
+ << laterTimeSameStmt.asTimestamp());
{
auto newCmd = txnRouter.attachTxnFieldsIfNeeded(shard2,
@@ -787,6 +792,24 @@ TEST_F(TransactionRouterTest, CannotChangeAtClusterTimeWithoutSnapshotError) {
<< "test"));
ASSERT_BSONOBJ_EQ(expectedReadConcern, newCmd["readConcern"].Obj());
}
+
+ // Later statements cannot change atClusterTime.
+
+ repl::ReadConcernArgs::get(operationContext()) = repl::ReadConcernArgs();
+ txnRouter.beginOrContinueTxn(operationContext(), txnNum, false);
+
+ LogicalTime laterTimeNewStmt(Timestamp(1000, 1));
+ ASSERT_GT(laterTimeNewStmt, laterTimeSameStmt);
+ LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(laterTimeNewStmt);
+
+ txnRouter.setDefaultAtClusterTime(operationContext());
+
+ {
+ auto newCmd = txnRouter.attachTxnFieldsIfNeeded(shard3,
+ BSON("insert"
+ << "test"));
+ ASSERT_BSONOBJ_EQ(expectedReadConcern, newCmd["readConcern"].Obj());
+ }
}
TEST_F(TransactionRouterTest, SnapshotErrorsClearsAllParticipants) {
@@ -795,7 +818,7 @@ TEST_F(TransactionRouterTest, SnapshotErrorsClearsAllParticipants) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
// Successfully start a transaction on two shards, selecting one as the coordinator.
@@ -810,7 +833,7 @@ TEST_F(TransactionRouterTest, SnapshotErrorsClearsAllParticipants) {
txnRouter.onSnapshotError();
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
ASSERT_FALSE(txnRouter.getCoordinatorId());
@@ -842,12 +865,12 @@ TEST_F(TransactionRouterTest, OnSnapshotErrorThrowsAfterFirstCommand) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
// Should not throw.
txnRouter.onSnapshotError();
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
repl::ReadConcernArgs::get(operationContext()) = repl::ReadConcernArgs();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, false);
@@ -866,7 +889,7 @@ TEST_F(TransactionRouterTest, ParticipantsRememberStmtIdCreatedAt) {
TxnNumber txnNum{3};
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
// Transaction 1 contacts shard1 and shard2 during the first command, then shard3 in the second
// command.
@@ -895,7 +918,7 @@ TEST_F(TransactionRouterTest, ParticipantsRememberStmtIdCreatedAt) {
repl::ReadConcernArgs(repl::ReadConcernLevel::kSnapshotReadConcern);
TxnNumber txnNum2{5};
txnRouter.beginOrContinueTxn(operationContext(), txnNum2, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
txnRouter.attachTxnFieldsIfNeeded(shard3, {});
txnRouter.attachTxnFieldsIfNeeded(shard2, {});
@@ -916,7 +939,7 @@ TEST_F(TransactionRouterTest, AllParticipantsAndCoordinatorClearedOnStaleErrorOn
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
// Start a transaction on two shards, selecting one as the coordinator, but simulate a
// re-targeting error from at least one of them.
@@ -957,7 +980,7 @@ TEST_F(TransactionRouterTest, OnlyNewlyCreatedParticipantsClearedOnStaleError) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
// First statement successfully targets one shard, selecing it as the coordinator.
@@ -983,44 +1006,47 @@ TEST_F(TransactionRouterTest, OnlyNewlyCreatedParticipantsClearedOnStaleError) {
ASSERT_TRUE(txnRouter.attachTxnFieldsIfNeeded(shard3, {})["startTransaction"].trueValue());
}
-TEST_F(TransactionRouterTest, RetryOnStaleErrorCannotPickNewAtClusterTime) {
+TEST_F(TransactionRouterTest, RetriesCannotPickNewAtClusterTimeOnStatementAfterSelected) {
TxnNumber txnNum{3};
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ // First statement selects an atClusterTime.
- BSONObj expectedReadConcern = BSON("level"
- << "snapshot"
- << "atClusterTime"
- << kInMemoryLogicalTime.asTimestamp());
+ txnRouter.setDefaultAtClusterTime(operationContext());
- {
- auto newCmd = txnRouter.attachTxnFieldsIfNeeded(shard1,
- BSON("find"
- << "test"));
- ASSERT_BSONOBJ_EQ(expectedReadConcern, newCmd["readConcern"].Obj());
- }
+ // A later statement retries on a stale version error and a view resolution error and cannot
+ // change the atClusterTime.
+
+ repl::ReadConcernArgs::get(operationContext()) = repl::ReadConcernArgs();
+ txnRouter.beginOrContinueTxn(operationContext(), txnNum, false);
- // Advance the latest time in the logical clock, simulate a stale config/db error, and verify
- // the retry attempt cannot pick a new atClusterTime.
LogicalTime laterTime(Timestamp(1000, 1));
ASSERT_GT(laterTime, kInMemoryLogicalTime);
LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(laterTime);
txnRouter.onStaleShardOrDbError("find");
+ txnRouter.setDefaultAtClusterTime(operationContext());
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ BSONObj expectedReadConcern = BSON("level"
+ << "snapshot"
+ << "atClusterTime"
+ << kInMemoryLogicalTime.asTimestamp());
- {
- auto newCmd = txnRouter.attachTxnFieldsIfNeeded(shard1,
- BSON("find"
- << "test"));
- ASSERT_BSONOBJ_EQ(expectedReadConcern, newCmd["readConcern"].Obj());
- }
+ auto newCmd = txnRouter.attachTxnFieldsIfNeeded(shard1,
+ BSON("find"
+ << "test"));
+ ASSERT_BSONOBJ_EQ(expectedReadConcern, newCmd["readConcern"].Obj());
+
+ txnRouter.onViewResolutionError();
+ txnRouter.setDefaultAtClusterTime(operationContext());
+
+ newCmd = txnRouter.attachTxnFieldsIfNeeded(shard1,
+ BSON("find"
+ << "test"));
+ ASSERT_BSONOBJ_EQ(expectedReadConcern, newCmd["readConcern"].Obj());
}
TEST_F(TransactionRouterTest, WritesCanOnlyBeRetriedIfFirstOverallCommand) {
@@ -1032,7 +1058,7 @@ TEST_F(TransactionRouterTest, WritesCanOnlyBeRetriedIfFirstOverallCommand) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
txnRouter.attachTxnFieldsIfNeeded(shard1, {});
@@ -1071,7 +1097,7 @@ TEST_F(TransactionRouterTest, AbortThrowsIfNoParticipants) {
auto txnRouter = TransactionRouter::get(opCtx);
txnRouter->beginOrContinueTxn(opCtx, txnNum, true);
- txnRouter->setAtClusterTimeToLatestTime(operationContext());
+ txnRouter->setDefaultAtClusterTime(operationContext());
ASSERT_THROWS_CODE(
txnRouter->abortTransaction(opCtx), DBException, ErrorCodes::NoSuchTransaction);
@@ -1089,7 +1115,7 @@ TEST_F(TransactionRouterTest, AbortForSingleParticipant) {
auto txnRouter = TransactionRouter::get(opCtx);
txnRouter->beginOrContinueTxn(opCtx, txnNum, true);
- txnRouter->setAtClusterTimeToLatestTime(operationContext());
+ txnRouter->setDefaultAtClusterTime(operationContext());
txnRouter->attachTxnFieldsIfNeeded(shard1, {});
auto future = launchAsync([&] { return txnRouter->abortTransaction(operationContext()); });
@@ -1122,7 +1148,7 @@ TEST_F(TransactionRouterTest, AbortForMultipleParticipants) {
auto txnRouter = TransactionRouter::get(opCtx);
txnRouter->beginOrContinueTxn(opCtx, txnNum, true);
- txnRouter->setAtClusterTimeToLatestTime(operationContext());
+ txnRouter->setDefaultAtClusterTime(operationContext());
txnRouter->attachTxnFieldsIfNeeded(shard1, {});
txnRouter->attachTxnFieldsIfNeeded(shard2, {});
@@ -1162,7 +1188,7 @@ TEST_F(TransactionRouterTest, OnViewResolutionErrorClearsAllNewParticipants) {
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
// One shard is targeted by the first statement.
auto firstShardCmd = txnRouter.attachTxnFieldsIfNeeded(shard1, {});
@@ -1212,7 +1238,7 @@ TEST_F(TransactionRouterTest, ImplicitAbortIsNoopWithNoParticipants) {
auto txnRouter = TransactionRouter::get(opCtx);
txnRouter->beginOrContinueTxn(opCtx, txnNum, true);
- txnRouter->setAtClusterTimeToLatestTime(operationContext());
+ txnRouter->setDefaultAtClusterTime(operationContext());
// Should not throw.
txnRouter->implicitlyAbortTransaction(opCtx);
@@ -1230,7 +1256,7 @@ TEST_F(TransactionRouterTest, ImplicitAbortForSingleParticipant) {
auto txnRouter = TransactionRouter::get(opCtx);
txnRouter->beginOrContinueTxn(opCtx, txnNum, true);
- txnRouter->setAtClusterTimeToLatestTime(operationContext());
+ txnRouter->setDefaultAtClusterTime(operationContext());
txnRouter->attachTxnFieldsIfNeeded(shard1, {});
auto future =
@@ -1263,7 +1289,7 @@ TEST_F(TransactionRouterTest, ImplicitAbortForMultipleParticipants) {
auto txnRouter = TransactionRouter::get(opCtx);
txnRouter->beginOrContinueTxn(opCtx, txnNum, true);
- txnRouter->setAtClusterTimeToLatestTime(operationContext());
+ txnRouter->setDefaultAtClusterTime(operationContext());
txnRouter->attachTxnFieldsIfNeeded(shard1, {});
txnRouter->attachTxnFieldsIfNeeded(shard2, {});
@@ -1309,7 +1335,7 @@ TEST_F(TransactionRouterTest, ImplicitAbortIgnoresErrors) {
auto txnRouter = TransactionRouter::get(opCtx);
txnRouter->beginOrContinueTxn(opCtx, txnNum, true);
- txnRouter->setAtClusterTimeToLatestTime(operationContext());
+ txnRouter->setDefaultAtClusterTime(operationContext());
txnRouter->attachTxnFieldsIfNeeded(shard1, {});
auto future =
@@ -1337,7 +1363,7 @@ TEST_F(TransactionRouterTest, ContinuingTransactionPlacesItsReadConcernOnOpCtx)
TransactionRouter txnRouter({});
txnRouter.checkOut();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, true);
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
repl::ReadConcernArgs::get(operationContext()) = repl::ReadConcernArgs();
txnRouter.beginOrContinueTxn(operationContext(), txnNum, false);
@@ -1359,7 +1385,7 @@ TEST_F(TransactionRouterTest, SubsequentStatementCanSelectAtClusterTimeIfNotSele
txnRouter.beginOrContinueTxn(operationContext(), txnNum, false);
// Subsequent statement does select an atClusterTime and does target a participant.
- txnRouter.setAtClusterTimeToLatestTime(operationContext());
+ txnRouter.setDefaultAtClusterTime(operationContext());
BSONObj expectedReadConcern = BSON("level"
<< "snapshot"
@@ -1370,7 +1396,26 @@ TEST_F(TransactionRouterTest, SubsequentStatementCanSelectAtClusterTimeIfNotSele
BSON("insert"
<< "test"));
ASSERT_BSONOBJ_EQ(expectedReadConcern, newCmd["readConcern"].Obj());
+
+ // The next statement cannot change the atClusterTime.
+
+ repl::ReadConcernArgs::get(operationContext()) = repl::ReadConcernArgs();
+ txnRouter.beginOrContinueTxn(operationContext(), txnNum, false);
+
+ LogicalTime laterTimeSameStmt(Timestamp(100, 1));
+ ASSERT_GT(laterTimeSameStmt, kInMemoryLogicalTime);
+ LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(laterTimeSameStmt);
+
+ txnRouter.setDefaultAtClusterTime(operationContext());
+
+ newCmd = txnRouter.attachTxnFieldsIfNeeded(shard2,
+ BSON("insert"
+ << "test"));
+ ASSERT_BSONOBJ_EQ(expectedReadConcern, newCmd["readConcern"].Obj());
}
+// TODO SERVER-37630: Verify transactions with majority level read concern don't have and cannot
+// select an atClusterTime once they are allowed.
+
} // unnamed namespace
} // namespace mongo