summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/router_transactions_metrics.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/noPassthrough/router_transactions_metrics.js')
-rw-r--r--jstests/noPassthrough/router_transactions_metrics.js1081
1 files changed, 540 insertions, 541 deletions
diff --git a/jstests/noPassthrough/router_transactions_metrics.js b/jstests/noPassthrough/router_transactions_metrics.js
index 9fd6c5f83eb..778f2adebea 100644
--- a/jstests/noPassthrough/router_transactions_metrics.js
+++ b/jstests/noPassthrough/router_transactions_metrics.js
@@ -2,576 +2,575 @@
// basic cases.
// @tags: [uses_transactions, uses_multi_shard_transaction]
(function() {
- "use strict";
-
- load("jstests/sharding/libs/sharded_transactions_helpers.js");
-
- // Verifies the transaction server status response has the fields that we expect.
- function verifyServerStatusFields(res) {
- const expectedFields = [
- "totalStarted",
- "totalAborted",
- "abortCause",
- "totalCommitted",
- "totalContactedParticipants",
- "totalParticipantsAtCommit",
- "totalRequestsTargeted",
- "commitTypes",
- ];
-
- assert(
- res.hasOwnProperty("transactions"),
- "Expected serverStatus response to have a 'transactions' field, res: " + tojson(res));
-
- assert.hasFields(res.transactions,
- expectedFields,
- "The 'transactions' field did not have all of the expected fields, res: " +
- tojson(res.transactions));
-
- assert.eq(expectedFields.length,
- Object.keys(res.transactions).length,
- "the 'transactions' field had an unexpected number of fields, res: " +
- tojson(res.transactions));
-
- // Verify the "commitTypes" sub-object has the expected fields.
- const commitTypes = [
- "noShards",
- "singleShard",
- "singleWriteShard",
- "readOnly",
- "twoPhaseCommit",
- "recoverWithToken",
- ];
- const commitTypeFields = ["initiated", "successful", "successfulDurationMicros"];
-
- assert.hasFields(res.transactions.commitTypes,
- commitTypes,
- "The 'transactions' field did not have each expected commit type, res: " +
- tojson(res.transactions));
-
- assert.eq(commitTypes.length,
- Object.keys(res.transactions.commitTypes).length,
- "the 'transactions' field had an unexpected number of commit types, res: " +
- tojson(res.transactions));
-
- commitTypes.forEach((type) => {
- assert.hasFields(res.transactions.commitTypes[type],
- commitTypeFields,
- "commit type " + type +
- " did not have all the expected fields, commit types: " +
- tojson(res.transactions.commitTypes));
-
- assert.eq(commitTypeFields.length,
- Object.keys(res.transactions.commitTypes[type]).length,
- "commit type " + type +
- " had an unexpected number of fields, commit types: " +
- tojson(res.transactions.commitTypes));
- });
+"use strict";
+
+load("jstests/sharding/libs/sharded_transactions_helpers.js");
+
+// Verifies the transaction server status response has the fields that we expect.
+function verifyServerStatusFields(res) {
+ const expectedFields = [
+ "totalStarted",
+ "totalAborted",
+ "abortCause",
+ "totalCommitted",
+ "totalContactedParticipants",
+ "totalParticipantsAtCommit",
+ "totalRequestsTargeted",
+ "commitTypes",
+ ];
+
+ assert(res.hasOwnProperty("transactions"),
+ "Expected serverStatus response to have a 'transactions' field, res: " + tojson(res));
+
+ assert.hasFields(res.transactions,
+ expectedFields,
+ "The 'transactions' field did not have all of the expected fields, res: " +
+ tojson(res.transactions));
+
+ assert.eq(expectedFields.length,
+ Object.keys(res.transactions).length,
+ "the 'transactions' field had an unexpected number of fields, res: " +
+ tojson(res.transactions));
+
+ // Verify the "commitTypes" sub-object has the expected fields.
+ const commitTypes = [
+ "noShards",
+ "singleShard",
+ "singleWriteShard",
+ "readOnly",
+ "twoPhaseCommit",
+ "recoverWithToken",
+ ];
+ const commitTypeFields = ["initiated", "successful", "successfulDurationMicros"];
+
+ assert.hasFields(res.transactions.commitTypes,
+ commitTypes,
+ "The 'transactions' field did not have each expected commit type, res: " +
+ tojson(res.transactions));
+
+ assert.eq(commitTypes.length,
+ Object.keys(res.transactions.commitTypes).length,
+ "the 'transactions' field had an unexpected number of commit types, res: " +
+ tojson(res.transactions));
+
+ commitTypes.forEach((type) => {
+ assert.hasFields(res.transactions.commitTypes[type],
+ commitTypeFields,
+ "commit type " + type +
+ " did not have all the expected fields, commit types: " +
+ tojson(res.transactions.commitTypes));
+
+ assert.eq(commitTypeFields.length,
+ Object.keys(res.transactions.commitTypes[type]).length,
+ "commit type " + type + " had an unexpected number of fields, commit types: " +
+ tojson(res.transactions.commitTypes));
+ });
+}
+
+class ExpectedCommitType {
+ constructor() {
+ this.initiated = 0;
+ this.successful = 0;
+ this.successfulDurationMicros = 0;
}
+}
- class ExpectedCommitType {
- constructor() {
- this.initiated = 0;
- this.successful = 0;
- this.successfulDurationMicros = 0;
- }
+class ExpectedAbortCause {
+ constructor() {
}
-
- class ExpectedAbortCause {
- constructor() {
- }
+}
+
+class ExpectedTransactionServerStatus {
+ constructor() {
+ this.totalStarted = 0;
+ this.totalAborted = 0;
+ this.abortCause = new ExpectedAbortCause();
+ this.totalCommitted = 0;
+ this.totalContactedParticipants = 0;
+ this.totalParticipantsAtCommit = 0;
+ this.totalRequestsTargeted = 0;
+ this.commitTypes = {
+ noShards: new ExpectedCommitType(),
+ singleShard: new ExpectedCommitType(),
+ singleWriteShard: new ExpectedCommitType(),
+ readOnly: new ExpectedCommitType(),
+ twoPhaseCommit: new ExpectedCommitType(),
+ recoverWithToken: new ExpectedCommitType(),
+ };
}
-
- class ExpectedTransactionServerStatus {
- constructor() {
- this.totalStarted = 0;
- this.totalAborted = 0;
- this.abortCause = new ExpectedAbortCause();
- this.totalCommitted = 0;
- this.totalContactedParticipants = 0;
- this.totalParticipantsAtCommit = 0;
- this.totalRequestsTargeted = 0;
- this.commitTypes = {
- noShards: new ExpectedCommitType(),
- singleShard: new ExpectedCommitType(),
- singleWriteShard: new ExpectedCommitType(),
- readOnly: new ExpectedCommitType(),
- twoPhaseCommit: new ExpectedCommitType(),
- recoverWithToken: new ExpectedCommitType(),
- };
+}
+
+// Verifies the transaction values in the server status response match the provided values.
+function verifyServerStatusValues(st, expectedStats) {
+ const res = assert.commandWorked(st.s.adminCommand({serverStatus: 1}));
+ verifyServerStatusFields(res);
+
+ const stats = res.transactions;
+ assert.eq(expectedStats.totalStarted,
+ stats.totalStarted,
+ "unexpected totalStarted, res: " + tojson(stats));
+ assert.eq(expectedStats.totalAborted,
+ stats.totalAborted,
+ "unexpected totalAborted, res: " + tojson(stats));
+ assert.eq(expectedStats.totalCommitted,
+ stats.totalCommitted,
+ "unexpected totalCommitted, res: " + tojson(stats));
+ assert.eq(expectedStats.totalContactedParticipants,
+ stats.totalContactedParticipants,
+ "unexpected totalContactedParticipants, res: " + tojson(stats));
+ assert.eq(expectedStats.totalParticipantsAtCommit,
+ stats.totalParticipantsAtCommit,
+ "unexpected totalParticipantsAtCommit, res: " + tojson(stats));
+ assert.eq(expectedStats.totalRequestsTargeted,
+ stats.totalRequestsTargeted,
+ "unexpected totalRequestsTargeted, res: " + tojson(stats));
+
+ const commitTypes = res.transactions.commitTypes;
+ Object.keys(commitTypes).forEach((commitType) => {
+ assert.eq(
+ expectedStats.commitTypes[commitType].initiated,
+ commitTypes[commitType].initiated,
+ "unexpected initiated for " + commitType + ", commit types: " + tojson(commitTypes));
+ assert.eq(
+ expectedStats.commitTypes[commitType].successful,
+ commitTypes[commitType].successful,
+ "unexpected successful for " + commitType + ", commit types: " + tojson(commitTypes));
+
+ assert.lte(expectedStats.commitTypes[commitType].successfulDurationMicros,
+ commitTypes[commitType].successfulDurationMicros,
+ "unexpected successfulDurationMicros for " + commitType +
+ ", commit types: " + tojson(commitTypes));
+ expectedStats.commitTypes[commitType].successfulDurationMicros =
+ commitTypes[commitType].successfulDurationMicros;
+
+ if (commitTypes[commitType].successful != 0) {
+ assert.gt(commitTypes[commitType].successfulDurationMicros,
+ 0,
+ "unexpected successfulDurationMicros for " + commitType +
+ ", commit types: " + tojson(commitTypes));
}
- }
-
- // Verifies the transaction values in the server status response match the provided values.
- function verifyServerStatusValues(st, expectedStats) {
- const res = assert.commandWorked(st.s.adminCommand({serverStatus: 1}));
- verifyServerStatusFields(res);
-
- const stats = res.transactions;
- assert.eq(expectedStats.totalStarted,
- stats.totalStarted,
- "unexpected totalStarted, res: " + tojson(stats));
- assert.eq(expectedStats.totalAborted,
- stats.totalAborted,
- "unexpected totalAborted, res: " + tojson(stats));
- assert.eq(expectedStats.totalCommitted,
- stats.totalCommitted,
- "unexpected totalCommitted, res: " + tojson(stats));
- assert.eq(expectedStats.totalContactedParticipants,
- stats.totalContactedParticipants,
- "unexpected totalContactedParticipants, res: " + tojson(stats));
- assert.eq(expectedStats.totalParticipantsAtCommit,
- stats.totalParticipantsAtCommit,
- "unexpected totalParticipantsAtCommit, res: " + tojson(stats));
- assert.eq(expectedStats.totalRequestsTargeted,
- stats.totalRequestsTargeted,
- "unexpected totalRequestsTargeted, res: " + tojson(stats));
-
- const commitTypes = res.transactions.commitTypes;
- Object.keys(commitTypes).forEach((commitType) => {
- assert.eq(expectedStats.commitTypes[commitType].initiated,
- commitTypes[commitType].initiated,
- "unexpected initiated for " + commitType + ", commit types: " +
- tojson(commitTypes));
- assert.eq(expectedStats.commitTypes[commitType].successful,
- commitTypes[commitType].successful,
- "unexpected successful for " + commitType + ", commit types: " +
- tojson(commitTypes));
-
- assert.lte(expectedStats.commitTypes[commitType].successfulDurationMicros,
- commitTypes[commitType].successfulDurationMicros,
- "unexpected successfulDurationMicros for " + commitType +
- ", commit types: " + tojson(commitTypes));
- expectedStats.commitTypes[commitType].successfulDurationMicros =
- commitTypes[commitType].successfulDurationMicros;
-
- if (commitTypes[commitType].successful != 0) {
- assert.gt(commitTypes[commitType].successfulDurationMicros,
- 0,
- "unexpected successfulDurationMicros for " + commitType +
- ", commit types: " + tojson(commitTypes));
- }
- });
-
- const abortCause = res.transactions.abortCause;
- Object.keys(abortCause).forEach((cause) => {
- assert.eq(expectedStats.abortCause[cause],
- abortCause[cause],
- "unexpected abortCause for " + cause + ", res: " + tojson(stats));
- });
-
- assert.eq(Object.keys(abortCause).length,
- Object.keys(expectedStats.abortCause).length,
- "the 'transactions' field had an unexpected number of abort causes, res: " +
- tojson(stats));
- }
-
- function abortFromUnderneath(st, session) {
- st._rs.forEach((rs) => {
- assert.commandWorkedOrFailedWithCode(rs.test.getPrimary().adminCommand({
- abortTransaction: 1,
- lsid: session.getSessionId(),
- txnNumber: session.getTxnNumber_forTesting(),
- autocommit: false
- }),
- ErrorCodes.NoSuchTransaction);
- });
- }
-
- const dbName = "test";
- const collName = "foo";
- const ns = dbName + '.' + collName;
-
- const st = new ShardingTest({shards: 2, mongos: 2, config: 1});
-
- const session = st.s.startSession();
- const sessionDB = session.getDatabase(dbName);
+ });
+
+ const abortCause = res.transactions.abortCause;
+ Object.keys(abortCause).forEach((cause) => {
+ assert.eq(expectedStats.abortCause[cause],
+ abortCause[cause],
+ "unexpected abortCause for " + cause + ", res: " + tojson(stats));
+ });
+
+ assert.eq(
+ Object.keys(abortCause).length,
+ Object.keys(expectedStats.abortCause).length,
+ "the 'transactions' field had an unexpected number of abort causes, res: " + tojson(stats));
+}
+
+function abortFromUnderneath(st, session) {
+ st._rs.forEach((rs) => {
+ assert.commandWorkedOrFailedWithCode(rs.test.getPrimary().adminCommand({
+ abortTransaction: 1,
+ lsid: session.getSessionId(),
+ txnNumber: session.getTxnNumber_forTesting(),
+ autocommit: false
+ }),
+ ErrorCodes.NoSuchTransaction);
+ });
+}
- const otherRouterSession = st.s1.startSession();
- const otherRouterSessionDB = otherRouterSession.getDatabase(dbName);
+const dbName = "test";
+const collName = "foo";
+const ns = dbName + '.' + collName;
- // Set up two chunks: [-inf, 0), [0, inf) one on each shard, with one document in each.
+const st = new ShardingTest({shards: 2, mongos: 2, config: 1});
- assert.commandWorked(sessionDB[collName].insert({_id: -1}));
- assert.commandWorked(sessionDB[collName].insert({_id: 1}));
+const session = st.s.startSession();
+const sessionDB = session.getDatabase(dbName);
- assert.commandWorked(st.s.adminCommand({enableSharding: dbName}));
- st.ensurePrimaryShard(dbName, st.shard0.shardName);
- assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {_id: 1}}));
- assert.commandWorked(st.s.adminCommand({split: ns, middle: {_id: 0}}));
- assert.commandWorked(
- st.s.adminCommand({moveChunk: ns, find: {_id: 1}, to: st.shard1.shardName}));
- flushRoutersAndRefreshShardMetadata(st, {ns});
+const otherRouterSession = st.s1.startSession();
+const otherRouterSessionDB = otherRouterSession.getDatabase(dbName);
- let expectedStats = new ExpectedTransactionServerStatus();
+// Set up two chunks: [-inf, 0), [0, inf) one on each shard, with one document in each.
- //
- // Helpers for setting up transactions that will trigger the various commit paths.
- //
+assert.commandWorked(sessionDB[collName].insert({_id: -1}));
+assert.commandWorked(sessionDB[collName].insert({_id: 1}));
- function startNoShardsTransaction() {
- session.startTransaction();
- assert.commandWorked(session.getDatabase("doesntExist").runCommand({find: collName}));
+assert.commandWorked(st.s.adminCommand({enableSharding: dbName}));
+st.ensurePrimaryShard(dbName, st.shard0.shardName);
+assert.commandWorked(st.s.adminCommand({shardCollection: ns, key: {_id: 1}}));
+assert.commandWorked(st.s.adminCommand({split: ns, middle: {_id: 0}}));
+assert.commandWorked(st.s.adminCommand({moveChunk: ns, find: {_id: 1}, to: st.shard1.shardName}));
+flushRoutersAndRefreshShardMetadata(st, {ns});
- expectedStats.totalStarted += 1;
- verifyServerStatusValues(st, expectedStats);
- }
+let expectedStats = new ExpectedTransactionServerStatus();
- function startSingleShardTransaction() {
- session.startTransaction();
- assert.commandWorked(sessionDB[collName].insert({x: 1}));
+//
+// Helpers for setting up transactions that will trigger the various commit paths.
+//
- expectedStats.totalStarted += 1;
- expectedStats.totalContactedParticipants += 1;
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
- }
+function startNoShardsTransaction() {
+ session.startTransaction();
+ assert.commandWorked(session.getDatabase("doesntExist").runCommand({find: collName}));
- function startSingleWriteShardTransaction() {
- session.startTransaction();
- assert.commandWorked(sessionDB[collName].insert({x: 1}));
+ expectedStats.totalStarted += 1;
+ verifyServerStatusValues(st, expectedStats);
+}
- expectedStats.totalStarted += 1;
- expectedStats.totalContactedParticipants += 1;
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
+function startSingleShardTransaction() {
+ session.startTransaction();
+ assert.commandWorked(sessionDB[collName].insert({x: 1}));
- assert.commandWorked(sessionDB.runCommand({find: collName}));
+ expectedStats.totalStarted += 1;
+ expectedStats.totalContactedParticipants += 1;
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+}
- expectedStats.totalContactedParticipants += 1;
- expectedStats.totalRequestsTargeted += 2;
- verifyServerStatusValues(st, expectedStats);
- }
+function startSingleWriteShardTransaction() {
+ session.startTransaction();
+ assert.commandWorked(sessionDB[collName].insert({x: 1}));
- function startReadOnlyTransaction() {
- session.startTransaction();
- assert.commandWorked(sessionDB.runCommand({find: collName}));
+ expectedStats.totalStarted += 1;
+ expectedStats.totalContactedParticipants += 1;
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
- expectedStats.totalStarted += 1;
- expectedStats.totalContactedParticipants += 2;
- expectedStats.totalRequestsTargeted += 2;
- verifyServerStatusValues(st, expectedStats);
- }
+ assert.commandWorked(sessionDB.runCommand({find: collName}));
- function startTwoPhaseCommitTransaction() {
- session.startTransaction();
- assert.commandWorked(sessionDB[collName].insert({_id: -5}));
+ expectedStats.totalContactedParticipants += 1;
+ expectedStats.totalRequestsTargeted += 2;
+ verifyServerStatusValues(st, expectedStats);
+}
- expectedStats.totalStarted += 1;
- expectedStats.totalContactedParticipants += 1;
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
+function startReadOnlyTransaction() {
+ session.startTransaction();
+ assert.commandWorked(sessionDB.runCommand({find: collName}));
- assert.commandWorked(sessionDB[collName].insert({_id: 5}));
+ expectedStats.totalStarted += 1;
+ expectedStats.totalContactedParticipants += 2;
+ expectedStats.totalRequestsTargeted += 2;
+ verifyServerStatusValues(st, expectedStats);
+}
- expectedStats.totalContactedParticipants += 1;
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
- }
+function startTwoPhaseCommitTransaction() {
+ session.startTransaction();
+ assert.commandWorked(sessionDB[collName].insert({_id: -5}));
- function setUpTransactionToRecoverCommit({shouldCommit}) {
- otherRouterSession.startTransaction();
- let resWithRecoveryToken = assert.commandWorked(
- otherRouterSessionDB.runCommand({insert: collName, documents: [{x: 5}]}));
- if (shouldCommit) {
- assert.commandWorked(otherRouterSession.commitTransaction_forTesting());
- } else {
- assert.commandWorked(otherRouterSession.abortTransaction_forTesting());
- }
+ expectedStats.totalStarted += 1;
+ expectedStats.totalContactedParticipants += 1;
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
- // The stats on the main mongos shouldn't have changed.
- verifyServerStatusValues(st, expectedStats);
+ assert.commandWorked(sessionDB[collName].insert({_id: 5}));
- return resWithRecoveryToken.recoveryToken;
- }
+ expectedStats.totalContactedParticipants += 1;
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+}
- //
- // Test cases for serverStatus output.
- //
-
- jsTest.log("Default values.");
- (() => {
- verifyServerStatusValues(st, expectedStats);
- })();
-
- // Note committing a no shards transaction can only succeed.
- jsTest.log("Committed no shards transaction.");
- (() => {
- startNoShardsTransaction();
-
- assert.commandWorked(session.commitTransaction_forTesting());
-
- expectedStats.totalCommitted += 1;
- expectedStats.commitTypes.noShards.initiated += 1;
- expectedStats.commitTypes.noShards.successful += 1;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Successful single shard transaction.");
- (() => {
- startSingleShardTransaction();
-
- assert.commandWorked(session.commitTransaction_forTesting());
-
- expectedStats.totalCommitted += 1;
- expectedStats.commitTypes.singleShard.initiated += 1;
- expectedStats.commitTypes.singleShard.successful += 1;
- expectedStats.totalParticipantsAtCommit += 1;
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Failed single shard transaction.");
- (() => {
- startSingleShardTransaction();
-
- abortFromUnderneath(st, session);
- assert.commandFailedWithCode(session.commitTransaction_forTesting(),
- ErrorCodes.NoSuchTransaction);
-
- expectedStats.totalAborted += 1;
- expectedStats.abortCause["NoSuchTransaction"] = 1;
- expectedStats.commitTypes.singleShard.initiated += 1;
- expectedStats.totalParticipantsAtCommit += 1;
- // The one shard is targeted for the commit then the implicit abort.
- expectedStats.totalRequestsTargeted += 1 + 1;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Successful single write shard transaction.");
- (() => {
- startSingleWriteShardTransaction();
-
- assert.commandWorked(session.commitTransaction_forTesting());
-
- expectedStats.totalCommitted += 1;
- expectedStats.commitTypes.singleWriteShard.initiated += 1;
- expectedStats.commitTypes.singleWriteShard.successful += 1;
- expectedStats.totalParticipantsAtCommit += 2;
- expectedStats.totalRequestsTargeted += 2;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Failed single write shard transaction.");
- (() => {
- startSingleWriteShardTransaction();
-
- abortFromUnderneath(st, session);
- assert.commandFailedWithCode(session.commitTransaction_forTesting(),
- ErrorCodes.NoSuchTransaction);
-
- expectedStats.totalAborted += 1;
- expectedStats.abortCause["NoSuchTransaction"] += 1;
- expectedStats.commitTypes.singleWriteShard.initiated += 1;
- expectedStats.totalParticipantsAtCommit += 2;
- // In a single write shard commit, all read shards are committed first, then the
- // write shards, so if committing on a read shard fails, the write shards aren't targeted.
- // The implicit abort after will target all shards.
- expectedStats.totalRequestsTargeted += 1 + 2;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Successful read only transaction.");
- (() => {
- startReadOnlyTransaction();
-
- assert.commandWorked(session.commitTransaction_forTesting());
-
- expectedStats.totalCommitted += 1;
- expectedStats.commitTypes.readOnly.initiated += 1;
- expectedStats.commitTypes.readOnly.successful += 1;
- expectedStats.totalParticipantsAtCommit += 2;
- expectedStats.totalRequestsTargeted += 2;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Failed read only transaction.");
- (() => {
- startReadOnlyTransaction();
-
- abortFromUnderneath(st, session);
- assert.commandFailedWithCode(session.commitTransaction_forTesting(),
- ErrorCodes.NoSuchTransaction);
-
- expectedStats.totalAborted += 1;
- expectedStats.abortCause["NoSuchTransaction"] += 1;
- expectedStats.commitTypes.readOnly.initiated += 1;
- expectedStats.totalParticipantsAtCommit += 2;
- // Both shards are targeted for the commit then the implicit abort.
- expectedStats.totalRequestsTargeted += 2 + 2;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Successful two phase commit transaction.");
- (() => {
- startTwoPhaseCommitTransaction();
-
- assert.commandWorked(session.commitTransaction_forTesting());
-
- expectedStats.totalCommitted += 1;
- expectedStats.commitTypes.twoPhaseCommit.initiated += 1;
- expectedStats.commitTypes.twoPhaseCommit.successful += 1;
- expectedStats.totalParticipantsAtCommit += 2;
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
-
- // Remove the inserted documents.
- assert.commandWorked(sessionDB[collName].remove({_id: {$in: [-5, 5]}}));
- })();
-
- jsTest.log("Failed two phase commit transaction.");
- (() => {
- startTwoPhaseCommitTransaction();
-
- abortFromUnderneath(st, session);
- assert.commandFailedWithCode(session.commitTransaction_forTesting(),
- ErrorCodes.NoSuchTransaction);
-
- expectedStats.totalAborted += 1;
- expectedStats.abortCause["NoSuchTransaction"] += 1;
- expectedStats.commitTypes.twoPhaseCommit.initiated += 1;
- expectedStats.totalParticipantsAtCommit += 2;
- // There are no implicit aborts after two phase commit, so the coordinator is targeted once.
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Recover successful commit result.");
- (() => {
- const recoveryToken = setUpTransactionToRecoverCommit({shouldCommit: true});
-
- assert.commandWorked(st.s.adminCommand({
- commitTransaction: 1,
- lsid: otherRouterSession.getSessionId(),
- txnNumber: otherRouterSession.getTxnNumber_forTesting(),
- autocommit: false, recoveryToken
- }));
-
- expectedStats.totalStarted += 1;
- expectedStats.totalCommitted += 1;
- expectedStats.commitTypes.recoverWithToken.initiated += 1;
- expectedStats.commitTypes.recoverWithToken.successful += 1;
- // The participant stats shouldn't increase if we're recovering commit.
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Recover failed commit result.");
- (() => {
- const recoveryToken = setUpTransactionToRecoverCommit({shouldCommit: false});
-
- assert.commandFailedWithCode(st.s.adminCommand({
- commitTransaction: 1,
- lsid: otherRouterSession.getSessionId(),
- txnNumber: otherRouterSession.getTxnNumber_forTesting(),
- autocommit: false, recoveryToken
- }),
- ErrorCodes.NoSuchTransaction);
-
- expectedStats.totalStarted += 1;
- expectedStats.totalAborted += 1;
- expectedStats.abortCause["NoSuchTransaction"] += 1;
- expectedStats.commitTypes.recoverWithToken.initiated += 1;
- // The participant stats shouldn't increase if we're recovering commit.
- // There are no implicit aborts during commit recovery, so the recovery shard is targeted
- // once.
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Empty recovery token.");
- (() => {
- otherRouterSession.startTransaction();
- let resWithEmptyRecoveryToken =
- assert.commandWorked(otherRouterSessionDB.runCommand({find: collName}));
+function setUpTransactionToRecoverCommit({shouldCommit}) {
+ otherRouterSession.startTransaction();
+ let resWithRecoveryToken = assert.commandWorked(
+ otherRouterSessionDB.runCommand({insert: collName, documents: [{x: 5}]}));
+ if (shouldCommit) {
assert.commandWorked(otherRouterSession.commitTransaction_forTesting());
+ } else {
+ assert.commandWorked(otherRouterSession.abortTransaction_forTesting());
+ }
- // The stats on the main mongos shouldn't have changed.
- verifyServerStatusValues(st, expectedStats);
-
- assert.commandFailedWithCode(st.s.adminCommand({
- commitTransaction: 1,
- lsid: otherRouterSession.getSessionId(),
- txnNumber: otherRouterSession.getTxnNumber_forTesting(),
- autocommit: false,
- recoveryToken: resWithEmptyRecoveryToken.recoveryToken
- }),
- ErrorCodes.NoSuchTransaction);
-
- expectedStats.totalStarted += 1;
- expectedStats.commitTypes.recoverWithToken.initiated += 1;
- // No requests are targeted and the decision isn't learned, so total committed/aborted and
- // total requests sent shouldn't change.
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Explicitly aborted transaction.");
- (() => {
- session.startTransaction();
- assert.commandWorked(sessionDB[collName].insert({x: 2}));
-
- expectedStats.totalStarted += 1;
- expectedStats.totalContactedParticipants += 1;
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
-
- assert.commandWorked(session.abortTransaction_forTesting());
-
- expectedStats.totalAborted += 1;
- expectedStats.abortCause["abort"] = 1;
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Implicitly aborted transaction.");
- (() => {
- session.startTransaction();
- assert.commandFailedWithCode(sessionDB[collName].insert({_id: 1}), ErrorCodes.DuplicateKey);
-
- expectedStats.totalStarted += 1;
- expectedStats.totalAborted += 1;
- expectedStats.abortCause["DuplicateKey"] = 1;
- expectedStats.totalContactedParticipants += 1;
- expectedStats.totalRequestsTargeted += 2; // Plus one for the implicit abort.
- verifyServerStatusValues(st, expectedStats);
-
- assert.commandFailedWithCode(session.abortTransaction_forTesting(),
- ErrorCodes.NoSuchTransaction);
-
- // A failed abortTransaction leads to an implicit abort, so two requests are targeted.
- expectedStats.totalRequestsTargeted += 2;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- jsTest.log("Abandoned transaction.");
- (() => {
- session.startTransaction();
- assert.commandWorked(sessionDB[collName].insert({_id: -15}));
-
- expectedStats.totalStarted += 1;
- expectedStats.totalContactedParticipants += 1;
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
-
- session.startTransaction_forTesting({}, {ignoreActiveTxn: true});
- assert.commandWorked(sessionDB[collName].insert({_id: -15}));
-
- expectedStats.totalStarted += 1;
- expectedStats.totalContactedParticipants += 1;
- expectedStats.totalRequestsTargeted += 1;
- // The router never learned if the previous transaction committed or aborted, so the aborted
- // counter shouldn't be incremented.
- verifyServerStatusValues(st, expectedStats);
-
- // Abort to clear the shell's session state.
- assert.commandWorked(session.abortTransaction_forTesting());
-
- expectedStats.totalAborted += 1;
- expectedStats.abortCause["abort"] += 1;
- expectedStats.totalRequestsTargeted += 1;
- verifyServerStatusValues(st, expectedStats);
- })();
-
- session.endSession();
- st.stop();
+ // The stats on the main mongos shouldn't have changed.
+ verifyServerStatusValues(st, expectedStats);
+
+ return resWithRecoveryToken.recoveryToken;
+}
+
+//
+// Test cases for serverStatus output.
+//
+
+jsTest.log("Default values.");
+(() => {
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+// Note committing a no shards transaction can only succeed.
+jsTest.log("Committed no shards transaction.");
+(() => {
+ startNoShardsTransaction();
+
+ assert.commandWorked(session.commitTransaction_forTesting());
+
+ expectedStats.totalCommitted += 1;
+ expectedStats.commitTypes.noShards.initiated += 1;
+ expectedStats.commitTypes.noShards.successful += 1;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Successful single shard transaction.");
+(() => {
+ startSingleShardTransaction();
+
+ assert.commandWorked(session.commitTransaction_forTesting());
+
+ expectedStats.totalCommitted += 1;
+ expectedStats.commitTypes.singleShard.initiated += 1;
+ expectedStats.commitTypes.singleShard.successful += 1;
+ expectedStats.totalParticipantsAtCommit += 1;
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Failed single shard transaction.");
+(() => {
+ startSingleShardTransaction();
+
+ abortFromUnderneath(st, session);
+ assert.commandFailedWithCode(session.commitTransaction_forTesting(),
+ ErrorCodes.NoSuchTransaction);
+
+ expectedStats.totalAborted += 1;
+ expectedStats.abortCause["NoSuchTransaction"] = 1;
+ expectedStats.commitTypes.singleShard.initiated += 1;
+ expectedStats.totalParticipantsAtCommit += 1;
+ // The one shard is targeted for the commit then the implicit abort.
+ expectedStats.totalRequestsTargeted += 1 + 1;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Successful single write shard transaction.");
+(() => {
+ startSingleWriteShardTransaction();
+
+ assert.commandWorked(session.commitTransaction_forTesting());
+
+ expectedStats.totalCommitted += 1;
+ expectedStats.commitTypes.singleWriteShard.initiated += 1;
+ expectedStats.commitTypes.singleWriteShard.successful += 1;
+ expectedStats.totalParticipantsAtCommit += 2;
+ expectedStats.totalRequestsTargeted += 2;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Failed single write shard transaction.");
+(() => {
+ startSingleWriteShardTransaction();
+
+ abortFromUnderneath(st, session);
+ assert.commandFailedWithCode(session.commitTransaction_forTesting(),
+ ErrorCodes.NoSuchTransaction);
+
+ expectedStats.totalAborted += 1;
+ expectedStats.abortCause["NoSuchTransaction"] += 1;
+ expectedStats.commitTypes.singleWriteShard.initiated += 1;
+ expectedStats.totalParticipantsAtCommit += 2;
+ // In a single write shard commit, all read shards are committed first, then the
+ // write shards, so if committing on a read shard fails, the write shards aren't targeted.
+ // The implicit abort after will target all shards.
+ expectedStats.totalRequestsTargeted += 1 + 2;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Successful read only transaction.");
+(() => {
+ startReadOnlyTransaction();
+
+ assert.commandWorked(session.commitTransaction_forTesting());
+
+ expectedStats.totalCommitted += 1;
+ expectedStats.commitTypes.readOnly.initiated += 1;
+ expectedStats.commitTypes.readOnly.successful += 1;
+ expectedStats.totalParticipantsAtCommit += 2;
+ expectedStats.totalRequestsTargeted += 2;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Failed read only transaction.");
+(() => {
+ startReadOnlyTransaction();
+
+ abortFromUnderneath(st, session);
+ assert.commandFailedWithCode(session.commitTransaction_forTesting(),
+ ErrorCodes.NoSuchTransaction);
+
+ expectedStats.totalAborted += 1;
+ expectedStats.abortCause["NoSuchTransaction"] += 1;
+ expectedStats.commitTypes.readOnly.initiated += 1;
+ expectedStats.totalParticipantsAtCommit += 2;
+ // Both shards are targeted for the commit then the implicit abort.
+ expectedStats.totalRequestsTargeted += 2 + 2;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Successful two phase commit transaction.");
+(() => {
+ startTwoPhaseCommitTransaction();
+
+ assert.commandWorked(session.commitTransaction_forTesting());
+
+ expectedStats.totalCommitted += 1;
+ expectedStats.commitTypes.twoPhaseCommit.initiated += 1;
+ expectedStats.commitTypes.twoPhaseCommit.successful += 1;
+ expectedStats.totalParticipantsAtCommit += 2;
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+
+ // Remove the inserted documents.
+ assert.commandWorked(sessionDB[collName].remove({_id: {$in: [-5, 5]}}));
+})();
+
+jsTest.log("Failed two phase commit transaction.");
+(() => {
+ startTwoPhaseCommitTransaction();
+
+ abortFromUnderneath(st, session);
+ assert.commandFailedWithCode(session.commitTransaction_forTesting(),
+ ErrorCodes.NoSuchTransaction);
+
+ expectedStats.totalAborted += 1;
+ expectedStats.abortCause["NoSuchTransaction"] += 1;
+ expectedStats.commitTypes.twoPhaseCommit.initiated += 1;
+ expectedStats.totalParticipantsAtCommit += 2;
+ // There are no implicit aborts after two phase commit, so the coordinator is targeted once.
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Recover successful commit result.");
+(() => {
+ const recoveryToken = setUpTransactionToRecoverCommit({shouldCommit: true});
+
+ assert.commandWorked(st.s.adminCommand({
+ commitTransaction: 1,
+ lsid: otherRouterSession.getSessionId(),
+ txnNumber: otherRouterSession.getTxnNumber_forTesting(),
+ autocommit: false,
+ recoveryToken
+ }));
+
+ expectedStats.totalStarted += 1;
+ expectedStats.totalCommitted += 1;
+ expectedStats.commitTypes.recoverWithToken.initiated += 1;
+ expectedStats.commitTypes.recoverWithToken.successful += 1;
+ // The participant stats shouldn't increase if we're recovering commit.
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Recover failed commit result.");
+(() => {
+ const recoveryToken = setUpTransactionToRecoverCommit({shouldCommit: false});
+
+ assert.commandFailedWithCode(st.s.adminCommand({
+ commitTransaction: 1,
+ lsid: otherRouterSession.getSessionId(),
+ txnNumber: otherRouterSession.getTxnNumber_forTesting(),
+ autocommit: false,
+ recoveryToken
+ }),
+ ErrorCodes.NoSuchTransaction);
+
+ expectedStats.totalStarted += 1;
+ expectedStats.totalAborted += 1;
+ expectedStats.abortCause["NoSuchTransaction"] += 1;
+ expectedStats.commitTypes.recoverWithToken.initiated += 1;
+ // The participant stats shouldn't increase if we're recovering commit.
+ // There are no implicit aborts during commit recovery, so the recovery shard is targeted
+ // once.
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Empty recovery token.");
+(() => {
+ otherRouterSession.startTransaction();
+ let resWithEmptyRecoveryToken =
+ assert.commandWorked(otherRouterSessionDB.runCommand({find: collName}));
+ assert.commandWorked(otherRouterSession.commitTransaction_forTesting());
+
+ // The stats on the main mongos shouldn't have changed.
+ verifyServerStatusValues(st, expectedStats);
+
+ assert.commandFailedWithCode(st.s.adminCommand({
+ commitTransaction: 1,
+ lsid: otherRouterSession.getSessionId(),
+ txnNumber: otherRouterSession.getTxnNumber_forTesting(),
+ autocommit: false,
+ recoveryToken: resWithEmptyRecoveryToken.recoveryToken
+ }),
+ ErrorCodes.NoSuchTransaction);
+
+ expectedStats.totalStarted += 1;
+ expectedStats.commitTypes.recoverWithToken.initiated += 1;
+ // No requests are targeted and the decision isn't learned, so total committed/aborted and
+ // total requests sent shouldn't change.
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Explicitly aborted transaction.");
+(() => {
+ session.startTransaction();
+ assert.commandWorked(sessionDB[collName].insert({x: 2}));
+
+ expectedStats.totalStarted += 1;
+ expectedStats.totalContactedParticipants += 1;
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+
+ assert.commandWorked(session.abortTransaction_forTesting());
+
+ expectedStats.totalAborted += 1;
+ expectedStats.abortCause["abort"] = 1;
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Implicitly aborted transaction.");
+(() => {
+ session.startTransaction();
+ assert.commandFailedWithCode(sessionDB[collName].insert({_id: 1}), ErrorCodes.DuplicateKey);
+
+ expectedStats.totalStarted += 1;
+ expectedStats.totalAborted += 1;
+ expectedStats.abortCause["DuplicateKey"] = 1;
+ expectedStats.totalContactedParticipants += 1;
+ expectedStats.totalRequestsTargeted += 2; // Plus one for the implicit abort.
+ verifyServerStatusValues(st, expectedStats);
+
+ assert.commandFailedWithCode(session.abortTransaction_forTesting(),
+ ErrorCodes.NoSuchTransaction);
+
+ // A failed abortTransaction leads to an implicit abort, so two requests are targeted.
+ expectedStats.totalRequestsTargeted += 2;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+jsTest.log("Abandoned transaction.");
+(() => {
+ session.startTransaction();
+ assert.commandWorked(sessionDB[collName].insert({_id: -15}));
+
+ expectedStats.totalStarted += 1;
+ expectedStats.totalContactedParticipants += 1;
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+
+ session.startTransaction_forTesting({}, {ignoreActiveTxn: true});
+ assert.commandWorked(sessionDB[collName].insert({_id: -15}));
+
+ expectedStats.totalStarted += 1;
+ expectedStats.totalContactedParticipants += 1;
+ expectedStats.totalRequestsTargeted += 1;
+ // The router never learned if the previous transaction committed or aborted, so the aborted
+ // counter shouldn't be incremented.
+ verifyServerStatusValues(st, expectedStats);
+
+ // Abort to clear the shell's session state.
+ assert.commandWorked(session.abortTransaction_forTesting());
+
+ expectedStats.totalAborted += 1;
+ expectedStats.abortCause["abort"] += 1;
+ expectedStats.totalRequestsTargeted += 1;
+ verifyServerStatusValues(st, expectedStats);
+})();
+
+session.endSession();
+st.stop();
}());