diff options
author | Siyuan Zhou <siyuan.zhou@mongodb.com> | 2018-05-18 00:52:13 -0400 |
---|---|---|
committer | Siyuan Zhou <siyuan.zhou@mongodb.com> | 2018-05-18 16:52:00 -0400 |
commit | 59239fadc01de57490a56990ee125a1ea44fb126 (patch) | |
tree | f2cd4cd857784b2463bbe1236f561275e46bf132 | |
parent | e30e606afca325c596a35763f6c874476ab7a1b7 (diff) | |
download | mongo-59239fadc01de57490a56990ee125a1ea44fb126.tar.gz |
SERVER-34924 Treat LockTimeout as transient transaction error
-rw-r--r-- | jstests/replsets/transient_txn_error_labels.js | 41 | ||||
-rw-r--r-- | src/mongo/db/service_entry_point_common.cpp | 1 |
2 files changed, 39 insertions, 3 deletions
diff --git a/jstests/replsets/transient_txn_error_labels.js b/jstests/replsets/transient_txn_error_labels.js index a7398a70d14..5484a708452 100644 --- a/jstests/replsets/transient_txn_error_labels.js +++ b/jstests/replsets/transient_txn_error_labels.js @@ -4,6 +4,7 @@ "use strict"; load("jstests/libs/write_concern_util.js"); + load("jstests/libs/parallelTester.js"); // For ScopedThread. const dbName = "test"; const collName = "no_error_labels_outside_txn"; @@ -16,6 +17,7 @@ const primary = rst.getPrimary(); const secondary = rst.getSecondary(); const testDB = primary.getDB(dbName); + const adminDB = testDB.getSiblingDB("admin"); const testColl = testDB.getCollection(collName); const sessionOptions = {causalConsistency: false}; @@ -138,11 +140,44 @@ assert(!res.hasOwnProperty("errorLabels")); assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"})); - // The fail-point leaves the transaction open so we need to abort it explicitly. - // TODO: after SERVER-34795, endSession() will kill the running transactions. + jsTest.log("LockTimeout should be TransientTransactionError"); + // Start a transaction to hold the DBLock in IX mode so that drop will be blocked. session.startTransaction(); - assert.docEq(sessionColl.find().toArray(), [{"_id": "write-with-write-concern"}]); + assert.commandWorked(sessionColl.insert({_id: "lock-timeout-1"})); + function dropCmdFunc(testData, primaryHost, dbName, collName) { + // Pass the TestData into the new shell so that jsTest.authenticate() can use the correct + // credentials in auth test suites. + TestData = testData; + const primary = new Mongo(primaryHost); + return primary.getDB(dbName).runCommand({drop: collName, writeConcern: {w: "majority"}}); + } + const thread = new ScopedThread(dropCmdFunc, TestData, primary.host, dbName, collName); + thread.start(); + // Wait for the drop to have a pending MODE_X lock on the database. + assert.soon( + function() { + return adminDB + .aggregate([ + {$currentOp: {}}, + {$match: {"command.drop": collName, waitingForLock: true}} + ]) + .itcount() === 1; + }, + function() { + return "Failed to find drop in currentOp output: " + + tojson(adminDB.aggregate([{$currentOp: {}}]).toArray()); + }); + // Start another transaction in a new session, which cannot acquire the database lock in time. + let sessionOther = primary.startSession(sessionOptions); + sessionOther.startTransaction(); + res = sessionOther.getDatabase(dbName).getCollection(collName).insert({_id: "lock-timeout-2"}); + assert.commandFailedWithCode(res, ErrorCodes.LockTimeout); + assert(res instanceof WriteCommandError); + assert.eq(res.errorLabels, ["TransientTransactionError"]); + sessionOther.abortTransaction(); session.abortTransaction(); + thread.join(); + assert.commandWorked(thread.returnData()); session.endSession(); diff --git a/src/mongo/db/service_entry_point_common.cpp b/src/mongo/db/service_entry_point_common.cpp index 05d6f3ef09d..f6cabff5bb1 100644 --- a/src/mongo/db/service_entry_point_common.cpp +++ b/src/mongo/db/service_entry_point_common.cpp @@ -220,6 +220,7 @@ BSONObj getErrorLabels(const boost::optional<OperationSessionInfoFromClient>& se bool isTransientTransactionError = code == ErrorCodes::WriteConflict // || code == ErrorCodes::SnapshotUnavailable // || code == ErrorCodes::NoSuchTransaction // + || code == ErrorCodes::LockTimeout // // Clients can retry a single commitTransaction command, but cannot retry the whole // transaction if commitTransaction fails due to NotMaster. || (isRetryable && (commandName != "commitTransaction")); |