This is an automated email from the ASF dual-hosted git repository.
wenjin272 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/flink-agents.git
The following commit(s) were added to refs/heads/main by this push:
new 20d6357f [api][java] Validate retry parameters in RetryExecutor
builder (#1142)
20d6357f is described below
commit 20d6357f51ce29b7774cccd314345497ceeb44c7
Author: william <[email protected]>
AuthorDate: Sun Sep 20 16:43:58 2026 +0800
[api][java] Validate retry parameters in RetryExecutor builder (#1142)
Generated-by: CodeBuddy (Auto model)
---
.../org/apache/flink/agents/api/RetryExecutor.java | 21 ++++++++
.../apache/flink/agents/api/RetryExecutorTest.java | 56 ++++++++++++++++++++++
2 files changed, 77 insertions(+)
diff --git a/api/src/main/java/org/apache/flink/agents/api/RetryExecutor.java
b/api/src/main/java/org/apache/flink/agents/api/RetryExecutor.java
index b369e1d1..b2bd174b 100644
--- a/api/src/main/java/org/apache/flink/agents/api/RetryExecutor.java
+++ b/api/src/main/java/org/apache/flink/agents/api/RetryExecutor.java
@@ -199,7 +199,28 @@ public class RetryExecutor {
return this;
}
+ /**
+ * Builds a {@link RetryExecutor} after checking the configured values.
+ *
+ * @throws IllegalArgumentException if {@code maxRetries} is negative,
if {@code
+ * initialBackoffMs} is negative, or if {@code maxBackoffMs} is
smaller than {@code
+ * initialBackoffMs}
+ */
public RetryExecutor build() {
+ if (maxRetries < 0) {
+ throw new IllegalArgumentException(
+ "maxRetries must be >= 0, but was " + maxRetries);
+ }
+ if (initialBackoffMs < 0) {
+ throw new IllegalArgumentException(
+ "initialBackoffMs must be >= 0, but was " +
initialBackoffMs);
+ }
+ if (maxBackoffMs < initialBackoffMs) {
+ throw new IllegalArgumentException(
+ String.format(
+ "maxBackoffMs must be >= initialBackoffMs, but
was %d < %d",
+ maxBackoffMs, initialBackoffMs));
+ }
return new RetryExecutor(
maxRetries, initialBackoffMs, maxBackoffMs,
retryablePredicate);
}
diff --git
a/api/src/test/java/org/apache/flink/agents/api/RetryExecutorTest.java
b/api/src/test/java/org/apache/flink/agents/api/RetryExecutorTest.java
index 982ea3ce..13bd52eb 100644
--- a/api/src/test/java/org/apache/flink/agents/api/RetryExecutorTest.java
+++ b/api/src/test/java/org/apache/flink/agents/api/RetryExecutorTest.java
@@ -274,4 +274,60 @@ class RetryExecutorTest {
assertThat(result).isEqualTo("success");
assertThat(attempts.get()).isEqualTo(2);
}
+
+ @Test
+ @DisplayName("Reject a negative maxRetries at build time")
+ void testBuildRejectsNegativeMaxRetries() {
+ assertThatThrownBy(() ->
RetryExecutor.builder().maxRetries(-1).build())
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("maxRetries must be >= 0")
+ .hasMessageContaining("-1");
+ }
+
+ @Test
+ @DisplayName("Reject a negative initialBackoffMs at build time")
+ void testBuildRejectsNegativeInitialBackoffMs() {
+ assertThatThrownBy(() ->
RetryExecutor.builder().initialBackoffMs(-5).build())
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("initialBackoffMs must be >= 0")
+ .hasMessageContaining("-5");
+ }
+
+ @Test
+ @DisplayName("Reject a maxBackoffMs smaller than initialBackoffMs")
+ void testBuildRejectsMaxBackoffBelowInitialBackoff() {
+ assertThatThrownBy(
+ () ->
+ RetryExecutor.builder()
+ .initialBackoffMs(100)
+ .maxBackoffMs(50)
+ .build())
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("maxBackoffMs must be >=
initialBackoffMs");
+ }
+
+ @Test
+ @DisplayName("Boundary values are accepted, and zero maxRetries still runs
the operation")
+ void testBuildAcceptsBoundaryValues() {
+ RetryExecutor executor =
+
RetryExecutor.builder().maxRetries(0).initialBackoffMs(0).maxBackoffMs(0).build();
+
+ assertThat(executor.getMaxRetries()).isZero();
+ assertThat(executor.getInitialBackoffMs()).isZero();
+ assertThat(executor.getMaxBackoffMs()).isZero();
+
+ AtomicInteger attempts = new AtomicInteger(0);
+ Callable<String> operation =
+ () -> {
+ attempts.incrementAndGet();
+ throw new SocketTimeoutException("Always fails");
+ };
+
+ assertThatThrownBy(() -> executor.execute(operation, "testOperation"))
+ .isInstanceOf(RuntimeException.class)
+ .hasMessageContaining("failed after 0 retries")
+ .hasMessageContaining("Always fails");
+
+ assertThat(attempts.get()).isEqualTo(1);
+ }
}