/** * Copyright 2016 Yahoo Inc. * * 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 com.yahoo.pulsar.client.impl; import java.util.Random; import java.util.concurrent.TimeUnit; public class Backoff { private static final long DEFAULT_INTERVAL_IN_NANOSECONDS = TimeUnit.MILLISECONDS.toNanos(100); private static final long MAX_BACKOFF_INTERVAL_NANOSECONDS = TimeUnit.SECONDS.toNanos(30); private final long initial; private final long max; private long next; private static final Random random = new Random(); public Backoff(long initial, TimeUnit unitInitial, long max, TimeUnit unitMax) { this.initial = unitInitial.toMillis(initial); this.max = unitMax.toMillis(max); this.next = this.initial; } public long next() { long current = this.next; if (current < max) { this.next = Math.min(this.next * 2, this.max); } // Randomly increase the timeout up to 25% to avoid simultaneous retries current += random.nextInt((int) current / 4); return current; } public void reduceToHalf() { if (next > initial) { this.next = Math.max(this.next / 2, this.initial); } } public void reset() { this.next = this.initial; } public static boolean shouldBackoff(long initialTimestamp, TimeUnit unitInitial, int failedAttempts) { long initialTimestampInNano = unitInitial.toNanos(initialTimestamp); long currentTime = System.nanoTime(); long interval = DEFAULT_INTERVAL_IN_NANOSECONDS; for (int i = 1; i < failedAttempts; i++) { interval = interval * 2; if (interval > MAX_BACKOFF_INTERVAL_NANOSECONDS) { interval = MAX_BACKOFF_INTERVAL_NANOSECONDS; break; } } // if the current time is less than the time at which next retry should occur, we should backoff return currentTime < (initialTimestampInNano + interval); } }