2121import com .mongodb .MongoException ;
2222import com .mongodb .MongoExecutionTimeoutException ;
2323import com .mongodb .MongoInternalException ;
24- import com .mongodb .MongoOperationTimeoutException ;
2524import com .mongodb .MongoTimeoutException ;
2625import com .mongodb .ReadConcern ;
2726import com .mongodb .TransactionOptions ;
3332import com .mongodb .internal .observability .micrometer .TransactionSpan ;
3433import com .mongodb .internal .operation .AbortTransactionOperation ;
3534import com .mongodb .internal .operation .CommitTransactionOperation ;
36- import com .mongodb .internal .operation .OperationHelper ;
3735import com .mongodb .internal .operation .ReadOperation ;
3836import com .mongodb .internal .operation .WriteConcernHelper ;
3937import com .mongodb .internal .operation .WriteOperation ;
@@ -160,6 +158,12 @@ public void abortTransaction() {
160158 }
161159 }
162160
161+ private void abortIfInTransaction () {
162+ if (transactionState == TransactionState .IN ) {
163+ abortTransaction ();
164+ }
165+ }
166+
163167 private void startTransaction (final TransactionOptions transactionOptions , final TimeoutContext timeoutContext ) {
164168 Boolean snapshot = getOptions ().isSnapshot ();
165169 if (snapshot != null && snapshot ) {
@@ -258,9 +262,10 @@ public <T> T withTransaction(final TransactionBody<T> transactionBody) {
258262 public <T > T withTransaction (final TransactionBody <T > transactionBody , final TransactionOptions options ) {
259263 notNull ("transactionBody" , transactionBody );
260264 TimeoutContext withTransactionTimeoutContext = createTimeoutContext (options );
261- final boolean hasTimeoutMS = withTransactionTimeoutContext .hasTimeoutMS ();
262- Timeout withTransactionTimeout = withTransactionTimeoutContext .timeoutOrAlternative (
263- assertNotNull (TimeoutContext .startTimeout (MAX_RETRY_TIME_LIMIT_MS )));
265+ boolean timeoutMsConfigured = withTransactionTimeoutContext .hasTimeoutMS ();
266+ Timeout withTransactionTimeout = assertNotNull (timeoutMsConfigured
267+ ? withTransactionTimeoutContext .getTimeout ()
268+ : TimeoutContext .startTimeout (MAX_RETRY_TIME_LIMIT_MS ));
264269 BooleanSupplier withTransactionTimeoutExpired = () -> withTransactionTimeout .call (TimeUnit .MILLISECONDS ,
265270 () -> false , ms -> false , () -> true );
266271 int transactionAttempt = 0 ;
@@ -270,35 +275,37 @@ public <T> T withTransaction(final TransactionBody<T> transactionBody, final Tra
270275 outer :
271276 while (true ) {
272277 if (transactionAttempt > 0 ) {
273- backoff (transactionAttempt , withTransactionTimeout , assertNotNull (lastError ), hasTimeoutMS );
278+ backoff (transactionAttempt , withTransactionTimeout , assertNotNull (lastError ), timeoutMsConfigured );
274279 }
275- T retVal ;
276280 try {
277281 startTransaction (options , withTransactionTimeoutContext );
278282 transactionAttempt ++;
279-
280283 if (transactionSpan != null ) {
281284 transactionSpan .setIsConvenientTransaction ();
282285 }
286+ } catch (Throwable e ) {
287+ abortIfInTransaction ();
288+ throw e ;
289+ }
290+ T retVal ;
291+ try {
283292 retVal = transactionBody .execute ();
284293 } catch (Throwable e ) {
285- if (transactionState == TransactionState .IN ) {
286- abortTransaction ();
287- }
294+ abortIfInTransaction ();
288295 if (e instanceof MongoException ) {
289- lastError = (MongoException ) e ;
290- if (!(e instanceof MongoOperationTimeoutException )) {
291- MongoException exceptionToHandle = OperationHelper .unwrap ((MongoException ) e );
292- if (exceptionToHandle .hasErrorLabel (TRANSIENT_TRANSACTION_ERROR_LABEL )) {
293- if (withTransactionTimeoutExpired .getAsBoolean ()) {
294- throw timeoutException (hasTimeoutMS , e );
295- } else {
296- if (transactionSpan != null ) {
297- transactionSpan .spanFinalizing (false );
298- }
299- continue ;
300- }
296+ MongoException mongoException = (MongoException ) e ;
297+ if (mongoException .hasErrorLabel (TRANSIENT_TRANSACTION_ERROR_LABEL )) {
298+ if (transactionSpan != null ) {
299+ transactionSpan .spanFinalizing (false );
301300 }
301+ lastError = mongoException ;
302+ continue ;
303+ } else if (mongoException .hasErrorLabel (UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL )) {
304+ throw e ;
305+ } else {
306+ throw withTransactionTimeoutExpired .getAsBoolean ()
307+ ? wrapInMongoTimeoutException (mongoException , timeoutMsConfigured )
308+ : mongoException ;
302309 }
303310 }
304311 throw e ;
@@ -308,28 +315,22 @@ public <T> T withTransaction(final TransactionBody<T> transactionBody, final Tra
308315 try {
309316 commitTransaction (false );
310317 break ;
311- } catch (MongoException e ) {
312- lastError = e ;
313- clearTransactionContextOnError (e );
314- if (!(e instanceof MongoOperationTimeoutException )) {
315- if (withTransactionTimeoutExpired .getAsBoolean ()) {
316- throw timeoutException (hasTimeoutMS , e );
317-
318- } else {
319- applyMajorityWriteConcernToTransactionOptions ();
320-
321- if (!(e instanceof MongoExecutionTimeoutException )
322- && e .hasErrorLabel (UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL )) {
323- continue ;
324- } else if (e .hasErrorLabel (TRANSIENT_TRANSACTION_ERROR_LABEL )) {
325- if (transactionSpan != null ) {
326- transactionSpan .spanFinalizing (true );
327- }
328- continue outer ;
329- }
318+ } catch (MongoException mongoException ) {
319+ if (mongoException .hasErrorLabel (UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL )
320+ && !(mongoException instanceof MongoExecutionTimeoutException )
321+ && !withTransactionTimeoutExpired .getAsBoolean ()) {
322+ applyMajorityWriteConcernToTransactionOptions ();
323+ continue ;
324+ } else if (mongoException .hasErrorLabel (TRANSIENT_TRANSACTION_ERROR_LABEL )) {
325+ if (transactionSpan != null ) {
326+ transactionSpan .spanFinalizing (true );
330327 }
328+ lastError = mongoException ;
329+ continue outer ;
331330 }
332- throw e ;
331+ throw withTransactionTimeoutExpired .getAsBoolean ()
332+ ? wrapInMongoTimeoutException (mongoException , timeoutMsConfigured )
333+ : mongoException ;
333334 }
334335 }
335336 }
@@ -351,9 +352,7 @@ public TransactionSpan getTransactionSpan() {
351352 @ Override
352353 public void close () {
353354 try {
354- if (transactionState == TransactionState .IN ) {
355- abortTransaction ();
356- }
355+ abortIfInTransaction ();
357356 } finally {
358357 clearTransactionContext ();
359358 super .close ();
@@ -391,10 +390,10 @@ private TimeoutContext createTimeoutContext(final TransactionOptions transaction
391390 }
392391
393392 private static void backoff (final int transactionAttempt ,
394- final Timeout withTransactionTimeout , final MongoException lastError , final boolean hasTimeoutMS ) {
393+ final Timeout withTransactionTimeout , final MongoException lastError , final boolean timeoutMsConfigured ) {
395394 long backoffMs = ExponentialBackoff .calculateTransactionBackoffMs (transactionAttempt );
396395 withTransactionTimeout .shortenBy (backoffMs , TimeUnit .MILLISECONDS ).onExpired (() -> {
397- throw timeoutException ( hasTimeoutMS , lastError );
396+ throw wrapInMongoTimeoutException ( lastError , timeoutMsConfigured );
398397 });
399398 try {
400399 if (backoffMs > 0 ) {
@@ -405,9 +404,15 @@ private static void backoff(final int transactionAttempt,
405404 }
406405 }
407406
408- private static MongoException timeoutException (final boolean hasTimeoutMS , final Throwable cause ) {
409- return hasTimeoutMS
410- ? createMongoTimeoutException (cause ) // CSOT timeout exception
411- : new MongoTimeoutException ("Operation exceeded the timeout limit" , cause ); // Legacy timeout exception
407+ private static MongoException wrapInMongoTimeoutException (final MongoException cause , final boolean timeoutMsConfigured ) {
408+ return timeoutMsConfigured
409+ ? createMongoTimeoutException (cause )
410+ : wrapInNonTimeoutMsMongoTimeoutException (cause );
411+ }
412+
413+ private static MongoTimeoutException wrapInNonTimeoutMsMongoTimeoutException (final MongoException cause ) {
414+ return cause instanceof MongoTimeoutException
415+ ? (MongoTimeoutException ) cause
416+ : new MongoTimeoutException ("Operation exceeded the timeout limit" , cause );
412417 }
413418}
0 commit comments