1 /** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 package org.apache.hadoop.hbase.util; 20 21 import java.util.concurrent.TimeUnit; 22 23 import org.apache.commons.logging.Log; 24 import org.apache.commons.logging.LogFactory; 25 import org.apache.hadoop.hbase.classification.InterfaceAudience; 26 27 @InterfaceAudience.Private 28 public class RetryCounter { 29 30 /** 31 * Configuration for a retry counter 32 */ 33 public static class RetryConfig { 34 private int maxAttempts; 35 private long sleepInterval; 36 private long maxSleepTime; 37 private TimeUnit timeUnit; 38 private BackoffPolicy backoffPolicy; 39 40 private static final BackoffPolicy DEFAULT_BACKOFF_POLICY = new ExponentialBackoffPolicy(); 41 RetryConfig()42 public RetryConfig() { 43 maxAttempts = 1; 44 sleepInterval = 1000; 45 maxSleepTime = -1; 46 timeUnit = TimeUnit.MILLISECONDS; 47 backoffPolicy = DEFAULT_BACKOFF_POLICY; 48 } 49 RetryConfig(int maxAttempts, long sleepInterval, long maxSleepTime, TimeUnit timeUnit, BackoffPolicy backoffPolicy)50 public RetryConfig(int maxAttempts, long sleepInterval, long maxSleepTime, 51 TimeUnit timeUnit, BackoffPolicy backoffPolicy) { 52 this.maxAttempts = maxAttempts; 53 this.sleepInterval = sleepInterval; 54 this.maxSleepTime = maxSleepTime; 55 this.timeUnit = timeUnit; 56 this.backoffPolicy = backoffPolicy; 57 } 58 setBackoffPolicy(BackoffPolicy backoffPolicy)59 public RetryConfig setBackoffPolicy(BackoffPolicy backoffPolicy) { 60 this.backoffPolicy = backoffPolicy; 61 return this; 62 } 63 setMaxAttempts(int maxAttempts)64 public RetryConfig setMaxAttempts(int maxAttempts) { 65 this.maxAttempts = maxAttempts; 66 return this; 67 } 68 setMaxSleepTime(long maxSleepTime)69 public RetryConfig setMaxSleepTime(long maxSleepTime) { 70 this.maxSleepTime = maxSleepTime; 71 return this; 72 } 73 setSleepInterval(long sleepInterval)74 public RetryConfig setSleepInterval(long sleepInterval) { 75 this.sleepInterval = sleepInterval; 76 return this; 77 } 78 setTimeUnit(TimeUnit timeUnit)79 public RetryConfig setTimeUnit(TimeUnit timeUnit) { 80 this.timeUnit = timeUnit; 81 return this; 82 } 83 getMaxAttempts()84 public int getMaxAttempts() { 85 return maxAttempts; 86 } 87 getMaxSleepTime()88 public long getMaxSleepTime() { 89 return maxSleepTime; 90 } 91 getSleepInterval()92 public long getSleepInterval() { 93 return sleepInterval; 94 } 95 getTimeUnit()96 public TimeUnit getTimeUnit() { 97 return timeUnit; 98 } 99 getBackoffPolicy()100 public BackoffPolicy getBackoffPolicy() { 101 return backoffPolicy; 102 } 103 } 104 105 /** 106 * Policy for calculating sleeping intervals between retry attempts 107 */ 108 public static class BackoffPolicy { getBackoffTime(RetryConfig config, int attempts)109 public long getBackoffTime(RetryConfig config, int attempts) { 110 return config.getSleepInterval(); 111 } 112 } 113 114 public static class ExponentialBackoffPolicy extends BackoffPolicy { 115 @Override getBackoffTime(RetryConfig config, int attempts)116 public long getBackoffTime(RetryConfig config, int attempts) { 117 long backoffTime = (long) (config.getSleepInterval() * Math.pow(2, attempts)); 118 return backoffTime; 119 } 120 } 121 122 public static class ExponentialBackoffPolicyWithLimit extends ExponentialBackoffPolicy { 123 @Override getBackoffTime(RetryConfig config, int attempts)124 public long getBackoffTime(RetryConfig config, int attempts) { 125 long backoffTime = super.getBackoffTime(config, attempts); 126 return config.getMaxSleepTime() > 0 ? Math.min(backoffTime, config.getMaxSleepTime()) : backoffTime; 127 } 128 } 129 130 private static final Log LOG = LogFactory.getLog(RetryCounter.class); 131 132 private RetryConfig retryConfig; 133 private int attempts; 134 RetryCounter(int maxAttempts, long sleepInterval, TimeUnit timeUnit)135 public RetryCounter(int maxAttempts, long sleepInterval, TimeUnit timeUnit) { 136 this(new RetryConfig(maxAttempts, sleepInterval, -1, timeUnit, new ExponentialBackoffPolicy())); 137 } 138 RetryCounter(RetryConfig retryConfig)139 public RetryCounter(RetryConfig retryConfig) { 140 this.attempts = 0; 141 this.retryConfig = retryConfig; 142 } 143 getMaxAttempts()144 public int getMaxAttempts() { 145 return retryConfig.getMaxAttempts(); 146 } 147 148 /** 149 * Sleep for a back off time as supplied by the backoff policy, and increases the attempts 150 * @throws InterruptedException 151 */ sleepUntilNextRetry()152 public void sleepUntilNextRetry() throws InterruptedException { 153 int attempts = getAttemptTimes(); 154 long sleepTime = retryConfig.backoffPolicy.getBackoffTime(retryConfig, attempts); 155 if (LOG.isTraceEnabled()) { 156 LOG.trace("Sleeping " + sleepTime + "ms before retry #" + attempts + "..."); 157 } 158 retryConfig.getTimeUnit().sleep(sleepTime); 159 useRetry(); 160 } 161 shouldRetry()162 public boolean shouldRetry() { 163 return attempts < retryConfig.getMaxAttempts(); 164 } 165 useRetry()166 public void useRetry() { 167 attempts++; 168 } 169 isRetry()170 public boolean isRetry() { 171 return attempts > 0; 172 } 173 getAttemptTimes()174 public int getAttemptTimes() { 175 return attempts; 176 } 177 } 178