diff options
Diffstat (limited to 'jstests/sharding/txn_commit_optimizations_for_read_only_shards.js')
-rw-r--r-- | jstests/sharding/txn_commit_optimizations_for_read_only_shards.js | 645 |
1 files changed, 321 insertions, 324 deletions
diff --git a/jstests/sharding/txn_commit_optimizations_for_read_only_shards.js b/jstests/sharding/txn_commit_optimizations_for_read_only_shards.js index ce643ed3ad1..fc6137f2ff7 100644 --- a/jstests/sharding/txn_commit_optimizations_for_read_only_shards.js +++ b/jstests/sharding/txn_commit_optimizations_for_read_only_shards.js @@ -10,355 +10,352 @@ */ (function() { - 'use strict'; - - load("jstests/libs/write_concern_util.js"); - load("jstests/sharding/libs/sharded_transactions_helpers.js"); - - // Waits for the given log to appear a number of times in the shell's rawMongoProgramOutput. - // Loops because it is not guaranteed the program output will immediately contain all lines - // logged at an earlier wall clock time. - function waitForLog(logLine, times) { - assert.soon(function() { - const matches = rawMongoProgramOutput().match(new RegExp(logLine, "g")) || []; - return matches.length === times; - }, 'Failed to find "' + logLine + '" logged ' + times + ' times'); - } - - const addTxnFields = function(command, lsid, txnNumber, startTransaction) { - let txnFields = { - lsid: lsid, - txnNumber: NumberLong(txnNumber), - stmtId: NumberInt(0), - autocommit: false, - }; - if (startTransaction) { - txnFields.startTransaction = true; - } - return Object.assign({}, command, txnFields); - }; - - const defaultCommitCommand = { - commitTransaction: 1, - writeConcern: {w: "majority", wtimeout: 6000} - }; - const noop = () => {}; - - const dbName = "test"; - const collName = "foo"; - const ns = dbName + "." + collName; - - // TODO (SERVER-37364): Uncomment this line; otherwise, the coordinator will wait too long to - // time out waiting for votes and the test will time out. - // Lower the transaction timeout, since this test exercises cases where the coordinator should - // time out collecting prepare votes. - // TestData.transactionLifetimeLimitSeconds = 30; - - let st = new ShardingTest({ - shards: 3, - // Create shards with more than one node because we test for writeConcern majority failing. - config: 1, - other: { - mongosOptions: {verbose: 3}, - rs0: {nodes: [{}, {rsConfig: {priority: 0}}]}, - rs1: {nodes: [{}, {rsConfig: {priority: 0}}]}, - rs2: {nodes: [{}, {rsConfig: {priority: 0}}]}, - }, - }); - - assert.commandWorked(st.s.adminCommand({enableSharding: dbName})); - assert.commandWorked(st.s.adminCommand({movePrimary: dbName, to: st.shard1.shardName})); - - // Create a "dummy" collection for doing noop writes to advance shard's last applied OpTimes. - assert.commandWorked(st.s.getDB(dbName).getCollection("dummy").insert({dummy: 1})); - - // The test uses three shards with one chunk each in order to control which shards are targeted - // for each statement: - // - // (-inf, 0): shard key = txnNumber * -1 - // (0, MAX_TRANSACTIONS): shard key = txnNumber - // (MAX_TRANSACTIONS, +inf): shard key = txnNumber + MAX_TRANSACTIONS - // - // So, if the test ever exceeds txnNumber transactions, statements that are meant to target the - // middle chunk will instead target the highest chunk. To fix this, increase MAX_TRANSACTIONS. - const MAX_TRANSACTIONS = 10000; - - // Create a sharded collection with a chunk on each shard: - 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({split: ns, middle: {_id: MAX_TRANSACTIONS}})); - assert.commandWorked( - st.s.adminCommand({moveChunk: ns, find: {_id: -1}, to: st.shard0.shardName})); - assert.commandWorked( - st.s.adminCommand({moveChunk: ns, find: {_id: MAX_TRANSACTIONS}, to: st.shard2.shardName})); - - // Insert something into each chunk so that a multi-update actually results in a write on each - // shard (otherwise the shard may remain read-only). This also ensures all the routers and - // shards have fresh routing table caches, so they do not need to be refreshed separately. - assert.commandWorked(st.s.getDB(dbName).runCommand({ - insert: collName, - documents: [{_id: -1 * MAX_TRANSACTIONS}, {_id: 0}, {_id: MAX_TRANSACTIONS}] - })); - - let lsid = {id: UUID()}; - let txnNumber = 1; - - const readShard0 = txnNumber => { - return {find: collName, filter: {_id: (-1 * txnNumber)}}; +'use strict'; + +load("jstests/libs/write_concern_util.js"); +load("jstests/sharding/libs/sharded_transactions_helpers.js"); + +// Waits for the given log to appear a number of times in the shell's rawMongoProgramOutput. +// Loops because it is not guaranteed the program output will immediately contain all lines +// logged at an earlier wall clock time. +function waitForLog(logLine, times) { + assert.soon(function() { + const matches = rawMongoProgramOutput().match(new RegExp(logLine, "g")) || []; + return matches.length === times; + }, 'Failed to find "' + logLine + '" logged ' + times + ' times'); +} + +const addTxnFields = function(command, lsid, txnNumber, startTransaction) { + let txnFields = { + lsid: lsid, + txnNumber: NumberLong(txnNumber), + stmtId: NumberInt(0), + autocommit: false, }; - - const readShard1 = txnNumber => { - return {find: collName, filter: {_id: txnNumber}}; - }; - - const readShard2 = txnNumber => { - return {find: collName, filter: {_id: (MAX_TRANSACTIONS + txnNumber)}}; - }; - - const readAllShards = () => { - return {find: collName}; - }; - - const writeShard0 = txnNumber => { - return { - update: collName, - updates: [ - {q: {_id: (txnNumber * -1)}, u: {_id: (txnNumber * -1), updated: 1}, upsert: true} - ], - }; + if (startTransaction) { + txnFields.startTransaction = true; + } + return Object.assign({}, command, txnFields); +}; + +const defaultCommitCommand = { + commitTransaction: 1, + writeConcern: {w: "majority", wtimeout: 6000} +}; +const noop = () => {}; + +const dbName = "test"; +const collName = "foo"; +const ns = dbName + "." + collName; + +// TODO (SERVER-37364): Uncomment this line; otherwise, the coordinator will wait too long to +// time out waiting for votes and the test will time out. +// Lower the transaction timeout, since this test exercises cases where the coordinator should +// time out collecting prepare votes. +// TestData.transactionLifetimeLimitSeconds = 30; + +let st = new ShardingTest({ + shards: 3, + // Create shards with more than one node because we test for writeConcern majority failing. + config: 1, + other: { + mongosOptions: {verbose: 3}, + rs0: {nodes: [{}, {rsConfig: {priority: 0}}]}, + rs1: {nodes: [{}, {rsConfig: {priority: 0}}]}, + rs2: {nodes: [{}, {rsConfig: {priority: 0}}]}, + }, +}); + +assert.commandWorked(st.s.adminCommand({enableSharding: dbName})); +assert.commandWorked(st.s.adminCommand({movePrimary: dbName, to: st.shard1.shardName})); + +// Create a "dummy" collection for doing noop writes to advance shard's last applied OpTimes. +assert.commandWorked(st.s.getDB(dbName).getCollection("dummy").insert({dummy: 1})); + +// The test uses three shards with one chunk each in order to control which shards are targeted +// for each statement: +// +// (-inf, 0): shard key = txnNumber * -1 +// (0, MAX_TRANSACTIONS): shard key = txnNumber +// (MAX_TRANSACTIONS, +inf): shard key = txnNumber + MAX_TRANSACTIONS +// +// So, if the test ever exceeds txnNumber transactions, statements that are meant to target the +// middle chunk will instead target the highest chunk. To fix this, increase MAX_TRANSACTIONS. +const MAX_TRANSACTIONS = 10000; + +// Create a sharded collection with a chunk on each shard: +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({split: ns, middle: {_id: MAX_TRANSACTIONS}})); +assert.commandWorked(st.s.adminCommand({moveChunk: ns, find: {_id: -1}, to: st.shard0.shardName})); +assert.commandWorked( + st.s.adminCommand({moveChunk: ns, find: {_id: MAX_TRANSACTIONS}, to: st.shard2.shardName})); + +// Insert something into each chunk so that a multi-update actually results in a write on each +// shard (otherwise the shard may remain read-only). This also ensures all the routers and +// shards have fresh routing table caches, so they do not need to be refreshed separately. +assert.commandWorked(st.s.getDB(dbName).runCommand({ + insert: collName, + documents: [{_id: -1 * MAX_TRANSACTIONS}, {_id: 0}, {_id: MAX_TRANSACTIONS}] +})); + +let lsid = {id: UUID()}; +let txnNumber = 1; + +const readShard0 = txnNumber => { + return {find: collName, filter: {_id: (-1 * txnNumber)}}; +}; + +const readShard1 = txnNumber => { + return {find: collName, filter: {_id: txnNumber}}; +}; + +const readShard2 = txnNumber => { + return {find: collName, filter: {_id: (MAX_TRANSACTIONS + txnNumber)}}; +}; + +const readAllShards = () => { + return {find: collName}; +}; + +const writeShard0 = txnNumber => { + return { + update: collName, + updates: + [{q: {_id: (txnNumber * -1)}, u: {_id: (txnNumber * -1), updated: 1}, upsert: true}], }; +}; - const writeShard1 = txnNumber => { - return { - update: collName, - updates: [{q: {_id: txnNumber}, u: {_id: txnNumber, updated: 1}, upsert: true}], - }; +const writeShard1 = txnNumber => { + return { + update: collName, + updates: [{q: {_id: txnNumber}, u: {_id: txnNumber, updated: 1}, upsert: true}], }; - - const writeShard2 = txnNumber => { - return { - update: collName, - updates: [{ - q: {_id: (txnNumber + MAX_TRANSACTIONS)}, - u: {_id: (txnNumber + MAX_TRANSACTIONS), updated: 1}, - upsert: true - }], - }; +}; + +const writeShard2 = txnNumber => { + return { + update: collName, + updates: [{ + q: {_id: (txnNumber + MAX_TRANSACTIONS)}, + u: {_id: (txnNumber + MAX_TRANSACTIONS), updated: 1}, + upsert: true + }], }; +}; - const writeAllShards = () => { - return { - update: collName, - updates: [{q: {}, u: {$inc: {updated: 1}}, multi: true}], - }; +const writeAllShards = () => { + return { + update: collName, + updates: [{q: {}, u: {$inc: {updated: 1}}, multi: true}], }; - - // For each transaction type, contains the list of statements for that type. - const transactionTypes = { - readOnlySingleShardSingleStatementExpectSingleShardCommit: txnNumber => { - return [readShard0(txnNumber)]; - }, - readOnlySingleShardMultiStatementExpectSingleShardCommit: txnNumber => { - return [readShard0(txnNumber), readShard0(txnNumber)]; +}; + +// For each transaction type, contains the list of statements for that type. +const transactionTypes = { + readOnlySingleShardSingleStatementExpectSingleShardCommit: txnNumber => { + return [readShard0(txnNumber)]; + }, + readOnlySingleShardMultiStatementExpectSingleShardCommit: txnNumber => { + return [readShard0(txnNumber), readShard0(txnNumber)]; + }, + readOnlyMultiShardSingleStatementExpectReadOnlyCommit: txnNumber => { + return [readAllShards(txnNumber)]; + }, + readOnlyMultiShardMultiStatementExpectReadOnlyCommit: txnNumber => { + return [readShard0(txnNumber), readShard1(txnNumber), readShard2(txnNumber)]; + }, + writeSingleShardSingleStatementExpectSingleShardCommit: txnNumber => { + return [writeShard0(txnNumber)]; + }, + writeSingleShardMultiStatementExpectSingleShardCommit: txnNumber => { + return [writeShard0(txnNumber), writeShard0(txnNumber)]; + }, + writeMultiShardSingleStatementExpectTwoPhaseCommit: txnNumber => { + return [writeAllShards(txnNumber)]; + }, + writeMultiShardMultiStatementExpectTwoPhaseCommit: txnNumber => { + return [writeShard0(txnNumber), writeShard1(txnNumber), writeShard2(txnNumber)]; + }, + readWriteSingleShardExpectSingleShardCommit: txnNumber => { + return [readShard0(txnNumber), writeShard0(txnNumber)]; + }, + writeReadSingleShardExpectSingleShardCommit: txnNumber => { + return [writeShard0(txnNumber), readShard0(txnNumber)]; + }, + readOneShardWriteOtherShardExpectSingleWriteShardCommit: txnNumber => { + return [readShard0(txnNumber), writeShard1(txnNumber)]; + }, + writeOneShardReadOtherShardExpectSingleWriteShardCommit: txnNumber => { + return [writeShard0(txnNumber), readShard1(txnNumber)]; + }, + readOneShardWriteTwoOtherShardsExpectTwoPhaseCommit: txnNumber => { + return [readShard0(txnNumber), writeShard1(txnNumber), writeShard2(txnNumber)]; + }, + writeTwoShardsReadOneOtherShardExpectTwoPhaseCommit: txnNumber => { + return [writeShard0(txnNumber), writeShard1(txnNumber), readShard2(txnNumber)]; + }, +}; + +const failureModes = { + noFailures: { + beforeStatements: noop, + beforeCommit: noop, + getCommitCommand: (lsid, txnNumber) => { + return addTxnFields(defaultCommitCommand, lsid, txnNumber); }, - readOnlyMultiShardSingleStatementExpectReadOnlyCommit: txnNumber => { - return [readAllShards(txnNumber)]; + checkCommitResult: (res) => { + // Commit should return ok without writeConcern error + assert.commandWorked(res); + assert.eq(null, res.errorLabels); }, - readOnlyMultiShardMultiStatementExpectReadOnlyCommit: txnNumber => { - return [readShard0(txnNumber), readShard1(txnNumber), readShard2(txnNumber)]; + cleanUp: noop, + }, + participantStepsDownBeforeClientSendsCommit: { + beforeStatements: noop, + beforeCommit: () => { + // Participant primary steps down. + assert.commandWorked( + st.shard0.adminCommand({replSetStepDown: 1 /* stepDownSecs */, force: true})); }, - writeSingleShardSingleStatementExpectSingleShardCommit: txnNumber => { - return [writeShard0(txnNumber)]; + getCommitCommand: (lsid, txnNumber) => { + return addTxnFields(defaultCommitCommand, lsid, txnNumber); }, - writeSingleShardMultiStatementExpectSingleShardCommit: txnNumber => { - return [writeShard0(txnNumber), writeShard0(txnNumber)]; + checkCommitResult: (res) => { + // Commit should return NoSuchTransaction. + assert.commandFailedWithCode(res, ErrorCodes.NoSuchTransaction); + assert.eq(["TransientTransactionError"], res.errorLabels); }, - writeMultiShardSingleStatementExpectTwoPhaseCommit: txnNumber => { - return [writeAllShards(txnNumber)]; + cleanUp: () => { + st.rs0.awaitNodesAgreeOnPrimary(); }, - writeMultiShardMultiStatementExpectTwoPhaseCommit: txnNumber => { - return [writeShard0(txnNumber), writeShard1(txnNumber), writeShard2(txnNumber)]; + }, + participantCannotMajorityCommitWritesClientSendsWriteConcernMajority: { + beforeStatements: () => { + // Participant cannot majority commit writes. + stopServerReplication(st.rs0.getSecondaries()); + + // Do a write on rs0 through the router outside the transaction to ensure the + // transaction will choose a read time that has not been majority committed. + assert.commandWorked(st.s.getDB(dbName).getCollection("dummy").insert({dummy: 1})); }, - readWriteSingleShardExpectSingleShardCommit: txnNumber => { - return [readShard0(txnNumber), writeShard0(txnNumber)]; + beforeCommit: noop, + getCommitCommand: (lsid, txnNumber) => { + return addTxnFields(defaultCommitCommand, lsid, txnNumber); }, - writeReadSingleShardExpectSingleShardCommit: txnNumber => { - return [writeShard0(txnNumber), readShard0(txnNumber)]; + checkCommitResult: (res) => { + // Commit should return ok with a writeConcernError with wtimeout. + assert.commandWorkedIgnoringWriteConcernErrors(res); + checkWriteConcernTimedOut(res); + assert.eq(null, res.errorLabels); }, - readOneShardWriteOtherShardExpectSingleWriteShardCommit: txnNumber => { - return [readShard0(txnNumber), writeShard1(txnNumber)]; + cleanUp: () => { + restartServerReplication(st.rs0.getSecondaries()); }, - writeOneShardReadOtherShardExpectSingleWriteShardCommit: txnNumber => { - return [writeShard0(txnNumber), readShard1(txnNumber)]; + }, + participantCannotMajorityCommitWritesClientSendsWriteConcern1: { + beforeStatements: () => { + // Participant cannot majority commit writes. + stopServerReplication(st.rs0.getSecondaries()); + + // Do a write on rs0 through the router outside the transaction to ensure the + // transaction will choose a read time that has not been majority committed. + assert.commandWorked(st.s.getDB(dbName).getCollection("dummy").insert({dummy: 1})); }, - readOneShardWriteTwoOtherShardsExpectTwoPhaseCommit: txnNumber => { - return [readShard0(txnNumber), writeShard1(txnNumber), writeShard2(txnNumber)]; + beforeCommit: noop, + getCommitCommand: (lsid, txnNumber) => { + return addTxnFields({commitTransaction: 1, writeConcern: {w: 1}}, lsid, txnNumber); }, - writeTwoShardsReadOneOtherShardExpectTwoPhaseCommit: txnNumber => { - return [writeShard0(txnNumber), writeShard1(txnNumber), readShard2(txnNumber)]; - }, - }; - - const failureModes = { - noFailures: { - beforeStatements: noop, - beforeCommit: noop, - getCommitCommand: (lsid, txnNumber) => { - return addTxnFields(defaultCommitCommand, lsid, txnNumber); - }, - checkCommitResult: (res) => { - // Commit should return ok without writeConcern error - assert.commandWorked(res); - assert.eq(null, res.errorLabels); - }, - cleanUp: noop, + checkCommitResult: (res) => { + // Commit should return ok without writeConcern error + assert.commandWorked(res); + assert.eq(null, res.errorLabels); }, - participantStepsDownBeforeClientSendsCommit: { - beforeStatements: noop, - beforeCommit: () => { - // Participant primary steps down. - assert.commandWorked( - st.shard0.adminCommand({replSetStepDown: 1 /* stepDownSecs */, force: true})); - }, - getCommitCommand: (lsid, txnNumber) => { - return addTxnFields(defaultCommitCommand, lsid, txnNumber); - }, - checkCommitResult: (res) => { - // Commit should return NoSuchTransaction. - assert.commandFailedWithCode(res, ErrorCodes.NoSuchTransaction); - assert.eq(["TransientTransactionError"], res.errorLabels); - }, - cleanUp: () => { - st.rs0.awaitNodesAgreeOnPrimary(); - }, + cleanUp: () => { + restartServerReplication(st.rs0.getSecondaries()); }, - participantCannotMajorityCommitWritesClientSendsWriteConcernMajority: { - beforeStatements: () => { - // Participant cannot majority commit writes. - stopServerReplication(st.rs0.getSecondaries()); - - // Do a write on rs0 through the router outside the transaction to ensure the - // transaction will choose a read time that has not been majority committed. - assert.commandWorked(st.s.getDB(dbName).getCollection("dummy").insert({dummy: 1})); - }, - beforeCommit: noop, - getCommitCommand: (lsid, txnNumber) => { - return addTxnFields(defaultCommitCommand, lsid, txnNumber); - }, - checkCommitResult: (res) => { - // Commit should return ok with a writeConcernError with wtimeout. - assert.commandWorkedIgnoringWriteConcernErrors(res); - checkWriteConcernTimedOut(res); - assert.eq(null, res.errorLabels); - }, - cleanUp: () => { - restartServerReplication(st.rs0.getSecondaries()); - }, + }, + clientSendsInvalidWriteConcernOnCommit: { + beforeStatements: noop, + beforeCommit: noop, + getCommitCommand: (lsid, txnNumber) => { + // Client sends invalid writeConcern on commit. + return addTxnFields( + {commitTransaction: 1, writeConcern: {w: "invalid"}}, lsid, txnNumber); }, - participantCannotMajorityCommitWritesClientSendsWriteConcern1: { - beforeStatements: () => { - // Participant cannot majority commit writes. - stopServerReplication(st.rs0.getSecondaries()); - - // Do a write on rs0 through the router outside the transaction to ensure the - // transaction will choose a read time that has not been majority committed. - assert.commandWorked(st.s.getDB(dbName).getCollection("dummy").insert({dummy: 1})); - }, - beforeCommit: noop, - getCommitCommand: (lsid, txnNumber) => { - return addTxnFields({commitTransaction: 1, writeConcern: {w: 1}}, lsid, txnNumber); - }, - checkCommitResult: (res) => { - // Commit should return ok without writeConcern error - assert.commandWorked(res); - assert.eq(null, res.errorLabels); - }, - cleanUp: () => { - restartServerReplication(st.rs0.getSecondaries()); - }, + checkCommitResult: (res) => { + // Commit should return ok with writeConcernError without wtimeout. + assert.commandWorkedIgnoringWriteConcernErrors(res); + assertWriteConcernError(res); + assert.eq(ErrorCodes.UnknownReplWriteConcern, res.writeConcernError.code); + assert.eq(null, res.writeConcernError.errInfo); // errInfo only set for wtimeout + assert.eq(null, res.errorLabels); }, - clientSendsInvalidWriteConcernOnCommit: { - beforeStatements: noop, - beforeCommit: noop, - getCommitCommand: (lsid, txnNumber) => { - // Client sends invalid writeConcern on commit. - return addTxnFields( - {commitTransaction: 1, writeConcern: {w: "invalid"}}, lsid, txnNumber); - }, - checkCommitResult: (res) => { - // Commit should return ok with writeConcernError without wtimeout. - assert.commandWorkedIgnoringWriteConcernErrors(res); - assertWriteConcernError(res); - assert.eq(ErrorCodes.UnknownReplWriteConcern, res.writeConcernError.code); - assert.eq(null, res.writeConcernError.errInfo); // errInfo only set for wtimeout - assert.eq(null, res.errorLabels); - }, - cleanUp: noop, - }, - }; + cleanUp: noop, + }, +}; + +for (const failureModeName in failureModes) { + for (const type in transactionTypes) { + // TODO (SERVER-37364): Unblacklist these test cases once the coordinator returns the + // decision as soon as the decision is made. At the moment, the coordinator makes an + // abort decision after timing out waiting for votes, but coordinateCommitTransaction + // hangs because it waits for the decision to be majority-ack'd by all participants, + // which can't happen while a participant can't majority commit writes. + if (failureModeName.includes("participantCannotMajorityCommitWrites") && + type.includes("ExpectTwoPhaseCommit")) { + jsTest.log( + `${failureModeName} with ${type} is skipped until SERVER-37364 is implemented`); + continue; + } - for (const failureModeName in failureModes) { - for (const type in transactionTypes) { - // TODO (SERVER-37364): Unblacklist these test cases once the coordinator returns the - // decision as soon as the decision is made. At the moment, the coordinator makes an - // abort decision after timing out waiting for votes, but coordinateCommitTransaction - // hangs because it waits for the decision to be majority-ack'd by all participants, - // which can't happen while a participant can't majority commit writes. - if (failureModeName.includes("participantCannotMajorityCommitWrites") && - type.includes("ExpectTwoPhaseCommit")) { - jsTest.log( - `${failureModeName} with ${type} is skipped until SERVER-37364 is implemented`); - continue; - } - - txnNumber++; - assert.lt(txnNumber, - MAX_TRANSACTIONS, - "Test exceeded maximum number of transactions allowable by the test's chunk" + - " distribution created during the test setup. Please increase" + - " MAX_TRANSACTIONS in the test."); - - jsTest.log(`Testing ${failureModeName} with ${type} at txnNumber ${txnNumber}`); - - const failureMode = failureModes[failureModeName]; - - // Run the statements. - failureMode.beforeStatements(); - let startTransaction = true; - transactionTypes[type](txnNumber).forEach(command => { - assert.commandWorked(st.s.getDB(dbName).runCommand( - addTxnFields(command, lsid, txnNumber, startTransaction))); - startTransaction = false; - }); - - // Run commit. - const commitCmd = failureMode.getCommitCommand(lsid, txnNumber); - failureMode.beforeCommit(); - const commitRes = st.s.adminCommand(commitCmd); - failureMode.checkCommitResult(commitRes); - - // Re-running commit should return the same response. - const commitRetryRes = st.s.adminCommand(commitCmd); - failureMode.checkCommitResult(commitRetryRes); - - if (type.includes("ExpectSingleShardCommit")) { - waitForLog("Committing single-shard transaction", 2); - } else if (type.includes("ExpectReadOnlyCommit")) { - waitForLog("Committing read-only transaction", 2); - } else if (type.includes("ExpectSingleWriteShardCommit")) { - waitForLog("Committing single-write-shard transaction", 2); - } else if (type.includes("ExpectTwoPhaseCommit")) { - waitForLog("Committing using two-phase commit", 2); - } else { - assert(false, `Unknown transaction type: ${type}`); - } - - clearRawMongoProgramOutput(); - - failureMode.cleanUp(); + txnNumber++; + assert.lt(txnNumber, + MAX_TRANSACTIONS, + "Test exceeded maximum number of transactions allowable by the test's chunk" + + " distribution created during the test setup. Please increase" + + " MAX_TRANSACTIONS in the test."); + + jsTest.log(`Testing ${failureModeName} with ${type} at txnNumber ${txnNumber}`); + + const failureMode = failureModes[failureModeName]; + + // Run the statements. + failureMode.beforeStatements(); + let startTransaction = true; + transactionTypes[type](txnNumber).forEach(command => { + assert.commandWorked(st.s.getDB(dbName).runCommand( + addTxnFields(command, lsid, txnNumber, startTransaction))); + startTransaction = false; + }); + + // Run commit. + const commitCmd = failureMode.getCommitCommand(lsid, txnNumber); + failureMode.beforeCommit(); + const commitRes = st.s.adminCommand(commitCmd); + failureMode.checkCommitResult(commitRes); + + // Re-running commit should return the same response. + const commitRetryRes = st.s.adminCommand(commitCmd); + failureMode.checkCommitResult(commitRetryRes); + + if (type.includes("ExpectSingleShardCommit")) { + waitForLog("Committing single-shard transaction", 2); + } else if (type.includes("ExpectReadOnlyCommit")) { + waitForLog("Committing read-only transaction", 2); + } else if (type.includes("ExpectSingleWriteShardCommit")) { + waitForLog("Committing single-write-shard transaction", 2); + } else if (type.includes("ExpectTwoPhaseCommit")) { + waitForLog("Committing using two-phase commit", 2); + } else { + assert(false, `Unknown transaction type: ${type}`); } - } - st.stop(); + clearRawMongoProgramOutput(); + + failureMode.cleanUp(); + } +} +st.stop(); })(); |