summaryrefslogtreecommitdiffstats
path: root/vespajlib
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2024-03-05 14:42:05 +0100
committerMartin Polden <mpolden@mpolden.no>2024-03-05 14:44:45 +0100
commite8e099766712a504bda2a96e664265cd77a3c223 (patch)
tree606664971d39599dfdcb3b24a379210bc151f245 /vespajlib
parent65c1240681f7e4d091776974a2f4348937d00d75 (diff)
Move ExponentialBackoff to vespajlib
Diffstat (limited to 'vespajlib')
-rw-r--r--vespajlib/src/main/java/com/yahoo/time/ExponentialBackoff.java47
-rw-r--r--vespajlib/src/test/java/com/yahoo/time/ExponentialBackoffTest.java31
2 files changed, 78 insertions, 0 deletions
diff --git a/vespajlib/src/main/java/com/yahoo/time/ExponentialBackoff.java b/vespajlib/src/main/java/com/yahoo/time/ExponentialBackoff.java
new file mode 100644
index 00000000000..95926a143e0
--- /dev/null
+++ b/vespajlib/src/main/java/com/yahoo/time/ExponentialBackoff.java
@@ -0,0 +1,47 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.time;
+
+import java.time.Duration;
+import java.util.Random;
+
+/**
+ * Calculate a delay using an exponential backoff algorithm. Based on ExponentialBackOff in google-http-client.
+ *
+ * @author mpolden
+ */
+public class ExponentialBackoff {
+
+ private static final double RANDOMIZATION_FACTOR = 0.5;
+
+ private final Duration initialDelay;
+ private final Duration maxDelay;
+ private final Random random;
+
+ public ExponentialBackoff(Duration initialDelay, Duration maxDelay) {
+ this(initialDelay, maxDelay, new Random());
+ }
+
+ ExponentialBackoff(Duration initialDelay, Duration maxDelay, Random random) {
+ this.initialDelay = requireNonNegative(initialDelay);
+ this.maxDelay = requireNonNegative(maxDelay);
+ this.random = random;
+ }
+
+ /** Return the delay of given attempt */
+ public Duration delay(int attempt) {
+ if (attempt < 1) throw new IllegalArgumentException("Attempt must be positive");
+ double currentDelay = attempt * initialDelay.toMillis();
+ double delta = RANDOMIZATION_FACTOR * currentDelay;
+ double lowerDelay = currentDelay - delta;
+ double upperDelay = currentDelay + delta;
+ long millis = (long) Math.min(lowerDelay + (random.nextDouble() * (upperDelay - lowerDelay + 1)),
+ maxDelay.toMillis());
+ return Duration.ofMillis(millis);
+ }
+
+ private static Duration requireNonNegative(Duration d) {
+ if (d.isNegative()) throw new IllegalArgumentException("Invalid duration: " + d);
+ return d;
+ }
+
+}
diff --git a/vespajlib/src/test/java/com/yahoo/time/ExponentialBackoffTest.java b/vespajlib/src/test/java/com/yahoo/time/ExponentialBackoffTest.java
new file mode 100644
index 00000000000..07295891cd0
--- /dev/null
+++ b/vespajlib/src/test/java/com/yahoo/time/ExponentialBackoffTest.java
@@ -0,0 +1,31 @@
+// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.time;
+
+import org.junit.Test;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Random;
+import java.util.stream.IntStream;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author mpolden
+ */
+public class ExponentialBackoffTest {
+
+ @Test
+ public void delay() {
+ ExponentialBackoff b = new ExponentialBackoff(Duration.ofSeconds(1), Duration.ofSeconds(10), new Random(1000));
+ assertEquals(List.of(Duration.ofMillis(1210),
+ Duration.ofMillis(2150),
+ Duration.ofMillis(4340),
+ Duration.ofMillis(2157),
+ Duration.ofMillis(4932)),
+ IntStream.rangeClosed(1, 5)
+ .mapToObj(b::delay)
+ .toList());
+ }
+
+}