Skip to content

Instantly share code, notes, and snippets.

@bric3
Last active February 3, 2022 10:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bric3/2e6616c851614d2bd0fda330d24a6117 to your computer and use it in GitHub Desktop.
Save bric3/2e6616c851614d2bd0fda330d24a6117 to your computer and use it in GitHub Desktop.
Exponential backoff counter implementing the Full Jitter algorithm from AWS article by Marc Brooker
import java.util.Random;
/**
* Exponential backoff counter implementing the Full Jitter algorithm from <a href="https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/">aws</a> by Marc Brooker.
*
* <p>
* The idea of backoff is to introduce a delay between retries, so that client don't compete
* on the same resource. However it has been observed that clients may retry at the same time,
* thus clustering calls on the same resource.
* </p>
* <p>
* This algorithm e idea of this algorithm is to introduce some randomness (jitter) to the backoff,
* so that clients don't compete for the same resource at exactly the same time.
* </p>
* <p>
* From this article adding the jitter helps to spread the calls over the time,
* and more over helps to keep an approximate constant rate of calls. In other words
* it increases the overall throughput.
* </p>
*
* <p>
* So adding jitter to the backoff help is the reduction of failed attempts and thus
* reducing the overall work of the system, as such put less pressure o resources
* like the network itself.
* </p>
*
* <p>
* The article describes 3 jitter algorithms:
* <ol>
* <li>Full jitter: <pre><code>
* sleep = random(0, min(cap, base &#xd7; 2 <sup>attempts</sup>))
* </code></pre></li>
* <li>Equal jitter: <pre><code>
* temp = min(cap, base &#xd7; 2 <sup>attempts</sup>)
* sleep = temp &#xf7; 2 + random(0, temp &#xf7; 2)
* </pre></code></li>
* <li>Decorrelated jitter: <pre><code>
* sleep = min(cap, random(base, previousSleep &#xd7; 3))
* </code></pre></li>
* </ol>
* Of the jitter approaches the <em>Equal jitter</em> is the loser, but it's
* less clear between <em>Decorrelated jitter</em> and <em>Full jitter</em>.
* <br>
* <strong>This implementation uses the <em>Full jitter</em> algorithm.</strong>
* </p>
*
* <p>
* This class is not thread safe.
* </p>
*/
public final class ExponentialBackoff {
private final Random jitterRandom;
private final int baseMillis;
private final int capMillis;
private int previousAttempt = -1;
/**
* Creates a new capped exponential backoff timed counter.
*
* <p>Example waiting between 1s and 30s:
* <pre><code>
* var exponentialBackoff = new ExponentialBackoff(1_000, 30_000, new Random());
* </code></pre>
* </p>
*
* @param baseMillis The minimum backoff time in milliseconds.
* @param capMillis The maximum backoff time in milliseconds.
* @param jitterRandom The jitterRandom source to use.
*/
public ExponentialBackoff(final int baseMillis, final int capMillis, final Random jitterRandom) {
this.baseMillis = baseMillis;
this.capMillis = capMillis;
this.jitterRandom = jitterRandom;
}
/**
* Returns the backoff time in milliseconds.
*
* <p>
* Example:
* <pre><code>
* if (error) {
* var backoff = exponentialBackoff.backoffMillis();
* Thread.sleep(backoff); // or instead of blocking schedule the task for later.
* }
* </code></pre>
* </p>
*
* @return The backoff time in milliseconds.
*/
public int backoffMillis() {
previousAttempt += 1;
final int multiplier = (int) Math.pow(2, previousAttempt);
final int upper = Math.min(capMillis, baseMillis * multiplier);
return jitterRandom.nextInt(upper);
}
/**
* Resets the backoff for reuse.
*/
public void reset() {
previousAttempt = -1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment