diff --git a/driver-core/src/main/com/mongodb/internal/async/AsyncRunnable.java b/driver-core/src/main/com/mongodb/internal/async/AsyncRunnable.java index c108aeed0d..deeef6239c 100644 --- a/driver-core/src/main/com/mongodb/internal/async/AsyncRunnable.java +++ b/driver-core/src/main/com/mongodb/internal/async/AsyncRunnable.java @@ -16,7 +16,6 @@ package com.mongodb.internal.async; -import com.mongodb.internal.TimeoutContext; import com.mongodb.internal.async.function.AsyncCallbackLoop; import com.mongodb.internal.async.function.LoopState; import com.mongodb.internal.async.function.RetryState; @@ -39,7 +38,7 @@ * following "sync" method: * *
- * public T myMethod()
+ * public T myMethod() {
  *     method1();
  *     method2();
  * }
@@ -47,7 +46,7 @@ *

The async counterpart would be: * *

- * public void myMethodAsync(SingleResultCallback<T> callback)
+ * public void myMethodAsync(SingleResultCallback<T> callback) {
  *     beginAsync().thenRun(c -> {
  *         method1Async(c);
  *     }).thenRun(c -> {
@@ -229,11 +228,11 @@ default  AsyncSupplier thenSupply(final AsyncSupplier supplier) {
      * @return the composition of this, and the looping branch
      * @see RetryingAsyncCallbackSupplier
      */
-    default AsyncRunnable thenRunRetryingWhile(
-            final TimeoutContext timeoutContext, final AsyncRunnable runnable, final Predicate shouldRetry) {
+    default AsyncRunnable thenRunRetryingWhile(final AsyncRunnable runnable, final Predicate shouldRetry) {
         return thenRun(callback -> {
             new RetryingAsyncCallbackSupplier(
-                    new RetryState(timeoutContext),
+                    new RetryState(),
+                    (previouslyChosenFailure, lastAttemptFailure) -> lastAttemptFailure,
                     (rs, lastAttemptFailure) -> shouldRetry.test(lastAttemptFailure),
                     // `finish` is required here instead of `unsafeFinish`
                     // because only `finish` meets the contract of
diff --git a/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java b/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java
index 3b8394dae1..b209035d7a 100644
--- a/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java
+++ b/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java
@@ -17,7 +17,6 @@
 
 import com.mongodb.MongoOperationTimeoutException;
 import com.mongodb.annotations.NotThreadSafe;
-import com.mongodb.internal.TimeoutContext;
 import com.mongodb.internal.async.SingleResultCallback;
 import com.mongodb.internal.async.function.LoopState.AttachmentKey;
 import com.mongodb.lang.NonNull;
@@ -46,62 +45,31 @@
  */
 @NotThreadSafe
 public final class RetryState {
-    public static final int RETRIES = 1;
-    public static final int INFINITE_ATTEMPTS = Integer.MAX_VALUE;
+    public static final int MAX_RETRIES = 1;
+    private static final int INFINITE_RETRIES = Integer.MAX_VALUE;
 
     private final LoopState loopState;
     private final int attempts;
-    private final boolean retryUntilTimeoutThrowsException;
     @Nullable
     private Throwable previouslyChosenException;
 
     /**
-     * Creates a {@code RetryState} with a positive number of allowed retry attempts.
-     * {@link Integer#MAX_VALUE} is a special value interpreted as being unlimited.
-     * 

- * If a timeout is not specified in the {@link TimeoutContext#hasTimeoutMS()}, the specified {@code retries} argument acts as a fallback - * bound. Otherwise, retries are unbounded until the timeout is reached. - *

- * It is possible to provide an additional {@code retryPredicate} in the {@link #doAdvanceOrThrow} method, - * which can be used to stop retrying based on a custom condition additionally to {@code retries} and {@link TimeoutContext}. - *

- * - * @param retries A positive number of allowed retry attempts. - * {@link Integer#MAX_VALUE} is a special value interpreted as being unlimited. - * @param retryUntilTimeoutThrowsException If {@code true}, then if a {@link MongoOperationTimeoutException} is thrown then retrying stops. - */ - public static RetryState withRetryableState(final int retries, final boolean retryUntilTimeoutThrowsException) { - assertTrue(retries > 0); - return new RetryState(retries, retryUntilTimeoutThrowsException); - } - - public static RetryState withNonRetryableState() { - return new RetryState(0, false); - } - - /** - * Creates a {@link RetryState} that does not limit the number of attempts. - * The number of attempts is limited iff {@link TimeoutContext#hasTimeoutMS()} is true and timeout has expired. - *

- * It is possible to provide an additional {@code retryPredicate} in the {@link #doAdvanceOrThrow} method, - * which can be used to stop retrying based on a custom condition additionally to {@link TimeoutContext}. - *

- * - * @param timeoutContext A timeout context that will be used to determine if the operation has timed out. + * Creates a {@link RetryState} that does not explicitly limit the number of attempts. + * Retrying still may be stopped because, for example, + * the failed result from the most recent attempt is {@link MongoOperationTimeoutException}. */ - public RetryState(final TimeoutContext timeoutContext) { - this(INFINITE_ATTEMPTS, timeoutContext.hasTimeoutMS()); + public RetryState() { + this(INFINITE_RETRIES); } /** * @param retries A non-negative number of allowed retry attempts. - * {@link Integer#MAX_VALUE} is a special value interpreted as being unlimited. + * {@value #INFINITE_RETRIES} is interpreted as {@linkplain #RetryState() absence of explicit limit}. */ - private RetryState(final int retries, final boolean retryUntilTimeoutThrowsException) { + public RetryState(final int retries) { assertTrue(retries >= 0); loopState = new LoopState(); - attempts = retries == INFINITE_ATTEMPTS ? INFINITE_ATTEMPTS : retries + 1; - this.retryUntilTimeoutThrowsException = retryUntilTimeoutThrowsException; + attempts = retries == INFINITE_RETRIES ? INFINITE_RETRIES : retries + 1; } /** @@ -351,14 +319,14 @@ public boolean isFirstAttempt() { * An attempt is known to be the last one iff any of the following applies: *
    *
  • {@link #breakAndThrowIfRetryAnd(Supplier)} / {@link #breakAndCompleteIfRetryAnd(Supplier, SingleResultCallback)} / {@link #markAsLastAttempt()} was called.
  • - *
  • A timeout is set and has been reached, as indicated by {@code attemptException}.
  • - *
  • No timeout is set, and the number of attempts is limited, and the current attempt is the last one.
  • + *
  • {@code attemptException} is a {@link MongoOperationTimeoutException}.
  • + *
  • The number of attempts is limited, and the current attempt is the last one.
  • *
* * @see #attempt() */ private boolean isLastAttempt(final Throwable attemptException) { - boolean operationTimeout = retryUntilTimeoutThrowsException && attemptException instanceof MongoOperationTimeoutException; + boolean operationTimeout = attemptException instanceof MongoOperationTimeoutException; boolean attemptLimit = attempt() == attempts - 1; return loopState.isLastIteration() || operationTimeout || attemptLimit; } @@ -403,7 +371,7 @@ public Optional attachment(final AttachmentKey key) { public String toString() { return "RetryState{" + "loopState=" + loopState - + ", attempts=" + (attempts == INFINITE_ATTEMPTS ? "infinite" : attempts) + + ", attempts=" + (attempts == INFINITE_RETRIES ? "infinite" : attempts) + ", exception=" + previouslyChosenException + '}'; } diff --git a/driver-core/src/main/com/mongodb/internal/async/function/RetryingAsyncCallbackSupplier.java b/driver-core/src/main/com/mongodb/internal/async/function/RetryingAsyncCallbackSupplier.java index 6ce08513aa..8d12e82e19 100644 --- a/driver-core/src/main/com/mongodb/internal/async/function/RetryingAsyncCallbackSupplier.java +++ b/driver-core/src/main/com/mongodb/internal/async/function/RetryingAsyncCallbackSupplier.java @@ -87,13 +87,6 @@ public RetryingAsyncCallbackSupplier( this.asyncFunction = asyncFunction; } - public RetryingAsyncCallbackSupplier( - final RetryState state, - final BiPredicate retryPredicate, - final AsyncCallbackSupplier asyncFunction) { - this(state, (previouslyChosenFailure, lastAttemptFailure) -> lastAttemptFailure, retryPredicate, asyncFunction); - } - @Override public void get(final SingleResultCallback callback) { /* `asyncFunction` and `callback` are the only externally provided pieces of code for which we do not need to care about diff --git a/driver-core/src/main/com/mongodb/internal/connection/OidcAuthenticator.java b/driver-core/src/main/com/mongodb/internal/connection/OidcAuthenticator.java index f8c7f3ea89..e002ed3397 100644 --- a/driver-core/src/main/com/mongodb/internal/connection/OidcAuthenticator.java +++ b/driver-core/src/main/com/mongodb/internal/connection/OidcAuthenticator.java @@ -335,7 +335,6 @@ private void authenticationLoopAsync(final InternalConnection connection, final final SingleResultCallback callback) { fallbackState = FallbackState.INITIAL; beginAsync().thenRunRetryingWhile( - operationContext.getTimeoutContext(), c -> super.authenticateAsync(connection, description, operationContext, c), e -> triggersRetry(e) && shouldRetryHandler() ).finish(callback); diff --git a/driver-core/src/main/com/mongodb/internal/operation/CommandOperationHelper.java b/driver-core/src/main/com/mongodb/internal/operation/CommandOperationHelper.java index 9495adc892..b39cddd654 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/CommandOperationHelper.java +++ b/driver-core/src/main/com/mongodb/internal/operation/CommandOperationHelper.java @@ -45,7 +45,7 @@ import static com.mongodb.assertions.Assertions.assertFalse; import static com.mongodb.assertions.Assertions.assertNotNull; -import static com.mongodb.internal.async.function.RetryState.INFINITE_ATTEMPTS; +import static com.mongodb.internal.async.function.RetryState.MAX_RETRIES; import static com.mongodb.internal.operation.OperationHelper.LOGGER; import static java.lang.String.format; import static java.util.Arrays.asList; @@ -123,11 +123,9 @@ private static Throwable chooseRetryableWriteException( static RetryState initialRetryState(final boolean retry, final TimeoutContext timeoutContext) { if (retry) { - boolean retryUntilTimeoutThrowsException = timeoutContext.hasTimeoutMS(); - int retries = retryUntilTimeoutThrowsException ? INFINITE_ATTEMPTS : RetryState.RETRIES; - return RetryState.withRetryableState(retries, retryUntilTimeoutThrowsException); + return timeoutContext.hasTimeoutMS() ? new RetryState() : new RetryState(MAX_RETRIES); } - return RetryState.withNonRetryableState(); + return new RetryState(0); } private static final List RETRYABLE_ERROR_CODES = asList(6, 7, 89, 91, 134, 189, 262, 9001, 13436, 13435, 11602, 11600, 10107); diff --git a/driver-core/src/main/com/mongodb/internal/operation/MixedBulkWriteOperation.java b/driver-core/src/main/com/mongodb/internal/operation/MixedBulkWriteOperation.java index 5f916d9d90..53b0f23208 100644 --- a/driver-core/src/main/com/mongodb/internal/operation/MixedBulkWriteOperation.java +++ b/driver-core/src/main/com/mongodb/internal/operation/MixedBulkWriteOperation.java @@ -187,7 +187,6 @@ public String getCommandName() { @Override public BulkWriteResult execute(final WriteBinding binding, final OperationContext operationContext) { - TimeoutContext timeoutContext = operationContext.getTimeoutContext(); /* We cannot use the tracking of attempts built in the `RetryState` class because conceptually we have to maintain multiple attempt * counters while executing a single bulk write operation: * - a counter that limits attempts to select server and checkout a connection before we created a batch; @@ -195,8 +194,8 @@ public BulkWriteResult execute(final WriteBinding binding, final OperationContex * Fortunately, these counters do not exist concurrently with each other. While maintaining the counters manually, * we must adhere to the contract of `RetryingSyncSupplier`. When the retry timeout is implemented, there will be no counters, * and the code related to the attempt tracking in `BulkWriteTracker` will be removed. */ - RetryState retryState = new RetryState(timeoutContext); - BulkWriteTracker.attachNew(retryState, retryWrites, timeoutContext); + RetryState retryState = new RetryState(); + BulkWriteTracker.attachNew(retryState, retryWrites, operationContext.getTimeoutContext()); Supplier retryingBulkWrite = decorateWriteWithRetries(retryState, operationContext, () -> withSourceAndConnection(binding::getWriteConnectionSource, true, (source, connection, operationContextWithMinRTT) -> { TimeoutContext timeoutContextWithMinRtt = operationContextWithMinRTT.getTimeoutContext(); @@ -226,10 +225,9 @@ public BulkWriteResult execute(final WriteBinding binding, final OperationContex } public void executeAsync(final AsyncWriteBinding binding, final OperationContext operationContext, final SingleResultCallback callback) { - TimeoutContext timeoutContext = operationContext.getTimeoutContext(); // see the comment in `execute(WriteBinding)` explaining the manual tracking of attempts - RetryState retryState = new RetryState(timeoutContext); - BulkWriteTracker.attachNew(retryState, retryWrites, timeoutContext); + RetryState retryState = new RetryState(); + BulkWriteTracker.attachNew(retryState, retryWrites, operationContext.getTimeoutContext()); binding.retain(); AsyncCallbackSupplier retryingBulkWrite = this.decorateWriteWithRetries(retryState, operationContext, @@ -493,7 +491,7 @@ private static void attach(final RetryState retryState, final BulkWriteTracker t private BulkWriteTracker(final boolean retry, @Nullable final BulkWriteBatch batch, final TimeoutContext timeoutContext) { attempt = 0; - attempts = retry ? RetryState.RETRIES + 1 : 1; + attempts = retry ? RetryState.MAX_RETRIES + 1 : 1; this.batch = batch; this.retryUntilTimeoutThrowsException = timeoutContext.hasTimeoutMS(); } diff --git a/driver-core/src/test/unit/com/mongodb/internal/async/AsyncFunctionsAbstractTest.java b/driver-core/src/test/unit/com/mongodb/internal/async/AsyncFunctionsAbstractTest.java index 8f6bc7046a..9ce94fc413 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/async/AsyncFunctionsAbstractTest.java +++ b/driver-core/src/test/unit/com/mongodb/internal/async/AsyncFunctionsAbstractTest.java @@ -16,8 +16,6 @@ package com.mongodb.internal.async; import com.mongodb.MongoException; -import com.mongodb.internal.TimeoutContext; -import com.mongodb.internal.TimeoutSettings; import org.junit.jupiter.api.Test; import java.util.function.BiConsumer; @@ -29,8 +27,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; abstract class AsyncFunctionsAbstractTest extends AsyncFunctionsTestBase { - private static final TimeoutContext TIMEOUT_CONTEXT = new TimeoutContext(new TimeoutSettings(0, 0, 0, 0L, 0)); - @Test void test1Method() { // the number of expected variations is often: 1 + N methods invoked @@ -856,7 +852,6 @@ void testRetryLoop() { }, (callback) -> { beginAsync().thenRunRetryingWhile( - TIMEOUT_CONTEXT, c -> async(plainTest(0) ? 1 : 2, c), e -> e.getMessage().equals("exception-1") ).finish(callback); diff --git a/driver-core/src/test/unit/com/mongodb/internal/async/function/RetryStateTest.java b/driver-core/src/test/unit/com/mongodb/internal/async/function/RetryStateTest.java index 6220753f13..9d6fc2f586 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/async/function/RetryStateTest.java +++ b/driver-core/src/test/unit/com/mongodb/internal/async/function/RetryStateTest.java @@ -18,7 +18,6 @@ import com.mongodb.MongoOperationTimeoutException; import com.mongodb.client.syncadapter.SupplyingCallback; import com.mongodb.internal.TimeoutContext; -import com.mongodb.internal.TimeoutSettings; import com.mongodb.internal.async.function.LoopState.AttachmentKey; import com.mongodb.internal.operation.retry.AttachmentKeys; import org.junit.jupiter.api.Assertions; @@ -44,42 +43,25 @@ import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Named.named; import static org.junit.jupiter.params.provider.Arguments.arguments; -import static org.mockito.Mockito.mock; final class RetryStateTest { - private static final TimeoutContext TIMEOUT_CONTEXT_NO_GLOBAL_TIMEOUT = new TimeoutContext(new TimeoutSettings(0L, 0L, - 0L, null, 0L)); - - private static final TimeoutContext TIMEOUT_CONTEXT_EXPIRED_GLOBAL_TIMEOUT = new TimeoutContext(new TimeoutSettings(0L, 0L, - 0L, 1L, 0L)); - - private static final TimeoutContext TIMEOUT_CONTEXT_INFINITE_GLOBAL_TIMEOUT = new TimeoutContext(new TimeoutSettings(0L, 0L, - 0L, 0L, 0L)); private static final String EXPECTED_TIMEOUT_MESSAGE = "Retry attempt exceeded the timeout limit."; - static Stream infiniteTimeout() { + private static Stream atMostTwoRetriesAndUnlimitedRetries() { return Stream.of( - arguments(named("Infinite timeoutMs", TIMEOUT_CONTEXT_INFINITE_GLOBAL_TIMEOUT)) - ); + arguments(named("at most two retries", new RetryState(2))), + arguments(named("unlimited retries", new RetryState()))); } - static Stream expiredTimeout() { + private static Stream noRetries() { return Stream.of( - arguments(named("Expired timeoutMs", TIMEOUT_CONTEXT_EXPIRED_GLOBAL_TIMEOUT)) - ); + arguments(named("no retries", new RetryState(0)))); } - static Stream noTimeout() { - return Stream.of( - arguments(named("No timeoutMs", TIMEOUT_CONTEXT_NO_GLOBAL_TIMEOUT)) - ); - } - - @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void unlimitedAttemptsAndAdvance(final TimeoutContext timeoutContext) { + @Test + void unlimitedAttemptsAndAdvance() { + final RetryState retryState = new RetryState(); RuntimeException attemptException = new RuntimeException(); - RetryState retryState = new RetryState(timeoutContext); assertAll( () -> assertTrue(retryState.isFirstAttempt()), () -> assertEquals(0, retryState.attempt()) @@ -99,7 +81,7 @@ void unlimitedAttemptsAndAdvance(final TimeoutContext timeoutContext) { @Test void limitedAttemptsAndAdvance() { - RetryState retryState = RetryState.withNonRetryableState(); + RetryState retryState = new RetryState(0); RuntimeException attemptException = new RuntimeException(); assertAll( () -> assertTrue(retryState.isFirstAttempt()), @@ -112,54 +94,39 @@ void limitedAttemptsAndAdvance() { } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void markAsLastAttemptAdvanceWithRuntimeException(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void markAsLastAttemptAdvanceWithRuntimeException(final RetryState retryState) { retryState.markAsLastAttempt(); RuntimeException attemptException = new RuntimeException(); assertAdvanceOrThrowThrows(attemptException, retryState, attemptException, (rs, e) -> fail()); } @ParameterizedTest(name = "should advance with non-retryable error when marked as last attempt and : ''{0}''") - @MethodSource({"infiniteTimeout", "expiredTimeout", "noTimeout"}) - void markAsLastAttemptAdvanceWithError(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"noRetries", "atMostTwoRetriesAndUnlimitedRetries"}) + void markAsLastAttemptAdvanceWithError(final RetryState retryState) { retryState.markAsLastAttempt(); Error attemptException = new Error(); assertAdvanceOrThrowThrows(attemptException, retryState, attemptException, (rs, e) -> fail()); } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void breakAndThrowIfRetryAndFirstAttempt(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void breakAndThrowIfRetryAndFirstAttempt(final RetryState retryState) { retryState.breakAndThrowIfRetryAnd(Assertions::fail); assertAdvanceOrThrowDoesNotThrow(retryState, new RuntimeException()); } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void breakAndThrowIfRetryAndFalse(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void breakAndThrowIfRetryAndFalse(final RetryState retryState) { advance(retryState); retryState.breakAndThrowIfRetryAnd(() -> false); assertAdvanceOrThrowDoesNotThrow(retryState, new RuntimeException()); } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void breakAndThrowIfRetryAndTrue(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); - advance(retryState); - assertThrows(RuntimeException.class, () -> retryState.breakAndThrowIfRetryAnd(() -> true)); - RuntimeException attemptException = new RuntimeException(); - assertAdvanceOrThrowThrows(attemptException, retryState, attemptException); - } - - @Test - void breakAndThrowIfRetryAndTrueWithExpiredTimeout() { - TimeoutContext tContextMock = mock(TimeoutContext.class); - RetryState retryState = new RetryState(tContextMock); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void breakAndThrowIfRetryAndTrue(final RetryState retryState) { advance(retryState); assertThrows(RuntimeException.class, () -> retryState.breakAndThrowIfRetryAnd(() -> true)); RuntimeException attemptException = new RuntimeException(); @@ -167,9 +134,8 @@ void breakAndThrowIfRetryAndTrueWithExpiredTimeout() { } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void breakAndThrowIfRetryIfPredicateThrows(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void breakAndThrowIfRetryIfPredicateThrows(final RetryState retryState) { advance(retryState); RuntimeException exception = new RuntimeException(); assertSame( @@ -181,9 +147,8 @@ void breakAndThrowIfRetryIfPredicateThrows(final TimeoutContext timeoutContext) } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void breakAndCompleteIfRetryAndFirstAttempt(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void breakAndCompleteIfRetryAndFirstAttempt(final RetryState retryState) { SupplyingCallback callback = new SupplyingCallback<>(); assertFalse(retryState.breakAndCompleteIfRetryAnd(Assertions::fail, callback)); assertFalse(callback.completed()); @@ -191,9 +156,8 @@ void breakAndCompleteIfRetryAndFirstAttempt(final TimeoutContext timeoutContext) } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void breakAndCompleteIfRetryAndFalse(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void breakAndCompleteIfRetryAndFalse(final RetryState retryState) { advance(retryState); SupplyingCallback callback = new SupplyingCallback<>(); assertFalse(retryState.breakAndCompleteIfRetryAnd(() -> false, callback)); @@ -202,9 +166,8 @@ void breakAndCompleteIfRetryAndFalse(final TimeoutContext timeoutContext) { } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void breakAndCompleteIfRetryAndTrue(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void breakAndCompleteIfRetryAndTrue(final RetryState retryState) { advance(retryState); SupplyingCallback callback = new SupplyingCallback<>(); assertTrue(retryState.breakAndCompleteIfRetryAnd(() -> true, callback)); @@ -214,9 +177,8 @@ void breakAndCompleteIfRetryAndTrue(final TimeoutContext timeoutContext) { } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void breakAndCompleteIfRetryAndPredicateThrows(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void breakAndCompleteIfRetryAndPredicateThrows(final RetryState retryState) { advance(retryState); Error exception = new Error(); SupplyingCallback callback = new SupplyingCallback<>(); @@ -230,19 +192,16 @@ void breakAndCompleteIfRetryAndPredicateThrows(final TimeoutContext timeoutConte } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void advanceOrThrowPredicateFalse(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void advanceOrThrowPredicateFalse(final RetryState retryState) { RuntimeException attemptException = new RuntimeException(); assertAdvanceOrThrowThrows(attemptException, retryState, attemptException, (rs, e) -> false); } @ParameterizedTest - @MethodSource({"infiniteTimeout"}) - @DisplayName("should rethrow detected timeout exception even if timeout in retry state is not expired") - void advanceReThrowDetectedTimeoutExceptionEvenIfTimeoutInRetryStateIsNotExpired(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); - + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + @DisplayName("should rethrow detected timeout exception") + void advanceReThrowDetectedTimeoutException(final RetryState retryState) { MongoOperationTimeoutException expectedTimeoutException = TimeoutContext.createMongoTimeoutException("Server selection failed"); assertAdvanceOrThrowThrows(expectedTimeoutException, retryState, expectedTimeoutException, (e1, e2) -> expectedTimeoutException, @@ -252,7 +211,7 @@ void advanceReThrowDetectedTimeoutExceptionEvenIfTimeoutInRetryStateIsNotExpired @Test @DisplayName("should throw timeout exception from retry, when transformer swallows original timeout exception") void advanceThrowTimeoutExceptionWhenTransformerSwallowOriginalTimeoutException() { - RetryState retryState = new RetryState(TIMEOUT_CONTEXT_INFINITE_GLOBAL_TIMEOUT); + RetryState retryState = new RetryState(); RuntimeException previousAttemptException = new RuntimeException(); MongoOperationTimeoutException latestAttemptException = TimeoutContext.createMongoTimeoutException("Server selection failed"); @@ -275,7 +234,7 @@ void advanceThrowTimeoutExceptionWhenTransformerSwallowOriginalTimeoutException( @Test @DisplayName("should throw original timeout exception from retry, when transformer returns original timeout exception") void advanceThrowOriginalTimeoutExceptionWhenTransformerReturnsOriginalTimeoutException() { - RetryState retryState = new RetryState(TIMEOUT_CONTEXT_INFINITE_GLOBAL_TIMEOUT); + RetryState retryState = new RetryState(); RuntimeException previousAttemptException = new RuntimeException(); MongoOperationTimeoutException expectedTimeoutException = TimeoutContext .createMongoTimeoutException("Server selection failed"); @@ -291,15 +250,14 @@ void advanceThrowOriginalTimeoutExceptionWhenTransformerReturnsOriginalTimeoutEx @Test void advanceOrThrowPredicateTrueAndLastAttempt() { - RetryState retryState = RetryState.withNonRetryableState(); + RetryState retryState = new RetryState(0); Error attemptException = new Error(); assertAdvanceOrThrowThrows(attemptException, retryState, attemptException); } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void advanceOrThrowPredicateThrowsAfterFirstAttempt(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void advanceOrThrowPredicateThrowsAfterFirstAttempt(final RetryState retryState) { RuntimeException predicateException = new RuntimeException(); RuntimeException attemptException = new RuntimeException(); assertAdvanceOrThrowThrows(predicateException, retryState, attemptException, @@ -313,7 +271,7 @@ void advanceOrThrowPredicateThrowsAfterFirstAttempt(final TimeoutContext timeout @Test void advanceOrThrowPredicateThrowsTimeoutAfterFirstAttempt() { - RetryState retryState = new RetryState(TIMEOUT_CONTEXT_EXPIRED_GLOBAL_TIMEOUT); + RetryState retryState = new RetryState(); RuntimeException predicateException = new RuntimeException(); RuntimeException attemptException = new MongoOperationTimeoutException(EXPECTED_TIMEOUT_MESSAGE); MongoOperationTimeoutException mongoOperationTimeoutException = assertThrows(MongoOperationTimeoutException.class, @@ -328,9 +286,8 @@ void advanceOrThrowPredicateThrowsTimeoutAfterFirstAttempt() { } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void advanceOrThrowPredicateThrows(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void advanceOrThrowPredicateThrows(final RetryState retryState) { RuntimeException firstAttemptException = new RuntimeException(); retryState.advanceOrThrow(firstAttemptException, (e1, e2) -> e2, (rs, e) -> true); RuntimeException secondAttemptException = new RuntimeException(); @@ -345,9 +302,8 @@ void advanceOrThrowPredicateThrows(final TimeoutContext timeoutContext) { } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout", "expiredTimeout"}) - void advanceOrThrowTransformerThrowsAfterFirstAttempt(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"noRetries", "atMostTwoRetriesAndUnlimitedRetries"}) + void advanceOrThrowTransformerThrowsAfterFirstAttempt(final RetryState retryState) { RuntimeException transformerException = new RuntimeException(); assertAdvanceOrThrowThrows(transformerException, retryState, new AssertionError(), (e1, e2) -> { @@ -357,9 +313,8 @@ void advanceOrThrowTransformerThrowsAfterFirstAttempt(final TimeoutContext timeo } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void advanceOrThrowTransformerThrows(final TimeoutContext timeoutContext) throws Throwable { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void advanceOrThrowTransformerThrows(final RetryState retryState) throws Throwable { Error firstAttemptException = new Error(); retryState.advanceOrThrow(firstAttemptException, (e1, e2) -> e2, (rs, e) -> true); RuntimeException transformerException = new RuntimeException(); @@ -371,9 +326,8 @@ void advanceOrThrowTransformerThrows(final TimeoutContext timeoutContext) throws } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void advanceOrThrowTransformAfterFirstAttempt(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void advanceOrThrowTransformAfterFirstAttempt(final RetryState retryState) { RuntimeException attemptException = new RuntimeException(); RuntimeException transformerResult = new RuntimeException(); assertAdvanceOrThrowThrows(transformerResult, retryState, attemptException, @@ -390,7 +344,7 @@ void advanceOrThrowTransformAfterFirstAttempt(final TimeoutContext timeoutContex @Test void advanceOrThrowTransformThrowsTimeoutExceptionAfterFirstAttempt() { - RetryState retryState = new RetryState(TIMEOUT_CONTEXT_EXPIRED_GLOBAL_TIMEOUT); + RetryState retryState = new RetryState(); RuntimeException attemptException = new MongoOperationTimeoutException(EXPECTED_TIMEOUT_MESSAGE); RuntimeException transformerResult = new RuntimeException(); @@ -412,9 +366,8 @@ void advanceOrThrowTransformThrowsTimeoutExceptionAfterFirstAttempt() { } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void advanceOrThrowTransform(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void advanceOrThrowTransform(final RetryState retryState) { RuntimeException firstAttemptException = new RuntimeException(); retryState.advanceOrThrow(firstAttemptException, (e1, e2) -> e2, (rs, e) -> true); RuntimeException secondAttemptException = new RuntimeException(); @@ -432,9 +385,8 @@ void advanceOrThrowTransform(final TimeoutContext timeoutContext) { } @ParameterizedTest - @MethodSource({"infiniteTimeout", "noTimeout"}) - void attachAndAttachment(final TimeoutContext timeoutContext) { - RetryState retryState = new RetryState(timeoutContext); + @MethodSource({"atMostTwoRetriesAndUnlimitedRetries"}) + void attachAndAttachment(final RetryState retryState) { AttachmentKey attachmentKey = AttachmentKeys.maxWireVersion(); int attachmentValue = 1; assertFalse(retryState.attachment(attachmentKey).isPresent());