summaryrefslogtreecommitdiff
path: root/src/mongo/s/transaction_router_test.cpp
diff options
context:
space:
mode:
authorEsha Maharishi <esha.maharishi@mongodb.com>2019-05-14 13:46:43 -0400
committerEsha Maharishi <esha.maharishi@mongodb.com>2019-05-16 14:07:44 -0400
commit8ab4e38d8ad3eb6901f79dfbd67b8b2cf6646c2d (patch)
tree17d980977fa6210c815e32d463b5c7ea8393cdc2 /src/mongo/s/transaction_router_test.cpp
parent102f13ddd63f90d00dd8489280522a94d49ca5e9 (diff)
downloadmongo-8ab4e38d8ad3eb6901f79dfbd67b8b2cf6646c2d.tar.gz
SERVER-40001 abortTransaction through mongos does not properly set error labels
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) {