001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.util; 020 021import java.util.concurrent.TimeUnit; 022 023import org.apache.yetus.audience.InterfaceAudience; 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026 027@InterfaceAudience.Private 028public class RetryCounter { 029 030 /** 031 * Configuration for a retry counter 032 */ 033 public static class RetryConfig { 034 private int maxAttempts; 035 private long sleepInterval; 036 private long maxSleepTime; 037 private TimeUnit timeUnit; 038 private BackoffPolicy backoffPolicy; 039 040 private static final BackoffPolicy DEFAULT_BACKOFF_POLICY = new ExponentialBackoffPolicy(); 041 042 public RetryConfig() { 043 maxAttempts = 1; 044 sleepInterval = 1000; 045 maxSleepTime = -1; 046 timeUnit = TimeUnit.MILLISECONDS; 047 backoffPolicy = DEFAULT_BACKOFF_POLICY; 048 } 049 050 public RetryConfig(int maxAttempts, long sleepInterval, long maxSleepTime, 051 TimeUnit timeUnit, BackoffPolicy backoffPolicy) { 052 this.maxAttempts = maxAttempts; 053 this.sleepInterval = sleepInterval; 054 this.maxSleepTime = maxSleepTime; 055 this.timeUnit = timeUnit; 056 this.backoffPolicy = backoffPolicy; 057 } 058 059 public RetryConfig setBackoffPolicy(BackoffPolicy backoffPolicy) { 060 this.backoffPolicy = backoffPolicy; 061 return this; 062 } 063 064 public RetryConfig setMaxAttempts(int maxAttempts) { 065 this.maxAttempts = maxAttempts; 066 return this; 067 } 068 069 public RetryConfig setMaxSleepTime(long maxSleepTime) { 070 this.maxSleepTime = maxSleepTime; 071 return this; 072 } 073 074 public RetryConfig setSleepInterval(long sleepInterval) { 075 this.sleepInterval = sleepInterval; 076 return this; 077 } 078 079 public RetryConfig setTimeUnit(TimeUnit timeUnit) { 080 this.timeUnit = timeUnit; 081 return this; 082 } 083 084 public int getMaxAttempts() { 085 return maxAttempts; 086 } 087 088 public long getMaxSleepTime() { 089 return maxSleepTime; 090 } 091 092 public long getSleepInterval() { 093 return sleepInterval; 094 } 095 096 public TimeUnit getTimeUnit() { 097 return timeUnit; 098 } 099 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 { 109 public long getBackoffTime(RetryConfig config, int attempts) { 110 return config.getSleepInterval(); 111 } 112 } 113 114 public static class ExponentialBackoffPolicy extends BackoffPolicy { 115 @Override 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 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 Logger LOG = LoggerFactory.getLogger(RetryCounter.class); 131 132 private RetryConfig retryConfig; 133 private int attempts; 134 135 public RetryCounter(int maxAttempts, long sleepInterval, TimeUnit timeUnit) { 136 this(new RetryConfig(maxAttempts, sleepInterval, -1, timeUnit, new ExponentialBackoffPolicy())); 137 } 138 139 public RetryCounter(RetryConfig retryConfig) { 140 this.attempts = 0; 141 this.retryConfig = retryConfig; 142 } 143 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 */ 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 162 public boolean shouldRetry() { 163 return attempts < retryConfig.getMaxAttempts(); 164 } 165 166 public void useRetry() { 167 attempts++; 168 } 169 170 public boolean isRetry() { 171 return attempts > 0; 172 } 173 174 public int getAttemptTimes() { 175 return attempts; 176 } 177}