summaryrefslogtreecommitdiff
path: root/jstests/replsets/transient_txn_error_labels_with_write_concern.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/replsets/transient_txn_error_labels_with_write_concern.js')
-rw-r--r--jstests/replsets/transient_txn_error_labels_with_write_concern.js119
1 files changed, 119 insertions, 0 deletions
diff --git a/jstests/replsets/transient_txn_error_labels_with_write_concern.js b/jstests/replsets/transient_txn_error_labels_with_write_concern.js
new file mode 100644
index 00000000000..5c16667e7c4
--- /dev/null
+++ b/jstests/replsets/transient_txn_error_labels_with_write_concern.js
@@ -0,0 +1,119 @@
+// Test TransientTransactionError error label for commands in transactions with write concern.
+// @tags: [uses_transactions]
+(function() {
+ "use strict";
+
+ load("jstests/libs/write_concern_util.js");
+ load("jstests/replsets/rslib.js");
+
+ const dbName = "test";
+ const collName = "transient_txn_error_labels_with_write_concern";
+ const rst = new ReplSetTest({name: collName, nodes: 3});
+ const config = rst.getReplSetConfig();
+ config.members[2].priority = 0;
+ config.settings = {};
+ // Disable catchup so the new primary will not sync from the old one.
+ config.settings.catchUpTimeoutMillis = 0;
+ // Disable catchup takeover to prevent the old primary to take over the new one.
+ config.settings.catchUpTakeoverDelayMillis = -1;
+ rst.startSet();
+ rst.initiate(config);
+
+ const primary = rst.getPrimary();
+ const secondary = rst.getSecondary();
+ assert.eq(primary, rst.nodes[0]);
+ const testDB = primary.getDB(dbName);
+
+ const sessionOptions = {causalConsistency: false};
+ const writeConcernMajority = {w: "majority", wtimeout: 500};
+
+ assert.commandWorked(testDB.createCollection(collName, {writeConcern: {w: "majority"}}));
+
+ jsTest.log("Write concern errors should not have error labels");
+ // Start a new session on the primary.
+ let session = primary.startSession(sessionOptions);
+ let sessionDb = session.getDatabase(dbName);
+ let sessionColl = sessionDb.getCollection(collName);
+ stopServerReplication(rst.getSecondaries());
+ session.startTransaction({writeConcern: writeConcernMajority});
+ assert.commandWorked(sessionColl.insert({_id: "write-with-write-concern"}));
+ let res = session.commitTransaction_forTesting();
+ checkWriteConcernTimedOut(res);
+ assert(!res.hasOwnProperty("code"));
+ assert(!res.hasOwnProperty("errorLabels"));
+ restartServerReplication(rst.getSecondaries());
+
+ jsTest.log(
+ "commitTransaction should wait for write concern even if it returns NoSuchTransaction");
+ // Make sure the new primary only miss the commitTransaction sent in this test case.
+ rst.awaitReplication();
+
+ session.startTransaction(writeConcernMajority);
+ // Pick up a high enough txnNumber so that it doesn't conflict with previous test cases.
+ let txnNumber = 20;
+ assert.commandWorked(sessionDb.runCommand({
+ insert: collName,
+ documents: [{_id: "commitTransaction-with-write-concern"}],
+ readConcern: {level: "snapshot"},
+ txnNumber: NumberLong(txnNumber),
+ startTransaction: true,
+ autocommit: false
+ }));
+ const oldPrimary = rst.getPrimary();
+ // Stop replication on all nodes, including the old primary so that it won't replicate from the
+ // new primary.
+ stopServerReplication(rst.nodes);
+ // commitTransaction fails on the old primary.
+ res = sessionDb.adminCommand({
+ commitTransaction: 1,
+ txnNumber: NumberLong(txnNumber),
+ autocommit: false,
+ writeConcern: writeConcernMajority,
+ });
+ checkWriteConcernTimedOut(res);
+
+ // Failover happens, but the new secondary cannot replicate anything to others.
+ assert.commandWorked(secondary.adminCommand({replSetStepUp: 1}));
+ rst.awaitNodesAgreeOnPrimary();
+ reconnect(secondary);
+ // Restart replication on the new primary, so it can become "master".
+ restartServerReplication(secondary);
+ const newPrimary = rst.getPrimary();
+ assert.neq(oldPrimary, newPrimary);
+ const newPrimarySession = newPrimary.startSession(sessionOptions);
+ // Force the new session to use the old session id to simulate driver's behavior.
+ const overridenSessionId = newPrimarySession._serverSession.handle.getId();
+ const lsid = session._serverSession.handle.getId();
+ jsTest.log("Overriding sessionID " + tojson(overridenSessionId) + " with " + tojson(lsid));
+ newPrimarySession._serverSession.handle.getId = () => lsid;
+ const newPrimarySessionDb = newPrimarySession.getDatabase(dbName);
+
+ res = newPrimarySessionDb.adminCommand({
+ commitTransaction: 1,
+ txnNumber: NumberLong(txnNumber),
+ autocommit: false,
+ writeConcern: writeConcernMajority,
+ });
+ checkWriteConcernTimedOut(res);
+ assert.commandFailedWithCode(res, ErrorCodes.NoSuchTransaction);
+
+ jsTest.log("NoSuchTransaction with write concern error is not transient");
+ assert(!res.hasOwnProperty("errorLabels"));
+
+ jsTest.log("NoSuchTransaction without write concern error is transient");
+ restartServerReplication(rst.nodes);
+ res = newPrimarySessionDb.adminCommand({
+ commitTransaction: 1,
+ txnNumber: NumberLong(txnNumber),
+ autocommit: false,
+ writeConcern: {w: "majority"}, // Wait with a long timeout.
+ });
+ assert.commandFailedWithCode(res, ErrorCodes.NoSuchTransaction);
+ assert(!res.hasOwnProperty("writeConcernError"), res);
+ assert.eq(res["errorLabels"], ["TransientTransactionError"], res);
+
+ rst.awaitNodesAgreeOnPrimary();
+ session.endSession();
+
+ rst.stopSet();
+}());