Skip to content

Commit

Permalink
Support cancel feature for AsyncFeign
Browse files Browse the repository at this point in the history
  • Loading branch information
wplong11 committed Oct 22, 2022
1 parent ca761ae commit 0dbaf30
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 1 deletion.
2 changes: 1 addition & 1 deletion core/src/main/java/feign/AsynchronousMethodHandler.java
Expand Up @@ -93,7 +93,7 @@ private CompletableFuture<Object> executeAndDecode(RequestTemplate template,
executeAndDecode(template, options)
.whenComplete((response, throwable) -> {
if (throwable != null) {
if (shouldRetry(retryer, throwable, resultFuture)) {
if (!resultFuture.isDone() && shouldRetry(retryer, throwable, resultFuture)) {
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
Expand Down
49 changes: 49 additions & 0 deletions core/src/test/java/feign/AsyncFeignTest.java
Expand Up @@ -48,10 +48,13 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
Expand Down Expand Up @@ -656,6 +659,52 @@ public void whenReturnTypeIsResponseNoErrorHandling() throws Throwable {
execs.shutdown();
}

@Test
public void cancelRetry() throws Throwable {
// Arrange
final int expectedTryCount = 1; // TODO: Modify it to work even if it is more than 2 tries
final CompletableFuture<Boolean> maximumTryCompleted = new CompletableFuture<>();
final AtomicInteger actualTryCount = new AtomicInteger();
final AtomicBoolean isCancelled = new AtomicBoolean(true);

final int RUNNING_TIME_MILLIS = 100;
final ExecutorService execs = Executors.newSingleThreadExecutor();
final AsyncClient<Void> clientMock = (request, options, requestContext) ->
CompletableFuture.supplyAsync(() -> {
final int tryCount = actualTryCount.addAndGet(1);
if (tryCount < expectedTryCount) {
throw new CompletionException(new IOException());
}

if (tryCount > expectedTryCount) {
isCancelled.set(false);
throw new CompletionException(new IOException());
}

maximumTryCompleted.complete(true);
try {
Thread.sleep(RUNNING_TIME_MILLIS);
throw new IOException();
} catch (Throwable e) {
throw new CompletionException(e);
}
}, execs);
final TestInterfaceAsync sut = AsyncFeign.<Void>builder()
.client(clientMock)
.target(TestInterfaceAsync.class, "http://localhost:" + server.getPort());

// Act
final CompletableFuture<String> actual = sut.post();
maximumTryCompleted.join();
actual.cancel(true);
Thread.sleep(RUNNING_TIME_MILLIS * 5);

// Assert
assertThat(actualTryCount.get()).isEqualTo(expectedTryCount);
assertThat(isCancelled.get()).isTrue();
execs.shutdown();
}

private static class MockRetryer implements Retryer {

boolean tripped;
Expand Down

0 comments on commit 0dbaf30

Please sign in to comment.