blob: f554cfd212098e1f5623452d5410332e7f1d059f [file] [log] [blame]
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License 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 org.apache.aurora.common.util;
import com.google.common.base.Preconditions;
import org.apache.aurora.common.quantity.Amount;
import org.apache.aurora.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 final Random random;
/**
* 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 initial 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
* @param random An instance of the random util which can be used for applying jitter to backoff.
*/
public TruncatedBinaryBackoff(
Amount<Long, Time> initialBackoff,
Amount<Long, Time> maxBackoff,
boolean stopAtMax,
Random random) {
Preconditions.checkNotNull(initialBackoff);
Preconditions.checkNotNull(maxBackoff);
Preconditions.checkNotNull(random);
Preconditions.checkArgument(initialBackoff.getValue() > 0);
Preconditions.checkArgument(maxBackoff.compareTo(initialBackoff) >= 0);
initialBackoffMs = initialBackoff.as(Time.MILLISECONDS);
maxBackoffIntervalMs = maxBackoff.as(Time.MILLISECONDS);
this.stopAtMax = stopAtMax;
this.random = random;
}
/**
* Same as the main constructor
* {@link TruncatedBinaryBackoff#TruncatedBinaryBackoff(Amount, Amount, boolean, Random)},
* but this will use a default Random implementation returned by
* {@link Random.Util#newDefaultRandom()}.
*
* @param initialBackoff the initial 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) {
this(initialBackoff, maxBackoff, stopAtMax, Random.Util.newDefaultRandom());
}
/**
* Same as the constructor
* {@link TruncatedBinaryBackoff#TruncatedBinaryBackoff(Amount, Amount, boolean)},
* but this will always return true from shouldContinue().
*
* @param initialBackoff the initial 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 halfBackoff = (lastBackoffMs == 0) ? initialBackoffMs : lastBackoffMs;
return Math.min(
maxBackoffIntervalMs,
halfBackoff + Math.round(random.nextDouble() * halfBackoff));
}
@Override
public boolean shouldContinue(long lastBackoffMs) {
Preconditions.checkArgument(lastBackoffMs >= 0);
boolean stop = stopAtMax && (lastBackoffMs >= maxBackoffIntervalMs);
return !stop;
}
}