// =================================================================================================
// 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.
// =================================================================================================
package com.twitter.common.util;
import com.google.common.base.Preconditions;
import com.twitter.common.quantity.Amount;
import com.twitter.common.quantity.Time;
/**
* A BackoffStrategy that implements truncated binary exponential backoff.
*/
public class TruncatedBinaryBackoff implements BackoffStrategy {
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
* @param maxBackoff the maximum amount of time to backoff
* @param stopAtMax whether shouldContinue() returns false when the max is reached
*/
public TruncatedBinaryBackoff(Amount<Long, Time> initialBackoff,
Amount<Long, Time> maxBackoff, boolean stopAtMax) {
Preconditions.checkNotNull(initialBackoff);
Preconditions.checkNotNull(maxBackoff);
Preconditions.checkArgument(initialBackoff.getValue() > 0);
Preconditions.checkArgument(maxBackoff.compareTo(initialBackoff) >= 0);
initialBackoffMs = initialBackoff.as(Time.MILLISECONDS);
maxBackoffIntervalMs = maxBackoff.as(Time.MILLISECONDS);
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(Amount<Long, Time> initialBackoff, Amount<Long, Time> maxBackoff) {
this(initialBackoff, maxBackoff, false);
}
@Override
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;
}
@Override
public boolean shouldContinue() {
return !stop;
}
}