summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSiyuan Zhou <siyuan.zhou@mongodb.com>2018-05-18 00:52:13 -0400
committerSiyuan Zhou <siyuan.zhou@mongodb.com>2018-05-18 16:52:00 -0400
commit59239fadc01de57490a56990ee125a1ea44fb126 (patch)
treef2cd4cd857784b2463bbe1236f561275e46bf132
parente30e606afca325c596a35763f6c874476ab7a1b7 (diff)
downloadmongo-59239fadc01de57490a56990ee125a1ea44fb126.tar.gz
SERVER-34924 Treat LockTimeout as transient transaction error
-rw-r--r--jstests/replsets/transient_txn_error_labels.js41
-rw-r--r--src/mongo/db/service_entry_point_common.cpp1
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"));