Created
June 19, 2014 18:38
-
-
Save rkroll/8323cd0f65ff7fc37a89 to your computer and use it in GitHub Desktop.
twitter TruncatedBinaryBackoff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ================================================================================================= | |
// 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