|
| 1 | +// SERVER-38015 Test having many interactive transactions to ensure we don't hold on to too |
| 2 | +// many resources (like "write tickets") and don't prevent other operations from succeeding. |
| 3 | +// @tags: [uses_transactions] |
| 4 | +(function() { |
| 5 | + "use strict"; |
| 6 | + |
| 7 | + const dbName = "test"; |
| 8 | + const collName = "many_txns"; |
| 9 | + const numTxns = 150; |
| 10 | + |
| 11 | + const testDB = db.getSiblingDB(dbName); |
| 12 | + const coll = testDB[collName]; |
| 13 | + |
| 14 | + testDB.runCommand({drop: collName, writeConcern: {w: "majority"}}); |
| 15 | + assert.commandWorked( |
| 16 | + testDB.runCommand({create: coll.getName(), writeConcern: {w: "majority"}})); |
| 17 | + |
| 18 | + const sessionOptions = {causalConsistency: false}; |
| 19 | + |
| 20 | + const startTime = new Date(); |
| 21 | + |
| 22 | + // Non-transactional write to give something to find. |
| 23 | + const initialDoc = {_id: "pretransaction1", x: 0}; |
| 24 | + assert.commandWorked(coll.insert(initialDoc, {writeConcern: {w: "majority"}})); |
| 25 | + |
| 26 | + // Start many transactions, each inserting two documents. |
| 27 | + jsTest.log("Start " + numTxns + " transactions, each inserting two documents"); |
| 28 | + var sessions = []; |
| 29 | + for (let txnNr = 0; txnNr < numTxns; ++txnNr) { |
| 30 | + const session = testDB.getMongo().startSession(sessionOptions); |
| 31 | + sessions[txnNr] = session; |
| 32 | + const sessionDb = session.getDatabase(dbName); |
| 33 | + const sessionColl = sessionDb[collName]; |
| 34 | + let doc = seq => ({_id: "txn-" + txnNr + "-" + seq}); |
| 35 | + |
| 36 | + session.startTransaction(); |
| 37 | + |
| 38 | + let docs = sessionColl.find({}).toArray(); |
| 39 | + assert.sameMembers(docs, [initialDoc]); |
| 40 | + |
| 41 | + // Insert a doc within the transaction. |
| 42 | + assert.commandWorked(sessionColl.insert(doc(1))); |
| 43 | + |
| 44 | + // Read in the same transaction returns the doc, but not from other txns. |
| 45 | + docs = sessionColl.find({_id: {$ne: initialDoc._id}}).toArray(); |
| 46 | + assert.sameMembers(docs, [doc(1)]); |
| 47 | + |
| 48 | + // Insert a doc within a transaction. |
| 49 | + assert.commandWorked(sessionColl.insert(doc(2))); |
| 50 | + } |
| 51 | + const secondDoc = {_id: "midtransactions", x: 1}; |
| 52 | + assert.commandWorked(coll.insert(secondDoc, {writeConcern: {w: "majority"}})); |
| 53 | + |
| 54 | + // Commit all sessions. |
| 55 | + jsTest.log("Commit all transactions."); |
| 56 | + let numAborted = 0; |
| 57 | + for (let txnNr = 0; txnNr < numTxns; ++txnNr) { |
| 58 | + // First check that a non-transactional operation conflicts and times out quickly. |
| 59 | + let doc = seq => ({_id: "txn-" + txnNr + "-" + seq}); |
| 60 | + let insertCmd = {insert: collName, documents: [doc(1)], maxTimeMS: 10}; |
| 61 | + let insertRes = testDB.runCommand(insertCmd); |
| 62 | + |
| 63 | + const session = sessions[txnNr]; |
| 64 | + let commitRes = session.commitTransaction_forTesting(); |
| 65 | + if (commitRes.code === ErrorCodes.NoSuchTransaction) { |
| 66 | + ++numAborted; |
| 67 | + continue; |
| 68 | + } |
| 69 | + assert.commandWorked(commitRes, "couldn't commit transaction " + txnNr); |
| 70 | + assert.commandFailedWithCode(insertRes, ErrorCodes.MaxTimeMSExpired, tojson({insertCmd})); |
| 71 | + |
| 72 | + // Read with default read concern sees the committed transaction. |
| 73 | + assert.eq(doc(1), coll.findOne(doc(1))); |
| 74 | + assert.eq(doc(2), coll.findOne(doc(2))); |
| 75 | + session.endSession(); |
| 76 | + } |
| 77 | + |
| 78 | + assert.eq(initialDoc, coll.findOne(initialDoc)); |
| 79 | + assert.eq(secondDoc, coll.findOne(secondDoc)); |
| 80 | + |
| 81 | + const elapsedTime = new Date() - startTime; |
| 82 | + jsTest.log("Test completed with " + numAborted + " aborted transactions in " + elapsedTime + |
| 83 | + " ms"); |
| 84 | + |
| 85 | + // Check whether we should expect aborts. If the parameter doesn't exist (mongos) don't check. |
| 86 | + const getParamRes = db.adminCommand({getParameter: 1, transactionLifetimeLimitSeconds: 1}); |
| 87 | + if (getParamRes.ok && elapsedTime < getParamRes.transactionLifetimeLimitSeconds) |
| 88 | + assert.eq(numAborted, |
| 89 | + 0, |
| 90 | + "should not get aborts when transactionLifetimeLimitSeconds not exceeded"); |
| 91 | +}()); |
0 commit comments