Skip to content

Commit 12ca55a

Browse files
brad-cater-mongodbMongoDB Bot
authored andcommitted
SERVER-92292 Skip ticket acquisition for prepareTransaction to v7.0 (#29142)
GitOrigin-RevId: 88412b9d995efe255b661882e1ce002a13f7a360
1 parent 88adfe6 commit 12ca55a

File tree

4 files changed

+49
-166
lines changed

4 files changed

+49
-166
lines changed

etc/backports_required_for_multiversion_tests.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,8 @@ last-continuous:
549549
ticket: SERVER-91195
550550
- test_file: jstests/core/timeseries/timeseries_update_mixed_schema_bucket.js
551551
ticket: SERVER-91195
552+
- test_file: jstests/sharding/coordinate_txn_commit_with_tickets_exhausted.js
553+
ticket: SERVER-92292
552554
- test_file: jstests/core/sparse_index_supports_ne_null.js
553555
ticket: SERVER-37164
554556
- test_file: jstests/core/timeseries/timeseries_insert_mixed_schema_bucket.js
@@ -996,8 +998,6 @@ last-lts:
996998
ticket: SERVER-76012
997999
- test_file: jstests/replsets/transactions_reaped_with_tickets_exhausted.js
9981000
ticket: SERVER-76012
999-
- test_file: jstests/sharding/cancel_coordinate_txn_commit_with_tickets_exhausted.js
1000-
ticket: SERVER-76012
10011001
- test_file: jstests/sharding/coordinate_txn_commit_with_tickets_exhausted.js
10021002
ticket: SERVER-76012
10031003
- test_file: jstests/noPassthrough/clustered_collection_sorted_scan.js
@@ -1182,6 +1182,8 @@ last-lts:
11821182
ticket: SERVER-91195
11831183
- test_file: jstests/core/timeseries/timeseries_update_mixed_schema_bucket.js
11841184
ticket: SERVER-91195
1185+
- test_file: jstests/sharding/coordinate_txn_commit_with_tickets_exhausted.js
1186+
ticket: SERVER-92292
11851187
- test_file: jstests/core/sparse_index_supports_ne_null.js
11861188
ticket: SERVER-37164
11871189
- test_file: jstests/core/timeseries/timeseries_insert_mixed_schema_bucket.js

jstests/sharding/cancel_coordinate_txn_commit_with_tickets_exhausted.js

Lines changed: 0 additions & 151 deletions
This file was deleted.

jstests/sharding/coordinate_txn_commit_with_tickets_exhausted.js

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
2-
* Validate SERVER-60682: TransactionCoordinator won't starve for a storage ticket to
3-
* persist its decision.
2+
* Validate SERVER-60682 and SERVER-92292: TransactionCoordinator won't starve for a storage ticket
3+
* to prepare or commit a cross-shard transaction.
44
*
55
* @tags: [
66
* requires_fcv_70,
@@ -53,9 +53,12 @@ CreateShardedCollectionUtil.shardCollectionWithChunks(sourceCollection, {key: 1}
5353
// Insert a document into each shard.
5454
assert.commandWorked(sourceCollection.insert([{key: 200}, {key: -200}]));
5555

56+
const txnCoordinator = st.rs1.getPrimary();
57+
5658
// Create a thread which leaves the TransactionCoordinator in a state where prepareTransaction has
5759
// been run on both participant shards and it is about to write the commit decision locally to the
5860
// config.transaction_coordinators collection.
61+
const hangBeforeWritingDecisionFp = configureFailPoint(txnCoordinator, "hangBeforeWritingDecision");
5962
const preparedTxnThread = new Thread(function runTwoPhaseCommitTxn(host, dbName, collName) {
6063
const conn = new Mongo(host);
6164
const session = conn.startSession({causalConsistency: false});
@@ -66,12 +69,26 @@ const preparedTxnThread = new Thread(function runTwoPhaseCommitTxn(host, dbName,
6669
assert.commandWorked(sessionCollection.update({key: -200}, {$inc: {counter: 1}}));
6770
assert.commandWorked(session.commitTransaction_forTesting());
6871
}, st.s.host, sourceCollection.getDB().getName(), sourceCollection.getName());
69-
70-
const txnCoordinator = st.rs1.getPrimary();
71-
const hangBeforeWritingDecisionFp = configureFailPoint(txnCoordinator, "hangBeforeWritingDecision");
72-
7372
preparedTxnThread.start();
74-
hangBeforeWritingDecisionFp.wait();
73+
hangBeforeWritingDecisionFp.wait({timesEntered: 1});
74+
75+
// Create a thread which leaves the TransactionCoordinator in a state where write operations has
76+
// been run on both participant shards and it is about to start the two-phase-commit protocol (e.g.
77+
// before writing the participant list).
78+
const hangBeforeWritingParticipantListFp =
79+
configureFailPoint(txnCoordinator, "hangBeforeWritingParticipantList");
80+
const commitTxnThread = new Thread(function runTwoPhaseCommitTxn(host, dbName, collName) {
81+
const conn = new Mongo(host);
82+
const session = conn.startSession({causalConsistency: false});
83+
const sessionCollection = session.getDatabase(dbName).getCollection(collName);
84+
85+
session.startTransaction();
86+
assert.commandWorked(sessionCollection.insert({key: 300}));
87+
assert.commandWorked(sessionCollection.insert({key: -300}));
88+
assert.commandWorked(session.commitTransaction_forTesting());
89+
}, st.s.host, sourceCollection.getDB().getName(), sourceCollection.getName());
90+
commitTxnThread.start();
91+
hangBeforeWritingParticipantListFp.wait();
7592

7693
// Create other threads which will block on a prepare conflict while still holding a write ticket to
7794
// test that the TransactionCoordinator from preparedTxnThread can still complete.
@@ -84,7 +101,7 @@ for (let i = 0; i < kNumWriteTickets; ++i) {
84101

85102
session.startTransaction();
86103
// Do a write to ensure the transaction takes a write ticket.
87-
assert.commandWorked(sessionCollection.insert({key: 300}));
104+
assert.commandWorked(sessionCollection.insert({key: 100}));
88105
// Then do a read which will block until the prepare conflict resolves.
89106
assert.eq({key: 200, counter: 1}, sessionCollection.findOne({key: 200}, {_id: 0}));
90107
assert.commandWorked(session.commitTransaction_forTesting());
@@ -100,9 +117,23 @@ assert.soon(() => {
100117
return ops.length >= Math.min(prepareConflictThreads.length, kNumWriteTickets);
101118
}, () => `Failed to find prepare conflicts in $currentOp output: ${tojson(currentOp())}`);
102119

120+
// Allow the commitTxnThread to proceed with preparing the transaction. This tests that we skip
121+
// write ticket acquisition when preparing a transaction.
122+
hangBeforeWritingParticipantListFp.off();
123+
124+
jsTestLog("Waiting for commitTxnThread to successfully prepare the transaction");
125+
hangBeforeWritingDecisionFp.wait({timesEntered: 3});
126+
127+
// Allow both prepared transactions to proceed with committing the transaction. This tests that we
128+
// skip write ticket acquisition when persisting the transaction decision and committing the
129+
// prepared transaction.
103130
hangBeforeWritingDecisionFp.off();
104131

132+
jsTestLog("Waiting for both transactions to successfully commit");
105133
preparedTxnThread.join();
134+
commitTxnThread.join();
135+
136+
jsTestLog("Waiting for all transactional reads to complete");
106137
for (let thread of prepareConflictThreads) {
107138
thread.join();
108139
}

src/mongo/db/transaction/transaction_participant.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1518,11 +1518,12 @@ void TransactionParticipant::Participant::unstashTransactionResources(OperationC
15181518
// commitTransaction and abortTransaction commands can skip ticketing mechanism as they
15191519
// don't acquire any new storage resources (except writing to oplog) but they release
15201520
// any claimed storage resources.
1521-
// Prepared transactions should not acquire ticket. Else, it can deadlock with other
1522-
// non-transactional operations that have exhausted the write tickets and are blocked on
1523-
// them due to prepare or lock conflict.
1524-
if (o().txnState.isPrepared() || cmdName == "commitTransaction" ||
1525-
cmdName == "abortTransaction") {
1521+
// Prepared transactions or attempts to prepare a transaction should not acquire ticket.
1522+
// Else, it can deadlock with other non-transactional operations that have exhausted the
1523+
// write tickets and are blocked on them due to prepare or lock conflict.
1524+
// SERVER-41980 and SERVER-92292
1525+
if (o().txnState.isPrepared() || cmdName == "prepareTransaction" ||
1526+
cmdName == "commitTransaction" || cmdName == "abortTransaction") {
15261527
acquireTicket = AcquireTicket::kSkip;
15271528
}
15281529
} else {

0 commit comments

Comments
 (0)