@@ -72,12 +72,6 @@ txn_opts_copy (const mongoc_transaction_opt_t *src,
72
72
}
73
73
74
74
75
- typedef enum {
76
- TXN_COMMIT ,
77
- TXN_ABORT ,
78
- } mongoc_txn_intent_t ;
79
-
80
-
81
75
static void
82
76
copy_labels_plus_unknown_commit_result (const bson_t * src , bson_t * dst )
83
77
{
@@ -110,12 +104,8 @@ copy_labels_plus_unknown_commit_result (const bson_t *src, bson_t *dst)
110
104
111
105
112
106
static bool
113
- txn_finish (mongoc_client_session_t * session ,
114
- mongoc_txn_intent_t intent ,
115
- bson_t * reply ,
116
- bson_error_t * error )
107
+ txn_abort (mongoc_client_session_t * session , bson_t * reply , bson_error_t * error )
117
108
{
118
- const char * cmd_name ;
119
109
bson_t cmd = BSON_INITIALIZER ;
120
110
bson_t opts = BSON_INITIALIZER ;
121
111
bson_error_t err_local ;
@@ -126,8 +116,6 @@ txn_finish (mongoc_client_session_t *session,
126
116
127
117
_mongoc_bson_init_if_set (reply );
128
118
129
- cmd_name = (intent == TXN_COMMIT ? "commitTransaction" : "abortTransaction" );
130
-
131
119
if (!mongoc_client_session_append (session , & opts , err_ptr )) {
132
120
GOTO (done );
133
121
}
@@ -143,7 +131,7 @@ txn_finish (mongoc_client_session_t *session,
143
131
}
144
132
}
145
133
146
- BSON_APPEND_INT32 (& cmd , cmd_name , 1 );
134
+ BSON_APPEND_INT32 (& cmd , "abortTransaction" , 1 );
147
135
148
136
/* will be reinitialized by mongoc_client_write_command_with_opts */
149
137
bson_destroy (& reply_local );
@@ -157,14 +145,114 @@ txn_finish (mongoc_client_session_t *session,
157
145
bson_destroy (& reply_local );
158
146
r = mongoc_client_write_command_with_opts (
159
147
session -> client , "admin" , & cmd , & opts , & reply_local , err_ptr );
148
+ }
149
+
150
+ if (!r ) {
151
+ /* we won't return an error from abortTransaction, so warn */
152
+ MONGOC_WARNING ("Error in abortTransaction: %s" , err_ptr -> message );
153
+ }
154
+
155
+ done :
156
+ bson_destroy (& reply_local );
157
+ bson_destroy (& cmd );
158
+ bson_destroy (& opts );
159
+
160
+ return r ;
161
+ }
162
+
163
+
164
+ static mongoc_write_concern_t *
165
+ create_commit_retry_wc (const mongoc_write_concern_t * existing_wc )
166
+ {
167
+ mongoc_write_concern_t * wc ;
168
+ int32_t wtimeout ;
169
+
170
+ wc = existing_wc ? mongoc_write_concern_copy (existing_wc )
171
+ : mongoc_write_concern_new ();
172
+
173
+ wtimeout = mongoc_write_concern_get_wtimeout (wc );
174
+
175
+ /* Transactions spec: "If the modified write concern does not include a
176
+ * wtimeout value, drivers MUST also apply wtimeout: 10000 to the write
177
+ * concern in order to avoid waiting forever if the majority write concern
178
+ * cannot be satisfied." */
179
+ if (wtimeout <= 0 ) {
180
+ wtimeout = MONGOC_DEFAULT_WTIMEOUT_FOR_COMMIT_RETRY ;
181
+ }
182
+
183
+ /* Transactions spec: "If the transaction is using a write concern that is
184
+ * not the server default, any other write concern options MUST be left as-is
185
+ * when applying w:majority. */
186
+ mongoc_write_concern_set_wmajority (wc , wtimeout );
187
+
188
+ return wc ;
189
+ }
190
+
191
+
192
+ static bool
193
+ txn_commit (mongoc_client_session_t * session ,
194
+ bool explicitly_retrying ,
195
+ bson_t * reply ,
196
+ bson_error_t * error )
197
+ {
198
+ bson_t cmd = BSON_INITIALIZER ;
199
+ bson_t opts = BSON_INITIALIZER ;
200
+ bson_error_t err_local ;
201
+ bson_error_t * err_ptr = error ? error : & err_local ;
202
+ bson_t reply_local = BSON_INITIALIZER ;
203
+ mongoc_write_err_type_t error_type ;
204
+ bool r = false;
205
+ bool retrying_after_error = false;
206
+ mongoc_write_concern_t * retry_wc = NULL ;
207
+
208
+ _mongoc_bson_init_if_set (reply );
209
+
210
+ BSON_APPEND_INT32 (& cmd , "commitTransaction" , 1 );
211
+
212
+ retry :
213
+ if (!mongoc_client_session_append (session , & opts , err_ptr )) {
214
+ GOTO (done );
215
+ }
216
+
217
+ /* Transactions Spec: "When commitTransaction is retried, either by the
218
+ * driver's internal retry-once logic or explicitly by the user calling
219
+ * commitTransaction again, drivers MUST apply w:majority to the write
220
+ * concern of the commitTransaction command." */
221
+ if (!retry_wc && (retrying_after_error || explicitly_retrying )) {
222
+ retry_wc = create_commit_retry_wc (session -> txn .opts .write_concern
223
+ ? session -> txn .opts .write_concern
224
+ : session -> client -> write_concern );
225
+ }
226
+
227
+ if (retry_wc || session -> txn .opts .write_concern ) {
228
+ if (!mongoc_write_concern_append (
229
+ retry_wc ? retry_wc : session -> txn .opts .write_concern , & opts )) {
230
+ bson_set_error (err_ptr ,
231
+ MONGOC_ERROR_TRANSACTION ,
232
+ MONGOC_ERROR_TRANSACTION_INVALID_STATE ,
233
+ "Invalid transaction write concern" );
234
+ GOTO (done );
235
+ }
236
+ }
160
237
161
- error_type = _mongoc_write_error_get_type (r , err_ptr , & reply_local );
238
+ /* will be reinitialized by mongoc_client_write_command_with_opts */
239
+ bson_destroy (& reply_local );
240
+ r = mongoc_client_write_command_with_opts (
241
+ session -> client , "admin" , & cmd , & opts , & reply_local , err_ptr );
242
+
243
+ /* Transactions Spec: "Drivers MUST retry the commitTransaction command once
244
+ * after it fails with a retryable error", same for abort */
245
+ error_type = _mongoc_write_error_get_type (r , err_ptr , & reply_local );
246
+ if (!retrying_after_error && error_type == MONGOC_WRITE_ERR_RETRY ) {
247
+ retrying_after_error = true; /* retry after error only once */
248
+ bson_reinit (& opts );
249
+ GOTO (retry );
162
250
}
163
251
164
252
/* Transactions Spec: "add the UnknownTransactionCommitResult error label
165
253
* when commitTransaction fails with a network error, server selection
166
254
* error, or write concern failed / timeout." */
167
- if (intent == TXN_COMMIT && reply ) {
255
+ if (reply ) {
168
256
if ((!r && err_ptr -> domain == MONGOC_ERROR_SERVER_SELECTION ) ||
169
257
error_type == MONGOC_WRITE_ERR_RETRY ||
170
258
error_type == MONGOC_WRITE_ERR_WRITE_CONCERN ) {
@@ -177,16 +265,17 @@ txn_finish (mongoc_client_session_t *session,
177
265
bson_steal (reply , & reply_local );
178
266
bson_init (& reply_local );
179
267
}
180
-
181
- } else if (intent == TXN_ABORT && !r ) {
182
- /* we won't return an error from abortTransaction, so warn */
183
- MONGOC_WARNING ("Error in %s: %s" , cmd_name , err_ptr -> message );
184
268
}
185
269
186
270
done :
187
271
bson_destroy (& reply_local );
188
272
bson_destroy (& cmd );
189
273
bson_destroy (& opts );
274
+
275
+ if (retry_wc ) {
276
+ mongoc_write_concern_destroy (retry_wc );
277
+ }
278
+
190
279
return r ;
191
280
}
192
281
@@ -811,15 +900,18 @@ mongoc_client_session_commit_transaction (mongoc_client_session_t *session,
811
900
_mongoc_bson_init_if_set (reply );
812
901
r = true;
813
902
break ;
814
- case MONGOC_TRANSACTION_IN_PROGRESS :
815
903
case MONGOC_TRANSACTION_COMMITTED :
904
+ case MONGOC_TRANSACTION_IN_PROGRESS : {
905
+ bool explicitly_retrying =
906
+ (session -> txn .state == MONGOC_TRANSACTION_COMMITTED );
816
907
/* in MONGOC_TRANSACTION_ENDING we add txnNumber and autocommit: false
817
908
* to the commitTransaction command, but if it fails with network error
818
909
* we add UnknownTransactionCommitResult not TransientTransactionError */
819
910
session -> txn .state = MONGOC_TRANSACTION_ENDING ;
820
- r = txn_finish (session , TXN_COMMIT , reply , error );
911
+ r = txn_commit (session , explicitly_retrying , reply , error );
821
912
session -> txn .state = MONGOC_TRANSACTION_COMMITTED ;
822
913
break ;
914
+ }
823
915
case MONGOC_TRANSACTION_ENDING :
824
916
MONGOC_ERROR ("commit called in invalid state MONGOC_TRANSACTION_ENDING" );
825
917
abort ();
@@ -854,7 +946,7 @@ mongoc_client_session_abort_transaction (mongoc_client_session_t *session,
854
946
case MONGOC_TRANSACTION_IN_PROGRESS :
855
947
session -> txn .state = MONGOC_TRANSACTION_ENDING ;
856
948
/* Transactions Spec: ignore errors from abortTransaction command */
857
- txn_finish (session , TXN_ABORT , NULL , NULL );
949
+ txn_abort (session , NULL , NULL );
858
950
session -> txn .state = MONGOC_TRANSACTION_ABORTED ;
859
951
RETURN (true);
860
952
case MONGOC_TRANSACTION_COMMITTED :
0 commit comments