summaryrefslogtreecommitdiff
path: root/jstests/replsets/transient_txn_error_labels.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/replsets/transient_txn_error_labels.js')
-rw-r--r--jstests/replsets/transient_txn_error_labels.js474
1 files changed, 235 insertions, 239 deletions
diff --git a/jstests/replsets/transient_txn_error_labels.js b/jstests/replsets/transient_txn_error_labels.js
index defea4c774b..0a886856beb 100644
--- a/jstests/replsets/transient_txn_error_labels.js
+++ b/jstests/replsets/transient_txn_error_labels.js
@@ -1,244 +1,240 @@
// Test TransientTransactionErrors error label in transactions.
// @tags: [uses_transactions]
(function() {
- "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";
-
- // We are testing coordinateCommitTransaction, which requires the nodes to be started with
- // --shardsvr.
- const st = new ShardingTest(
- {config: 1, mongos: 1, shards: {rs0: {nodes: [{}, {rsConfig: {priority: 0}}]}}});
- const primary = st.rs0.getPrimary();
- const secondary = st.rs0.getSecondary();
-
- const testDB = primary.getDB(dbName);
- const adminDB = testDB.getSiblingDB("admin");
- const testColl = testDB.getCollection(collName);
-
- const sessionOptions = {causalConsistency: false};
- let session = primary.startSession(sessionOptions);
- let sessionDb = session.getDatabase(dbName);
- let sessionColl = sessionDb.getCollection(collName);
- let secondarySession = secondary.startSession(sessionOptions);
- let secondarySessionDb = secondarySession.getDatabase(dbName);
-
- assert.commandWorked(testDB.createCollection(collName, {writeConcern: {w: "majority"}}));
-
- jsTest.log("Insert inside a transaction on secondary should fail but return error labels");
- let txnNumber = 0;
- let res = secondarySessionDb.runCommand({
- insert: collName,
- documents: [{_id: "insert-1"}],
- readConcern: {level: "snapshot"},
- txnNumber: NumberLong(txnNumber),
- startTransaction: true,
- autocommit: false
+"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";
+
+// We are testing coordinateCommitTransaction, which requires the nodes to be started with
+// --shardsvr.
+const st = new ShardingTest(
+ {config: 1, mongos: 1, shards: {rs0: {nodes: [{}, {rsConfig: {priority: 0}}]}}});
+const primary = st.rs0.getPrimary();
+const secondary = st.rs0.getSecondary();
+
+const testDB = primary.getDB(dbName);
+const adminDB = testDB.getSiblingDB("admin");
+const testColl = testDB.getCollection(collName);
+
+const sessionOptions = {
+ causalConsistency: false
+};
+let session = primary.startSession(sessionOptions);
+let sessionDb = session.getDatabase(dbName);
+let sessionColl = sessionDb.getCollection(collName);
+let secondarySession = secondary.startSession(sessionOptions);
+let secondarySessionDb = secondarySession.getDatabase(dbName);
+
+assert.commandWorked(testDB.createCollection(collName, {writeConcern: {w: "majority"}}));
+
+jsTest.log("Insert inside a transaction on secondary should fail but return error labels");
+let txnNumber = 0;
+let res = secondarySessionDb.runCommand({
+ insert: collName,
+ documents: [{_id: "insert-1"}],
+ readConcern: {level: "snapshot"},
+ txnNumber: NumberLong(txnNumber),
+ startTransaction: true,
+ autocommit: false
+});
+assert.commandFailedWithCode(res, ErrorCodes.NotMaster);
+assert.eq(res.errorLabels, ["TransientTransactionError"]);
+
+jsTest.log("Insert outside a transaction on secondary should fail but not return error labels");
+txnNumber++;
+// Insert as a retryable write.
+res = secondarySessionDb.runCommand(
+ {insert: collName, documents: [{_id: "insert-1"}], txnNumber: NumberLong(txnNumber)});
+
+assert.commandFailedWithCode(res, ErrorCodes.NotMaster);
+assert(!res.hasOwnProperty("errorLabels"));
+secondarySession.endSession();
+
+jsTest.log("failCommand should be able to return errors with TransientTransactionError");
+assert.commandWorked(testDB.adminCommand({
+ configureFailPoint: "failCommand",
+ mode: "alwaysOn",
+ data: {errorCode: ErrorCodes.WriteConflict, failCommands: ["insert"]}
+}));
+session.startTransaction();
+jsTest.log("WriteCommandError should have error labels inside transactions.");
+res = sessionColl.insert({_id: "write-fail-point"});
+assert.commandFailedWithCode(res, ErrorCodes.WriteConflict);
+assert(res instanceof WriteCommandError);
+assert.eq(res.errorLabels, ["TransientTransactionError"]);
+res = testColl.insert({_id: "write-fail-point-outside-txn"});
+jsTest.log("WriteCommandError should not have error labels outside transactions.");
+// WriteConflict will not be returned outside transactions in real cases, but it's fine for
+// testing purpose.
+assert.commandFailedWithCode(res, ErrorCodes.WriteConflict);
+assert(res instanceof WriteCommandError);
+assert(!res.hasOwnProperty("errorLabels"));
+assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
+assert.commandFailedWithCode(session.abortTransaction_forTesting(), ErrorCodes.NoSuchTransaction);
+
+jsTest.log("WriteConflict returned by commitTransaction command is TransientTransactionError");
+session.startTransaction();
+assert.commandWorked(sessionColl.insert({_id: "commitTransaction-fail-point"}));
+assert.commandWorked(testDB.adminCommand({
+ configureFailPoint: "failCommand",
+ mode: "alwaysOn",
+ data: {errorCode: ErrorCodes.WriteConflict, failCommands: ["commitTransaction"]}
+}));
+res = session.commitTransaction_forTesting();
+assert.commandFailedWithCode(res, ErrorCodes.WriteConflict);
+assert.eq(res.errorLabels, ["TransientTransactionError"]);
+assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
+
+jsTest.log("NotMaster returned by commitTransaction command is not TransientTransactionError");
+// commitTransaction will attempt to perform a noop write in response to a NoSuchTransaction
+// error and non-empty writeConcern. This will throw NotMaster.
+res = secondarySessionDb.adminCommand({
+ commitTransaction: 1,
+ txnNumber: NumberLong(secondarySession.getTxnNumber_forTesting() + 1),
+ autocommit: false,
+ writeConcern: {w: "majority"}
+});
+assert.commandFailedWithCode(res, ErrorCodes.NotMaster);
+assert(!res.hasOwnProperty("errorLabels"));
+
+jsTest.log(
+ "NotMaster returned by coordinateCommitTransaction command is not TransientTransactionError");
+// coordinateCommitTransaction will attempt to perform a noop write in response to a
+// NoSuchTransaction error and non-empty writeConcern. This will throw NotMaster.
+res = secondarySessionDb.adminCommand({
+ coordinateCommitTransaction: 1,
+ participants: [],
+ txnNumber: NumberLong(secondarySession.getTxnNumber_forTesting() + 1),
+ autocommit: false,
+ writeConcern: {w: "majority"}
+});
+assert.commandFailedWithCode(res, ErrorCodes.NotMaster);
+assert(!res.hasOwnProperty("errorLabels"));
+
+jsTest.log("ShutdownInProgress returned by write commands is TransientTransactionError");
+session.startTransaction();
+assert.commandWorked(testDB.adminCommand({
+ configureFailPoint: "failCommand",
+ mode: "alwaysOn",
+ data: {errorCode: ErrorCodes.ShutdownInProgress, failCommands: ["insert"]}
+}));
+res = sessionColl.insert({_id: "commitTransaction-fail-point"});
+assert.commandFailedWithCode(res, ErrorCodes.ShutdownInProgress);
+assert(res instanceof WriteCommandError);
+assert.eq(res.errorLabels, ["TransientTransactionError"]);
+assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
+assert.commandFailedWithCode(session.abortTransaction_forTesting(), ErrorCodes.NoSuchTransaction);
+
+jsTest.log(
+ "ShutdownInProgress returned by commitTransaction command is not TransientTransactionError");
+session.startTransaction();
+assert.commandWorked(sessionColl.insert({_id: "commitTransaction-fail-point"}));
+assert.commandWorked(testDB.adminCommand({
+ configureFailPoint: "failCommand",
+ mode: "alwaysOn",
+ data: {errorCode: ErrorCodes.ShutdownInProgress, failCommands: ["commitTransaction"]}
+}));
+res = session.commitTransaction_forTesting();
+assert.commandFailedWithCode(res, ErrorCodes.ShutdownInProgress);
+assert(!res.hasOwnProperty("errorLabels"));
+assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
+
+jsTest.log(
+ "ShutdownInProgress returned by coordinateCommitTransaction command is not TransientTransactionError");
+session.startTransaction();
+assert.commandWorked(sessionColl.insert({_id: "coordinateCommitTransaction-fail-point"}));
+assert.commandWorked(testDB.adminCommand({
+ configureFailPoint: "failCommand",
+ mode: "alwaysOn",
+ data: {errorCode: ErrorCodes.ShutdownInProgress, failCommands: ["coordinateCommitTransaction"]}
+}));
+res = sessionDb.adminCommand({
+ coordinateCommitTransaction: 1,
+ participants: [],
+ txnNumber: NumberLong(session.getTxnNumber_forTesting()),
+ autocommit: false
+});
+assert.commandFailedWithCode(res, ErrorCodes.ShutdownInProgress);
+assert(!res.hasOwnProperty("errorLabels"));
+assert.commandWorked(session.abortTransaction_forTesting());
+assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
+
+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.commandWorked(sessionColl.insert({_id: "lock-timeout-1"}));
+function dropCmdFunc(primaryHost, dbName, collName) {
+ const primary = new Mongo(primaryHost);
+ return primary.getDB(dbName).runCommand({drop: collName, writeConcern: {w: "majority"}});
+}
+const thread = new ScopedThread(dropCmdFunc, 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());
});
- assert.commandFailedWithCode(res, ErrorCodes.NotMaster);
- assert.eq(res.errorLabels, ["TransientTransactionError"]);
-
- jsTest.log("Insert outside a transaction on secondary should fail but not return error labels");
- txnNumber++;
- // Insert as a retryable write.
- res = secondarySessionDb.runCommand(
- {insert: collName, documents: [{_id: "insert-1"}], txnNumber: NumberLong(txnNumber)});
-
- assert.commandFailedWithCode(res, ErrorCodes.NotMaster);
- assert(!res.hasOwnProperty("errorLabels"));
- secondarySession.endSession();
-
- jsTest.log("failCommand should be able to return errors with TransientTransactionError");
- assert.commandWorked(testDB.adminCommand({
- configureFailPoint: "failCommand",
- mode: "alwaysOn",
- data: {errorCode: ErrorCodes.WriteConflict, failCommands: ["insert"]}
- }));
- session.startTransaction();
- jsTest.log("WriteCommandError should have error labels inside transactions.");
- res = sessionColl.insert({_id: "write-fail-point"});
- assert.commandFailedWithCode(res, ErrorCodes.WriteConflict);
- assert(res instanceof WriteCommandError);
- assert.eq(res.errorLabels, ["TransientTransactionError"]);
- res = testColl.insert({_id: "write-fail-point-outside-txn"});
- jsTest.log("WriteCommandError should not have error labels outside transactions.");
- // WriteConflict will not be returned outside transactions in real cases, but it's fine for
- // testing purpose.
- assert.commandFailedWithCode(res, ErrorCodes.WriteConflict);
- assert(res instanceof WriteCommandError);
- assert(!res.hasOwnProperty("errorLabels"));
- assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
- assert.commandFailedWithCode(session.abortTransaction_forTesting(),
- ErrorCodes.NoSuchTransaction);
-
- jsTest.log("WriteConflict returned by commitTransaction command is TransientTransactionError");
- session.startTransaction();
- assert.commandWorked(sessionColl.insert({_id: "commitTransaction-fail-point"}));
- assert.commandWorked(testDB.adminCommand({
- configureFailPoint: "failCommand",
- mode: "alwaysOn",
- data: {errorCode: ErrorCodes.WriteConflict, failCommands: ["commitTransaction"]}
- }));
- res = session.commitTransaction_forTesting();
- assert.commandFailedWithCode(res, ErrorCodes.WriteConflict);
- assert.eq(res.errorLabels, ["TransientTransactionError"]);
- assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
-
- jsTest.log("NotMaster returned by commitTransaction command is not TransientTransactionError");
- // commitTransaction will attempt to perform a noop write in response to a NoSuchTransaction
- // error and non-empty writeConcern. This will throw NotMaster.
- res = secondarySessionDb.adminCommand({
- commitTransaction: 1,
- txnNumber: NumberLong(secondarySession.getTxnNumber_forTesting() + 1),
- autocommit: false,
- writeConcern: {w: "majority"}
- });
- assert.commandFailedWithCode(res, ErrorCodes.NotMaster);
- assert(!res.hasOwnProperty("errorLabels"));
-
- jsTest.log(
- "NotMaster returned by coordinateCommitTransaction command is not TransientTransactionError");
- // coordinateCommitTransaction will attempt to perform a noop write in response to a
- // NoSuchTransaction error and non-empty writeConcern. This will throw NotMaster.
- res = secondarySessionDb.adminCommand({
- coordinateCommitTransaction: 1,
- participants: [],
- txnNumber: NumberLong(secondarySession.getTxnNumber_forTesting() + 1),
- autocommit: false,
- writeConcern: {w: "majority"}
- });
- assert.commandFailedWithCode(res, ErrorCodes.NotMaster);
- assert(!res.hasOwnProperty("errorLabels"));
-
- jsTest.log("ShutdownInProgress returned by write commands is TransientTransactionError");
- session.startTransaction();
- assert.commandWorked(testDB.adminCommand({
- configureFailPoint: "failCommand",
- mode: "alwaysOn",
- data: {errorCode: ErrorCodes.ShutdownInProgress, failCommands: ["insert"]}
- }));
- res = sessionColl.insert({_id: "commitTransaction-fail-point"});
- assert.commandFailedWithCode(res, ErrorCodes.ShutdownInProgress);
- assert(res instanceof WriteCommandError);
- assert.eq(res.errorLabels, ["TransientTransactionError"]);
- assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
- assert.commandFailedWithCode(session.abortTransaction_forTesting(),
- ErrorCodes.NoSuchTransaction);
-
- jsTest.log(
- "ShutdownInProgress returned by commitTransaction command is not TransientTransactionError");
- session.startTransaction();
- assert.commandWorked(sessionColl.insert({_id: "commitTransaction-fail-point"}));
- assert.commandWorked(testDB.adminCommand({
- configureFailPoint: "failCommand",
- mode: "alwaysOn",
- data: {errorCode: ErrorCodes.ShutdownInProgress, failCommands: ["commitTransaction"]}
- }));
- res = session.commitTransaction_forTesting();
- assert.commandFailedWithCode(res, ErrorCodes.ShutdownInProgress);
- assert(!res.hasOwnProperty("errorLabels"));
- assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
-
- jsTest.log(
- "ShutdownInProgress returned by coordinateCommitTransaction command is not TransientTransactionError");
- session.startTransaction();
- assert.commandWorked(sessionColl.insert({_id: "coordinateCommitTransaction-fail-point"}));
- assert.commandWorked(testDB.adminCommand({
- configureFailPoint: "failCommand",
- mode: "alwaysOn",
- data: {
- errorCode: ErrorCodes.ShutdownInProgress,
- failCommands: ["coordinateCommitTransaction"]
- }
- }));
- res = sessionDb.adminCommand({
- coordinateCommitTransaction: 1,
- participants: [],
- txnNumber: NumberLong(session.getTxnNumber_forTesting()),
- autocommit: false
- });
- assert.commandFailedWithCode(res, ErrorCodes.ShutdownInProgress);
- assert(!res.hasOwnProperty("errorLabels"));
- assert.commandWorked(session.abortTransaction_forTesting());
- assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
-
- 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.commandWorked(sessionColl.insert({_id: "lock-timeout-1"}));
- function dropCmdFunc(primaryHost, dbName, collName) {
- const primary = new Mongo(primaryHost);
- return primary.getDB(dbName).runCommand({drop: collName, writeConcern: {w: "majority"}});
- }
- const thread = new ScopedThread(dropCmdFunc, 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"]);
- assert.commandFailedWithCode(sessionOther.abortTransaction_forTesting(),
- ErrorCodes.NoSuchTransaction);
- assert.commandWorked(session.abortTransaction_forTesting());
- thread.join();
- assert.commandWorked(thread.returnData());
-
- // Re-create the collection for later test cases.
- assert.commandWorked(testDB.createCollection(collName, {writeConcern: {w: "majority"}}));
-
- jsTest.log("Network errors for in-progress statements should be transient");
- session.startTransaction();
- assert.commandWorked(testDB.adminCommand({
- configureFailPoint: "failCommand",
- mode: "alwaysOn",
- data: {errorCode: ErrorCodes.HostUnreachable, failCommands: ["aggregate"]}
- }));
- res = sessionDb.runCommand({aggregate: collName, pipeline: [{$match: {}}], cursor: {}});
- assert.commandFailedWithCode(res, ErrorCodes.HostUnreachable);
- assert.eq(res.errorLabels, ["TransientTransactionError"]);
- assert.commandFailedWithCode(session.abortTransaction_forTesting(),
- ErrorCodes.NoSuchTransaction);
- assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
-
- jsTest.log("Network errors for commit should not be transient");
- session.startTransaction();
- assert.commandWorked(sessionColl.insert({_id: "commitTransaction-network-error"}));
- assert.commandWorked(testDB.adminCommand({
- configureFailPoint: "failCommand",
- mode: "alwaysOn",
- data: {errorCode: ErrorCodes.HostUnreachable, failCommands: ["commitTransaction"]}
- }));
- res = sessionDb.adminCommand({
- commitTransaction: 1,
- txnNumber: NumberLong(session.getTxnNumber_forTesting()),
- autocommit: false
- });
- assert.commandFailedWithCode(res, ErrorCodes.HostUnreachable);
- assert(!res.hasOwnProperty("errorLabels"), tojson(res));
- assert.commandWorked(session.abortTransaction_forTesting());
- assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
-
- session.endSession();
-
- st.stop();
+// 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"]);
+assert.commandFailedWithCode(sessionOther.abortTransaction_forTesting(),
+ ErrorCodes.NoSuchTransaction);
+assert.commandWorked(session.abortTransaction_forTesting());
+thread.join();
+assert.commandWorked(thread.returnData());
+
+// Re-create the collection for later test cases.
+assert.commandWorked(testDB.createCollection(collName, {writeConcern: {w: "majority"}}));
+
+jsTest.log("Network errors for in-progress statements should be transient");
+session.startTransaction();
+assert.commandWorked(testDB.adminCommand({
+ configureFailPoint: "failCommand",
+ mode: "alwaysOn",
+ data: {errorCode: ErrorCodes.HostUnreachable, failCommands: ["aggregate"]}
+}));
+res = sessionDb.runCommand({aggregate: collName, pipeline: [{$match: {}}], cursor: {}});
+assert.commandFailedWithCode(res, ErrorCodes.HostUnreachable);
+assert.eq(res.errorLabels, ["TransientTransactionError"]);
+assert.commandFailedWithCode(session.abortTransaction_forTesting(), ErrorCodes.NoSuchTransaction);
+assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
+
+jsTest.log("Network errors for commit should not be transient");
+session.startTransaction();
+assert.commandWorked(sessionColl.insert({_id: "commitTransaction-network-error"}));
+assert.commandWorked(testDB.adminCommand({
+ configureFailPoint: "failCommand",
+ mode: "alwaysOn",
+ data: {errorCode: ErrorCodes.HostUnreachable, failCommands: ["commitTransaction"]}
+}));
+res = sessionDb.adminCommand({
+ commitTransaction: 1,
+ txnNumber: NumberLong(session.getTxnNumber_forTesting()),
+ autocommit: false
+});
+assert.commandFailedWithCode(res, ErrorCodes.HostUnreachable);
+assert(!res.hasOwnProperty("errorLabels"), tojson(res));
+assert.commandWorked(session.abortTransaction_forTesting());
+assert.commandWorked(testDB.adminCommand({configureFailPoint: "failCommand", mode: "off"}));
+
+session.endSession();
+
+st.stop();
}());