package org.corfudb.util.retry; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import java.time.Duration; import java.util.Random; /** * This class implements a basic exponential backoff retry. * <p> * Created by mwei on 9/1/15. */ @Slf4j public class ExponentialBackoffRetry<E extends Exception, F extends Exception, G extends Exception, H extends Exception, O, A extends IRetry> extends AbstractRetry<E, F, G, H, O, ExponentialBackoffRetry> { private static final int DEFAULT_BASE = 2; private static final float DEFAULT_RANDOM_PORTION = 0f; private static final Duration DEFAULT_BACKOFF_DURATION = Duration.ofMinutes(1); private long retryCounter = 0; private long nextBackoffTime = 0; @Getter @Setter private Duration backoffDuration = DEFAULT_BACKOFF_DURATION; /** * Base to multiply */ @Getter @Setter private int base = DEFAULT_BASE; /** * Portion of the number to be randomized, between 0 and 1. * 0 - no random portion, 1 - randomize everything. */ @Getter @Setter private float randomPortion = DEFAULT_RANDOM_PORTION; /** * Additional fixed retry time in milliseconds for each retry * @param runFunction */ @Getter @Setter private long extraWait = 0; public ExponentialBackoffRetry(IRetryable runFunction) { super(runFunction); } @Override public void nextWait() throws InterruptedException { if (nextBackoffTime==0) { nextBackoffTime = System.currentTimeMillis()+backoffDuration.toMillis(); } retryCounter++; long sleepTime = (long) Math.pow(base, retryCounter); sleepTime += extraWait; float randomPart = new Random().nextFloat()*randomPortion; sleepTime -= sleepTime*randomPart; if (System.currentTimeMillis()+sleepTime>nextBackoffTime) { nextBackoffTime = 0; retryCounter = 1; sleepTime = base + extraWait; sleepTime -= sleepTime*randomPart; } Thread.sleep(sleepTime); } }