Skip to content

Instantly share code, notes, and snippets.

@rkroll
Created June 19, 2014 18:38
Show Gist options
  • Save rkroll/8323cd0f65ff7fc37a89 to your computer and use it in GitHub Desktop.
Save rkroll/8323cd0f65ff7fc37a89 to your computer and use it in GitHub Desktop.
twitter TruncatedBinaryBackoff
// =================================================================================================
// Copyright 2011 Twitter, Inc.
// -------------------------------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this work except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file, or at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =================================================================================================
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
/**
* A BackoffStrategy that implements truncated binary exponential backoff.
*/
public class TruncatedBinaryBackoff {
private final long initialBackoffMs;
private final long maxBackoffIntervalMs;
private final boolean stopAtMax;
private volatile boolean stop = false;
/**
* Creates a new TruncatedBinaryBackoff that will start by backing off for {@code initialBackoff} and then backoff of twice as long each time its called until reaching the {@code maxBackoff} at
* which point shouldContinue() will return false and any future backoffs will always wait for
* that amount of time.
*
* @param initialBackoff
* the intial amount of time to backoff (in seconds)
* @param maxBackoff
* the maximum amount of time to backoff (in seconds)
* @param stopAtMax
* whether shouldContinue() returns false when the max is reached
*/
public TruncatedBinaryBackoff(Long initialBackoff, Long maxBackoff, boolean stopAtMax) {
Preconditions.checkNotNull(initialBackoff);
Preconditions.checkNotNull(maxBackoff);
Preconditions.checkArgument(initialBackoff.longValue() > 0);
Preconditions.checkArgument(maxBackoff.compareTo(initialBackoff) >= 0);
initialBackoffMs = initialBackoff * 1000;
maxBackoffIntervalMs = maxBackoff * 1000;
this.stopAtMax = stopAtMax;
}
/**
* Same as main constructor, but this will always return true from shouldContinue().
*
* @param initialBackoff
* the intial amount of time to backoff
* @param maxBackoff
* the maximum amount of time to backoff
*/
public TruncatedBinaryBackoff(Long initialBackoff, Long maxBackoff) {
this(initialBackoff, maxBackoff, false);
}
public long calculateBackoffMs(long lastBackoffMs) {
Preconditions.checkArgument(lastBackoffMs >= 0);
long backoff = (lastBackoffMs == 0) ? initialBackoffMs : Math.min(maxBackoffIntervalMs, lastBackoffMs * 2);
stop = stop || (stopAtMax && (backoff >= maxBackoffIntervalMs));
return backoff;
}
public boolean shouldContinue() {
return !stop;
}
public <T> void doWork(T input, Predicate<T> completed) throws BackoffTimeoutException {
long interval = 0;
while (!completed.apply(input) && shouldContinue())
{
try {
interval = calculateBackoffMs(interval);
Thread.sleep(interval);
} catch (InterruptedException e) {
throw new BackoffTimeoutException(e);
}
}
}
public <T> T doWork(T input, PredicateWithResult<T> completed) throws BackoffTimeoutException {
long interval = 0;
while (!completed.apply(input) && shouldContinue())
{
try {
interval = calculateBackoffMs(interval);
Thread.sleep(interval);
} catch (InterruptedException e) {
throw new BackoffTimeoutException(e);
}
}
return completed.getItem();
}
}
public class BackoffTimeoutException extends RuntimeException {
private static final long serialVersionUID = -4837005568042294036L;
public BackoffTimeoutException() {
super();
}
public BackoffTimeoutException(String message, Throwable cause) {
super(message, cause);
}
public BackoffTimeoutException(String message) {
super(message);
}
public BackoffTimeoutException(Throwable cause) {
super(cause);
}
}
public interface PredicateWithResult<T extends Object> extends Predicate<T> {
public T getItem();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment