@@ -180,32 +180,22 @@ public BulkWriteResult execute(final WriteBinding binding) {
180
180
logRetryExecute (retryState );
181
181
return withSourceAndConnection (binding ::getWriteConnectionSource , true , (source , connection ) -> {
182
182
ConnectionDescription connectionDescription = connection .getDescription ();
183
- int maxWireVersion = connectionDescription .getMaxWireVersion ();
184
183
// attach `maxWireVersion` ASAP because it is used to check whether we can retry
185
- retryState .attach (AttachmentKeys .maxWireVersion (), maxWireVersion , true );
186
- BulkWriteTracker bulkWriteTracker = retryState .attachment (AttachmentKeys .bulkWriteTracker ())
187
- .orElseThrow (Assertions ::fail );
184
+ retryState .attach (AttachmentKeys .maxWireVersion (), connectionDescription .getMaxWireVersion (), true );
188
185
SessionContext sessionContext = binding .getSessionContext ();
189
186
WriteConcern writeConcern = getAppliedWriteConcern (sessionContext );
190
- if (!retryState .isFirstAttempt () && !isRetryableWrite (retryWrites , writeConcern , source .getServerDescription (),
191
- connectionDescription , sessionContext )) {
192
- RuntimeException prospectiveFailedResult = (RuntimeException ) retryState .exception ().orElse (null );
193
- retryState .breakAndThrowIfRetryAnd (() -> !(prospectiveFailedResult instanceof MongoWriteConcernWithResponseException ));
194
- bulkWriteTracker .batch ().ifPresent (bulkWriteBatch -> {
195
- assertTrue (prospectiveFailedResult instanceof MongoWriteConcernWithResponseException );
196
- bulkWriteBatch .addResult ((BsonDocument ) ((MongoWriteConcernWithResponseException ) prospectiveFailedResult )
197
- .getResponse ());
198
- BulkWriteTracker .attachNext (retryState , bulkWriteBatch );
199
- });
187
+ if (!isRetryableWrite (retryWrites , getAppliedWriteConcern (sessionContext ),
188
+ source .getServerDescription (), connectionDescription , sessionContext )) {
189
+ handleMongoWriteConcernWithResponseException (retryState , true );
200
190
}
201
191
validateWriteRequests (connectionDescription , bypassDocumentValidation , writeRequests , writeConcern );
202
- if (!bulkWriteTracker .batch ().isPresent ()) {
192
+ if (!retryState . attachment ( AttachmentKeys . bulkWriteTracker ()). orElseThrow ( Assertions :: fail ) .batch ().isPresent ()) {
203
193
BulkWriteTracker .attachNew (retryState , BulkWriteBatch .createBulkWriteBatch (namespace ,
204
194
source .getServerDescription (), connectionDescription , ordered , writeConcern ,
205
195
bypassDocumentValidation , retryWrites , writeRequests , sessionContext , comment , variables ));
206
196
}
207
197
logRetryExecute (retryState );
208
- return executeBulkWriteBatch (retryState , binding , connection , maxWireVersion );
198
+ return executeBulkWriteBatch (retryState , binding , connection );
209
199
});
210
200
});
211
201
try {
@@ -226,33 +216,22 @@ public void executeAsync(final AsyncWriteBinding binding, final SingleResultCall
226
216
withAsyncSourceAndConnection (binding ::getWriteConnectionSource , true , funcCallback ,
227
217
(source , connection , releasingCallback ) -> {
228
218
ConnectionDescription connectionDescription = connection .getDescription ();
229
- int maxWireVersion = connectionDescription .getMaxWireVersion ();
230
219
// attach `maxWireVersion` ASAP because it is used to check whether we can retry
231
- retryState .attach (AttachmentKeys .maxWireVersion (), maxWireVersion , true );
232
- BulkWriteTracker bulkWriteTracker = retryState .attachment (AttachmentKeys .bulkWriteTracker ())
233
- .orElseThrow (Assertions ::fail );
220
+ retryState .attach (AttachmentKeys .maxWireVersion (), connectionDescription .getMaxWireVersion (), true );
234
221
SessionContext sessionContext = binding .getSessionContext ();
235
222
WriteConcern writeConcern = getAppliedWriteConcern (sessionContext );
236
- if (!retryState .isFirstAttempt () && !isRetryableWrite (retryWrites , writeConcern , source .getServerDescription (),
237
- connectionDescription , sessionContext )) {
238
- Throwable prospectiveFailedResult = retryState .exception ().orElse (null );
239
- if (retryState .breakAndCompleteIfRetryAnd (() ->
240
- !(prospectiveFailedResult instanceof MongoWriteConcernWithResponseException ), releasingCallback )) {
241
- return ;
242
- }
243
- bulkWriteTracker .batch ().ifPresent (bulkWriteBatch -> {
244
- assertTrue (prospectiveFailedResult instanceof MongoWriteConcernWithResponseException );
245
- bulkWriteBatch .addResult ((BsonDocument ) ((MongoWriteConcernWithResponseException ) prospectiveFailedResult )
246
- .getResponse ());
247
- BulkWriteTracker .attachNext (retryState , bulkWriteBatch );
248
- });
223
+ if (!isRetryableWrite (retryWrites , getAppliedWriteConcern (sessionContext ),
224
+ source .getServerDescription (),
225
+ connectionDescription , sessionContext )
226
+ && handleMongoWriteConcernWithResponseExceptionAsync (retryState , releasingCallback )) {
227
+ return ;
249
228
}
250
229
if (validateWriteRequestsAndCompleteIfInvalid (connectionDescription , bypassDocumentValidation , writeRequests ,
251
230
writeConcern , releasingCallback )) {
252
231
return ;
253
232
}
254
233
try {
255
- if (!bulkWriteTracker .batch ().isPresent ()) {
234
+ if (!retryState . attachment ( AttachmentKeys . bulkWriteTracker ()). orElseThrow ( Assertions :: fail ) .batch ().isPresent ()) {
256
235
BulkWriteTracker .attachNew (retryState , BulkWriteBatch .createBulkWriteBatch (namespace ,
257
236
source .getServerDescription (), connectionDescription , ordered , writeConcern ,
258
237
bypassDocumentValidation , retryWrites , writeRequests , sessionContext , comment , variables ));
@@ -262,17 +241,17 @@ public void executeAsync(final AsyncWriteBinding binding, final SingleResultCall
262
241
return ;
263
242
}
264
243
logRetryExecute (retryState );
265
- executeBulkWriteBatchAsync (retryState , binding , connection , maxWireVersion , releasingCallback );
244
+ executeBulkWriteBatchAsync (retryState , binding , connection , releasingCallback );
266
245
});
267
246
}).whenComplete (binding ::release );
268
247
retryingBulkWrite .get (exceptionTransformingCallback (errorHandlingCallback (callback , LOGGER )));
269
248
}
270
249
271
- private BulkWriteResult executeBulkWriteBatch (final RetryState retryState , final WriteBinding binding , final Connection connection ,
272
- final int maxWireVersion ) {
250
+ private BulkWriteResult executeBulkWriteBatch (final RetryState retryState , final WriteBinding binding , final Connection connection ) {
273
251
BulkWriteTracker currentBulkWriteTracker = retryState .attachment (AttachmentKeys .bulkWriteTracker ())
274
252
.orElseThrow (Assertions ::fail );
275
253
BulkWriteBatch currentBatch = currentBulkWriteTracker .batch ().orElseThrow (Assertions ::fail );
254
+ int maxWireVersion = connection .getDescription ().getMaxWireVersion ();
276
255
while (currentBatch .shouldProcessBatch ()) {
277
256
try {
278
257
BsonDocument result = executeCommand (connection , currentBatch , binding );
@@ -292,9 +271,10 @@ private BulkWriteResult executeBulkWriteBatch(final RetryState retryState, final
292
271
currentBulkWriteTracker = BulkWriteTracker .attachNext (retryState , currentBatch );
293
272
currentBatch = currentBulkWriteTracker .batch ().orElseThrow (Assertions ::fail );
294
273
} catch (MongoException exception ) {
295
- if (!( retryState .isFirstAttempt () || (exception instanceof MongoWriteConcernWithResponseException ) )) {
274
+ if (!retryState .isFirstAttempt () && ! (exception instanceof MongoWriteConcernWithResponseException )) {
296
275
addRetryableWriteErrorLabel (exception , maxWireVersion );
297
276
}
277
+ handleMongoWriteConcernWithResponseException (retryState , false );
298
278
throw exception ;
299
279
}
300
280
}
@@ -307,13 +287,14 @@ private BulkWriteResult executeBulkWriteBatch(final RetryState retryState, final
307
287
}
308
288
309
289
private void executeBulkWriteBatchAsync (final RetryState retryState , final AsyncWriteBinding binding , final AsyncConnection connection ,
310
- final int maxWireVersion , final SingleResultCallback <BulkWriteResult > callback ) {
290
+ final SingleResultCallback <BulkWriteResult > callback ) {
311
291
LoopState loopState = new LoopState ();
312
292
AsyncCallbackRunnable loop = new AsyncCallbackLoop (loopState , iterationCallback -> {
313
293
BulkWriteTracker currentBulkWriteTracker = retryState .attachment (AttachmentKeys .bulkWriteTracker ())
314
294
.orElseThrow (Assertions ::fail );
315
295
loopState .attach (AttachmentKeys .bulkWriteTracker (), currentBulkWriteTracker , true );
316
296
BulkWriteBatch currentBatch = currentBulkWriteTracker .batch ().orElseThrow (Assertions ::fail );
297
+ int maxWireVersion = connection .getDescription ().getMaxWireVersion ();
317
298
if (loopState .breakAndCompleteIf (() -> !currentBatch .shouldProcessBatch (), iterationCallback )) {
318
299
return ;
319
300
}
@@ -340,9 +321,12 @@ private void executeBulkWriteBatchAsync(final RetryState retryState, final Async
340
321
} else {
341
322
if (t instanceof MongoException ) {
342
323
MongoException exception = (MongoException ) t ;
343
- if (!( retryState .isFirstAttempt () || (exception instanceof MongoWriteConcernWithResponseException ) )) {
324
+ if (!retryState .isFirstAttempt () && ! (exception instanceof MongoWriteConcernWithResponseException )) {
344
325
addRetryableWriteErrorLabel (exception , maxWireVersion );
345
326
}
327
+ if (handleMongoWriteConcernWithResponseExceptionAsync (retryState , null )) {
328
+ return ;
329
+ }
346
330
}
347
331
iterationCallback .onResult (null , t );
348
332
}
@@ -368,6 +352,41 @@ private void executeBulkWriteBatchAsync(final RetryState retryState, final Async
368
352
});
369
353
}
370
354
355
+ private void handleMongoWriteConcernWithResponseException (final RetryState retryState , final boolean breakAndThrowIfDifferent ) {
356
+ if (!retryState .isFirstAttempt ()) {
357
+ RuntimeException prospectiveFailedResult = (RuntimeException ) retryState .exception ().orElse (null );
358
+ boolean prospectiveResultIsWriteConcernException = prospectiveFailedResult instanceof MongoWriteConcernWithResponseException ;
359
+ retryState .breakAndThrowIfRetryAnd (() -> breakAndThrowIfDifferent && !prospectiveResultIsWriteConcernException );
360
+ if (prospectiveResultIsWriteConcernException ) {
361
+ retryState .attachment (AttachmentKeys .bulkWriteTracker ()).orElseThrow (Assertions ::fail )
362
+ .batch ().ifPresent (bulkWriteBatch -> {
363
+ bulkWriteBatch .addResult (
364
+ (BsonDocument ) ((MongoWriteConcernWithResponseException ) prospectiveFailedResult ).getResponse ());
365
+ BulkWriteTracker .attachNext (retryState , bulkWriteBatch );
366
+ });
367
+ }
368
+ }
369
+ }
370
+
371
+ private boolean handleMongoWriteConcernWithResponseExceptionAsync (final RetryState retryState ,
372
+ @ Nullable final SingleResultCallback <BulkWriteResult > callback ) {
373
+ if (!retryState .isFirstAttempt ()) {
374
+ RuntimeException prospectiveFailedResult = (RuntimeException ) retryState .exception ().orElse (null );
375
+ boolean prospectiveResultIsWriteConcernException = prospectiveFailedResult instanceof MongoWriteConcernWithResponseException ;
376
+ if (callback != null && retryState .breakAndCompleteIfRetryAnd (() -> !prospectiveResultIsWriteConcernException , callback )) {
377
+ return true ;
378
+ }
379
+ if (prospectiveResultIsWriteConcernException ) {
380
+ retryState .attachment (AttachmentKeys .bulkWriteTracker ()).orElseThrow (Assertions ::fail )
381
+ .batch ().ifPresent (bulkWriteBatch -> {
382
+ bulkWriteBatch .addResult (
383
+ (BsonDocument ) ((MongoWriteConcernWithResponseException ) prospectiveFailedResult ).getResponse ());
384
+ BulkWriteTracker .attachNext (retryState , bulkWriteBatch );
385
+ });
386
+ }
387
+ }
388
+ return false ;
389
+ }
371
390
372
391
private BsonDocument executeCommand (final Connection connection , final BulkWriteBatch batch , final WriteBinding binding ) {
373
392
return connection .command (namespace .getDatabaseName (), batch .getCommand (), NO_OP_FIELD_NAME_VALIDATOR ,
0 commit comments