/* * Copyright 2013 David Tinker * * 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 io.qdb.server; /** * Calculates delay intervals base on consecutive failure counts. */ public interface BackoffPolicy { /** * Convert a failure count into a the delay in ms. */ int getDelayMs(int failureCount); /** * What is the default max delay time? */ int getMaxDelayMs(); /** * Sleep for the appropriate amount of time. Ignores InterruptedException from Thread.sleep. */ void sleep(int failureCount); /** * Sleep for the appropriate amount of time up to maxDelayMs. Ignores InterruptedException from Thread.sleep. */ void sleep(int failureCount, int maxDelayMs); /** * Simple implementation of several policies parsed from a string. */ public static class Standard implements BackoffPolicy { /** * Parse 'policy' as a comma separated list of between 1 and 3 items. The first argument is the name which * must be one of {@link Type}, the second is the max delay in ms (default 60000) and the third the base * delay in ms (default 1000). * * @throws IllegalArgumentException * @throws NumberFormatException */ public static BackoffPolicy parse(String policy) { String[] a = policy.split("[\\s]*,[\\s]*"); Type type = Type.valueOf(Type.class, a[0]); int max = a.length >= 2 ? Integer.parseInt(a[1]) : 60000; int base = a.length >= 3 ? Integer.parseInt(a[2]) : 1000; return new Standard(type, base, max); } enum Type { FIXED, LINEAR, EXPONENTIAL } private final Type type; private final int baseMs; private final int maxMs; public Standard(Type type, int baseMs, int maxMs) { this.type = type; this.baseMs = baseMs; this.maxMs = maxMs; } @Override public int getDelayMs(int failureCount) { if (failureCount < 1) failureCount = 1; int m; switch (type) { case FIXED: m = 1; break; case LINEAR: m = failureCount - 1; break; case EXPONENTIAL: m = 1 << (failureCount < 16 ? (failureCount - 1) : 15); break; default: throw new IllegalStateException("Unhandled backoff type " + type); } return Math.min(maxMs, baseMs * m); } @Override public int getMaxDelayMs() { return maxMs; } @Override public void sleep(int failureCount) { sleep(failureCount, Integer.MAX_VALUE); } @Override public void sleep(int failureCount, int maxDelayMs) { try { Thread.sleep(Math.min(getDelayMs(failureCount), maxDelayMs)); } catch (InterruptedException ignore) { } } } }