From d2f113f29290c95f35b1ec16a528cabaf8805f86 Mon Sep 17 00:00:00 2001 From: fjtirado Date: Mon, 3 Nov 2025 16:05:03 +0100 Subject: [PATCH] [Fix #950] Fix retry attemp limit Signed-off-by: fjtirado --- .../impl/executors/TryExecutor.java | 12 +++++----- .../executors/retry/DefaultRetryExecutor.java | 9 ++++---- .../impl/executors/retry/RetryExecutor.java | 3 ++- .../impl/test/RetryTest.java | 22 +++++++++++++++++++ .../try-catch-retry-inline.yaml | 2 +- .../try-catch-retry-reusable.yaml | 4 ++-- 6 files changed, 37 insertions(+), 15 deletions(-) diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TryExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TryExecutor.java index a071d996..cb52d5b2 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TryExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/TryExecutor.java @@ -182,17 +182,17 @@ private CompletableFuture handleException( completable = completable .thenCompose( - model -> retryIntervalExecutor.get().retry(workflow, taskContext, model)) + model -> + retryIntervalExecutor + .get() + .retry(workflow, taskContext, model) + .orElse(CompletableFuture.failedFuture(e))) .thenCompose(model -> doIt(workflow, taskContext, model)); } } return completable; } else { - if (e instanceof RuntimeException) { - throw (RuntimeException) e; - } else { - throw new RuntimeException(e); - } + return CompletableFuture.failedFuture(e); } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/retry/DefaultRetryExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/retry/DefaultRetryExecutor.java index fcfb83c8..04f74cac 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/retry/DefaultRetryExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/retry/DefaultRetryExecutor.java @@ -44,19 +44,18 @@ public DefaultRetryExecutor( } @Override - public CompletableFuture retry( + public Optional> retry( WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel model) { - CompletableFuture completable = new CompletableFuture<>(); short numAttempts = taskContext.retryAttempt(); if (numAttempts++ < maxAttempts && WorkflowUtils.whenExceptTest( whenFilter, exceptFilter, workflowContext, taskContext, model)) { taskContext.retryAttempt(numAttempts); Duration delay = intervalFunction.apply(workflowContext, taskContext, model, numAttempts); + CompletableFuture completable = new CompletableFuture<>(); completable.completeOnTimeout(model, delay.toMillis(), TimeUnit.MILLISECONDS); - } else { - completable.complete(model); + return Optional.of(completable); } - return completable; + return Optional.empty(); } } diff --git a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/retry/RetryExecutor.java b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/retry/RetryExecutor.java index ff3bd7d3..00716180 100644 --- a/impl/core/src/main/java/io/serverlessworkflow/impl/executors/retry/RetryExecutor.java +++ b/impl/core/src/main/java/io/serverlessworkflow/impl/executors/retry/RetryExecutor.java @@ -18,9 +18,10 @@ import io.serverlessworkflow.impl.TaskContext; import io.serverlessworkflow.impl.WorkflowContext; import io.serverlessworkflow.impl.WorkflowModel; +import java.util.Optional; import java.util.concurrent.CompletableFuture; public interface RetryExecutor { - CompletableFuture retry( + Optional> retry( WorkflowContext worfklowContext, TaskContext taskContext, WorkflowModel model); } diff --git a/impl/test/src/test/java/io/serverlessworkflow/impl/test/RetryTest.java b/impl/test/src/test/java/io/serverlessworkflow/impl/test/RetryTest.java index cc745f64..76846272 100644 --- a/impl/test/src/test/java/io/serverlessworkflow/impl/test/RetryTest.java +++ b/impl/test/src/test/java/io/serverlessworkflow/impl/test/RetryTest.java @@ -16,9 +16,11 @@ package io.serverlessworkflow.impl.test; import static io.serverlessworkflow.api.WorkflowReader.readWorkflowFromClasspath; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import com.fasterxml.jackson.databind.JsonNode; import io.serverlessworkflow.impl.WorkflowApplication; +import io.serverlessworkflow.impl.WorkflowException; import io.serverlessworkflow.impl.WorkflowModel; import io.serverlessworkflow.impl.jackson.JsonUtils; import java.io.IOException; @@ -32,6 +34,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -84,4 +87,23 @@ void testRetry(String path) throws IOException { .atMost(Duration.ofSeconds(1)) .until(() -> future.join().as(JsonNode.class).orElseThrow().equals(result)); } + + @Test + void testRetryEnd() throws IOException { + apiServer.enqueue(new MockResponse().setResponseCode(404)); + apiServer.enqueue(new MockResponse().setResponseCode(404)); + apiServer.enqueue(new MockResponse().setResponseCode(404)); + apiServer.enqueue(new MockResponse().setResponseCode(404)); + apiServer.enqueue(new MockResponse().setResponseCode(404)); + apiServer.enqueue(new MockResponse().setResponseCode(404)); + assertThatThrownBy( + () -> + app.workflowDefinition( + readWorkflowFromClasspath( + "workflows-samples/try-catch-retry-reusable.yaml")) + .instance(Map.of()) + .start() + .join()) + .hasCauseInstanceOf(WorkflowException.class); + } } diff --git a/impl/test/src/test/resources/workflows-samples/try-catch-retry-inline.yaml b/impl/test/src/test/resources/workflows-samples/try-catch-retry-inline.yaml index 0dc028ae..76037fec 100644 --- a/impl/test/src/test/resources/workflows-samples/try-catch-retry-inline.yaml +++ b/impl/test/src/test/resources/workflows-samples/try-catch-retry-inline.yaml @@ -1,7 +1,7 @@ document: dsl: '1.0.0' namespace: default - name: try-catch-retry + name: try-catch-retry-inline version: '0.1.0' do: - tryGetPet: diff --git a/impl/test/src/test/resources/workflows-samples/try-catch-retry-reusable.yaml b/impl/test/src/test/resources/workflows-samples/try-catch-retry-reusable.yaml index 5c743847..ecf66a46 100644 --- a/impl/test/src/test/resources/workflows-samples/try-catch-retry-reusable.yaml +++ b/impl/test/src/test/resources/workflows-samples/try-catch-retry-reusable.yaml @@ -1,7 +1,7 @@ document: dsl: '1.0.0' namespace: default - name: try-catch-retry + name: try-catch-retry-reusable version: '0.1.0' use: retries: @@ -9,7 +9,7 @@ use: delay: milliseconds: 10 backoff: - exponential: {} + constant: {} limit: attempt: count: 5