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.cpp110
1 files changed, 107 insertions, 3 deletions
diff --git a/src/mongo/s/transaction_router_test.cpp b/src/mongo/s/transaction_router_test.cpp
index d6dd2c2fe36..da7a80b5130 100644
--- a/src/mongo/s/transaction_router_test.cpp
+++ b/src/mongo/s/transaction_router_test.cpp
@@ -52,6 +52,8 @@ using executor::RemoteCommandRequest;
const BSONObj kOkReadOnlyFalseResponse = BSON("ok" << 1 << "readOnly" << false);
const BSONObj kOkReadOnlyTrueResponse = BSON("ok" << 1 << "readOnly" << true);
+const BSONObj kNoSuchTransactionResponse =
+ BSON("ok" << 0 << "code" << ErrorCodes::NoSuchTransaction);
class TransactionRouterTest : public ShardingTestFixture {
protected:
@@ -1869,10 +1871,10 @@ TEST_F(TransactionRouterTest, AbortForSingleParticipant) {
});
auto response = future.default_timed_get();
- ASSERT_FALSE(response.empty());
+ ASSERT_BSONOBJ_EQ(kOkReadOnlyFalseResponse, response);
}
-TEST_F(TransactionRouterTest, AbortForMultipleParticipants) {
+TEST_F(TransactionRouterTest, AbortForMultipleParticipantsAllReturnSuccess) {
LogicalSessionId lsid(makeLogicalSessionIdForTest());
TxnNumber txnNum{3};
@@ -1912,7 +1914,109 @@ TEST_F(TransactionRouterTest, AbortForMultipleParticipants) {
}
auto response = future.default_timed_get();
- ASSERT_FALSE(response.empty());
+ ASSERT_BSONOBJ_EQ(kOkReadOnlyFalseResponse, response);
+}
+
+TEST_F(TransactionRouterTest, AbortForMultipleParticipantsSomeReturnNoSuchTransaction) {
+ LogicalSessionId lsid(makeLogicalSessionIdForTest());
+ TxnNumber txnNum{3};
+
+ auto opCtx = operationContext();
+ opCtx->setLogicalSessionId(lsid);
+ opCtx->setTxnNumber(txnNum);
+
+ RouterOperationContextSession scopedSession(opCtx);
+ auto txnRouter = TransactionRouter::get(opCtx);
+
+ txnRouter->beginOrContinueTxn(opCtx, txnNum, TransactionRouter::TransactionActions::kStart);
+ txnRouter->setDefaultAtClusterTime(operationContext());
+ txnRouter->attachTxnFieldsIfNeeded(shard1, {});
+ txnRouter->attachTxnFieldsIfNeeded(shard2, {});
+ txnRouter->attachTxnFieldsIfNeeded(shard3, {});
+ txnRouter->processParticipantResponse(shard1, kOkReadOnlyFalseResponse);
+ txnRouter->processParticipantResponse(shard2, kOkReadOnlyFalseResponse);
+ txnRouter->processParticipantResponse(shard3, kOkReadOnlyFalseResponse);
+
+ auto future = launchAsync([&] { return txnRouter->abortTransaction(operationContext()); });
+
+ std::map<HostAndPort, boost::optional<bool>> targets = {
+ {hostAndPort1, true}, {hostAndPort2, {}}, {hostAndPort3, {}}};
+
+ int count = 0;
+ while (!targets.empty()) {
+ onCommandForPoolExecutor([&](const RemoteCommandRequest& request) {
+ auto target = targets.find(request.target);
+ ASSERT(target != targets.end());
+ ASSERT_EQ("admin", request.dbname);
+
+ auto cmdName = request.cmdObj.firstElement().fieldNameStringData();
+ ASSERT_EQ(cmdName, "abortTransaction");
+
+ checkSessionDetails(request.cmdObj, lsid, txnNum, target->second);
+
+ targets.erase(request.target);
+
+ // The middle response is NoSuchTransaction, the rest are success.
+ return (count == 1 ? kNoSuchTransactionResponse : kOkReadOnlyFalseResponse);
+ });
+ count++;
+ }
+
+ auto response = future.default_timed_get();
+ ASSERT_BSONOBJ_EQ(kNoSuchTransactionResponse, response);
+}
+
+TEST_F(TransactionRouterTest, AbortForMultipleParticipantsSomeReturnNetworkError) {
+ LogicalSessionId lsid(makeLogicalSessionIdForTest());
+ TxnNumber txnNum{3};
+
+ auto opCtx = operationContext();
+ opCtx->setLogicalSessionId(lsid);
+ opCtx->setTxnNumber(txnNum);
+
+ RouterOperationContextSession scopedSession(opCtx);
+ auto txnRouter = TransactionRouter::get(opCtx);
+
+ txnRouter->beginOrContinueTxn(opCtx, txnNum, TransactionRouter::TransactionActions::kStart);
+ txnRouter->setDefaultAtClusterTime(operationContext());
+ txnRouter->attachTxnFieldsIfNeeded(shard1, {});
+ txnRouter->attachTxnFieldsIfNeeded(shard2, {});
+ txnRouter->attachTxnFieldsIfNeeded(shard3, {});
+ txnRouter->processParticipantResponse(shard1, kOkReadOnlyFalseResponse);
+ txnRouter->processParticipantResponse(shard2, kOkReadOnlyFalseResponse);
+ txnRouter->processParticipantResponse(shard3, kOkReadOnlyFalseResponse);
+
+ auto future = launchAsync([&] { return txnRouter->abortTransaction(operationContext()); });
+
+ std::map<HostAndPort, boost::optional<bool>> targets = {
+ {hostAndPort1, true}, {hostAndPort2, {}}, {hostAndPort3, {}}};
+
+ int count = 0;
+ while (!targets.empty()) {
+ onCommandForPoolExecutor([&](const RemoteCommandRequest& request) -> StatusWith<BSONObj> {
+ auto target = targets.find(request.target);
+ ASSERT(target != targets.end());
+ ASSERT_EQ("admin", request.dbname);
+
+ auto cmdName = request.cmdObj.firstElement().fieldNameStringData();
+ ASSERT_EQ(cmdName, "abortTransaction");
+
+ checkSessionDetails(request.cmdObj, lsid, txnNum, target->second);
+
+ targets.erase(request.target);
+
+ // The middle response is a "network error", the rest are success. Use InternalError as
+ // the "network error" because the server will retry three times on actual network
+ // errors; this just skips the retries.
+ if (count == 1) {
+ return Status{ErrorCodes::InternalError, "dummy"};
+ }
+ return kOkReadOnlyFalseResponse;
+ });
+ count++;
+ }
+
+ ASSERT_THROWS_CODE(future.default_timed_get(), AssertionException, ErrorCodes::InternalError);
}
TEST_F(TransactionRouterTestWithDefaultSession, OnViewResolutionErrorClearsAllNewParticipants) {