From 218b8dc07927ab25ca6366e04a4bdd2a0c29ecfe Mon Sep 17 00:00:00 2001 From: Max Lambrecht Date: Fri, 1 Jun 2018 11:44:44 -0300 Subject: [PATCH] Refactoring the retries handling --- .../spiffe/api/svid/WorkloadAPIClient.java | 49 +++++-------- .../spiffe/api/svid/retry/RetryHandler.java | 36 ++++++++++ .../spiffe/api/svid/retry/RetryPolicy.java | 72 +++++++++++++++++++ 3 files changed, 125 insertions(+), 32 deletions(-) create mode 100644 src/main/java/spiffe/api/svid/retry/RetryHandler.java create mode 100644 src/main/java/spiffe/api/svid/retry/RetryPolicy.java diff --git a/src/main/java/spiffe/api/svid/WorkloadAPIClient.java b/src/main/java/spiffe/api/svid/WorkloadAPIClient.java index f94cfc4..50d4aa0 100644 --- a/src/main/java/spiffe/api/svid/WorkloadAPIClient.java +++ b/src/main/java/spiffe/api/svid/WorkloadAPIClient.java @@ -4,13 +4,12 @@ import io.grpc.Status; import io.grpc.stub.StreamObserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import spiffe.api.svid.retry.RetryHandler; +import spiffe.api.svid.retry.RetryPolicy; import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import java.util.function.Function; import static spiffe.api.svid.Workload.*; @@ -24,8 +23,8 @@ public final class WorkloadAPIClient { private SpiffeWorkloadStub spiffeWorkloadStub; - private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); - private RetryState retryState = new RetryState(1, 60, TimeUnit.SECONDS); + private RetryHandler retryHandler; + /** * Constructor @@ -33,6 +32,16 @@ public final class WorkloadAPIClient { */ public WorkloadAPIClient(String spiffeEndpointAddress) { spiffeWorkloadStub = new SpiffeWorkloadStub(spiffeEndpointAddress); + retryHandler = new RetryHandler(new RetryPolicy(1, 60, TimeUnit.SECONDS)); + } + + /** + * Constructor + * @param spiffeEndpointAddress + */ + public WorkloadAPIClient(String spiffeEndpointAddress, RetryPolicy retryPolicy) { + spiffeWorkloadStub = new SpiffeWorkloadStub(spiffeEndpointAddress); + retryHandler = new RetryHandler(retryPolicy); } /** @@ -40,7 +49,8 @@ public final class WorkloadAPIClient { * */ public WorkloadAPIClient() { - spiffeWorkloadStub = new SpiffeWorkloadStub(); + this(null); + } /** @@ -59,8 +69,7 @@ public final class WorkloadAPIClient { public void onError(Throwable t) { LOGGER.error(t.getMessage()); if (isRetryableError(t)) { - scheduledExecutorService.schedule( - () -> fetchX509SVIDs(listener), retryState.delay(), retryState.timeUnit); + retryHandler.scheduleRetry(() -> fetchX509SVIDs(listener)); } } @@ -87,28 +96,4 @@ public final class WorkloadAPIClient { return X509SVIDRequest.newBuilder().build(); } - static class RetryState { - RetryState(long delay, long maxDelay, TimeUnit timeUnit) { - if (delay < 1) { - this.delay = 1; - } else { - this.delay = delay; - } - this.maxDelay = maxDelay; - this.timeUnit = timeUnit; - } - - private long delay; - private long maxDelay; - private TimeUnit timeUnit; - private Function calculateDelay = (d) -> d * 2; - - long delay() { - delay = calculateDelay.apply(delay); - if (delay > maxDelay) { - delay = maxDelay; - } - return delay; - } - } } diff --git a/src/main/java/spiffe/api/svid/retry/RetryHandler.java b/src/main/java/spiffe/api/svid/retry/RetryHandler.java new file mode 100644 index 0000000..6ccd51a --- /dev/null +++ b/src/main/java/spiffe/api/svid/retry/RetryHandler.java @@ -0,0 +1,36 @@ +package spiffe.api.svid.retry; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +/** + * Handle the retries and backoffs. + */ +public class RetryHandler { + + private long nextDelay; + + private RetryPolicy retryPolicy; + + private ScheduledExecutorService scheduledExecutorService; + + /** + * Constructor + * @param retryPolicy + */ + public RetryHandler(RetryPolicy retryPolicy) { + this.nextDelay = retryPolicy.initialDelay(); + this.retryPolicy = retryPolicy; + this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); + } + + /** + * Schedule to execture a Runnable, based on the retry and backoff policy + * Updates the next delay + * @param callable + */ + public void scheduleRetry(Runnable callable) { + scheduledExecutorService.schedule(callable, nextDelay, retryPolicy.timeUnit()); + nextDelay = retryPolicy.nextDelay(nextDelay); + } +} diff --git a/src/main/java/spiffe/api/svid/retry/RetryPolicy.java b/src/main/java/spiffe/api/svid/retry/RetryPolicy.java new file mode 100644 index 0000000..1d29561 --- /dev/null +++ b/src/main/java/spiffe/api/svid/retry/RetryPolicy.java @@ -0,0 +1,72 @@ +package spiffe.api.svid.retry; + +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +/** + * Configuration Parameters for the Retry behavior + * Allow configure initialDelay, maxDelay, timeUnit + * and the Function to calculate the delays + */ +public class RetryPolicy { + + private long initialDelay; + private long maxDelay; + private TimeUnit timeUnit; + private Function backoffFunction; + + /** + * Constructor + * + * Sets default backoff function to multiply by 2 + * @param initialDelay + * @param maxDelay + * @param timeUnit + */ + public RetryPolicy(long initialDelay, long maxDelay, TimeUnit timeUnit) { + if (initialDelay < 1) { + this.initialDelay = 1; + } else { + this.initialDelay = initialDelay; + } + this.maxDelay = maxDelay; + this.timeUnit = timeUnit; + this.backoffFunction = (d) -> d * 2; + + } + + /** + * Constructor + * + * Allow to configure the backoff function + * @param initialDelay + * @param maxDelay + * @param timeUnit + * @param backoffFunction + */ + public RetryPolicy(long initialDelay, long maxDelay, TimeUnit timeUnit, Function backoffFunction) { + this.initialDelay = initialDelay; + this.maxDelay = maxDelay; + this.timeUnit = timeUnit; + this.backoffFunction = backoffFunction; + } + + public long initialDelay() { + return initialDelay; + } + + public TimeUnit timeUnit() { + return timeUnit; + } + + /** + * Calculate the nextDelay based on a currentDelay, applying the backoff function + * If the calculated delay is greater than maxDelay, it returns maxDelay + * @param currentDelay + * @return + */ + public long nextDelay(long currentDelay) { + long next = backoffFunction.apply(currentDelay); + return next < maxDelay ? next : maxDelay; + } +}