1
1
/**
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 .
4
4
*
5
5
* @tags : [
6
6
* requires_fcv_70,
@@ -53,9 +53,12 @@ CreateShardedCollectionUtil.shardCollectionWithChunks(sourceCollection, {key: 1}
53
53
// Insert a document into each shard.
54
54
assert . commandWorked ( sourceCollection . insert ( [ { key : 200 } , { key : - 200 } ] ) ) ;
55
55
56
+ const txnCoordinator = st . rs1 . getPrimary ( ) ;
57
+
56
58
// Create a thread which leaves the TransactionCoordinator in a state where prepareTransaction has
57
59
// been run on both participant shards and it is about to write the commit decision locally to the
58
60
// config.transaction_coordinators collection.
61
+ const hangBeforeWritingDecisionFp = configureFailPoint ( txnCoordinator , "hangBeforeWritingDecision" ) ;
59
62
const preparedTxnThread = new Thread ( function runTwoPhaseCommitTxn ( host , dbName , collName ) {
60
63
const conn = new Mongo ( host ) ;
61
64
const session = conn . startSession ( { causalConsistency : false } ) ;
@@ -66,12 +69,26 @@ const preparedTxnThread = new Thread(function runTwoPhaseCommitTxn(host, dbName,
66
69
assert . commandWorked ( sessionCollection . update ( { key : - 200 } , { $inc : { counter : 1 } } ) ) ;
67
70
assert . commandWorked ( session . commitTransaction_forTesting ( ) ) ;
68
71
} , st . s . host , sourceCollection . getDB ( ) . getName ( ) , sourceCollection . getName ( ) ) ;
69
-
70
- const txnCoordinator = st . rs1 . getPrimary ( ) ;
71
- const hangBeforeWritingDecisionFp = configureFailPoint ( txnCoordinator , "hangBeforeWritingDecision" ) ;
72
-
73
72
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 ( ) ;
75
92
76
93
// Create other threads which will block on a prepare conflict while still holding a write ticket to
77
94
// test that the TransactionCoordinator from preparedTxnThread can still complete.
@@ -84,7 +101,7 @@ for (let i = 0; i < kNumWriteTickets; ++i) {
84
101
85
102
session . startTransaction ( ) ;
86
103
// 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 } ) ) ;
88
105
// Then do a read which will block until the prepare conflict resolves.
89
106
assert . eq ( { key : 200 , counter : 1 } , sessionCollection . findOne ( { key : 200 } , { _id : 0 } ) ) ;
90
107
assert . commandWorked ( session . commitTransaction_forTesting ( ) ) ;
@@ -100,9 +117,23 @@ assert.soon(() => {
100
117
return ops . length >= Math . min ( prepareConflictThreads . length , kNumWriteTickets ) ;
101
118
} , ( ) => `Failed to find prepare conflicts in $currentOp output: ${ tojson ( currentOp ( ) ) } ` ) ;
102
119
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.
103
130
hangBeforeWritingDecisionFp . off ( ) ;
104
131
132
+ jsTestLog ( "Waiting for both transactions to successfully commit" ) ;
105
133
preparedTxnThread . join ( ) ;
134
+ commitTxnThread . join ( ) ;
135
+
136
+ jsTestLog ( "Waiting for all transactional reads to complete" ) ;
106
137
for ( let thread of prepareConflictThreads ) {
107
138
thread . join ( ) ;
108
139
}
0 commit comments