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/** 028 * Operation retry accounting. 029 * Use to calculate wait period, {@link #getBackoffTimeAndIncrementAttempts()}}, or for performing 030 * wait, {@link #sleepUntilNextRetry()}, in accordance with a {@link RetryConfig}, initial 031 * settings, and a Retry Policy, (See org.apache.hadoop.io.retry.RetryPolicy). 032 * Like <a href=https://github.com/rholder/guava-retrying>guava-retrying</a>. 033 * @since 0.92.0 034 * @see RetryCounterFactory 035 */ 036@InterfaceAudience.Private 037public class RetryCounter { 038 /** 039 * Configuration for a retry counter 040 */ 041 public static class RetryConfig { 042 private int maxAttempts; 043 private long sleepInterval; 044 private long maxSleepTime; 045 private TimeUnit timeUnit; 046 private BackoffPolicy backoffPolicy; 047 048 private static final BackoffPolicy DEFAULT_BACKOFF_POLICY = new ExponentialBackoffPolicy(); 049 050 public RetryConfig() { 051 maxAttempts = 1; 052 sleepInterval = 1000; 053 maxSleepTime = -1; 054 timeUnit = TimeUnit.MILLISECONDS; 055 backoffPolicy = DEFAULT_BACKOFF_POLICY; 056 } 057 058 public RetryConfig(int maxAttempts, long sleepInterval, long maxSleepTime, 059 TimeUnit timeUnit, BackoffPolicy backoffPolicy) { 060 this.maxAttempts = maxAttempts; 061 this.sleepInterval = sleepInterval; 062 this.maxSleepTime = maxSleepTime; 063 this.timeUnit = timeUnit; 064 this.backoffPolicy = backoffPolicy; 065 } 066 067 public RetryConfig setBackoffPolicy(BackoffPolicy backoffPolicy) { 068 this.backoffPolicy = backoffPolicy; 069 return this; 070 } 071 072 public RetryConfig setMaxAttempts(int maxAttempts) { 073 this.maxAttempts = maxAttempts; 074 return this; 075 } 076 077 public RetryConfig setMaxSleepTime(long maxSleepTime) { 078 this.maxSleepTime = maxSleepTime; 079 return this; 080 } 081 082 public RetryConfig setSleepInterval(long sleepInterval) { 083 this.sleepInterval = sleepInterval; 084 return this; 085 } 086 087 public RetryConfig setTimeUnit(TimeUnit timeUnit) { 088 this.timeUnit = timeUnit; 089 return this; 090 } 091 092 public int getMaxAttempts() { 093 return maxAttempts; 094 } 095 096 public long getMaxSleepTime() { 097 return maxSleepTime; 098 } 099 100 public long getSleepInterval() { 101 return sleepInterval; 102 } 103 104 public TimeUnit getTimeUnit() { 105 return timeUnit; 106 } 107 108 public BackoffPolicy getBackoffPolicy() { 109 return backoffPolicy; 110 } 111 } 112 113 /** 114 * Policy for calculating sleeping intervals between retry attempts 115 */ 116 public static class BackoffPolicy { 117 public long getBackoffTime(RetryConfig config, int attempts) { 118 return config.getSleepInterval(); 119 } 120 } 121 122 public static class ExponentialBackoffPolicy extends BackoffPolicy { 123 @Override 124 public long getBackoffTime(RetryConfig config, int attempts) { 125 long backoffTime = (long) (config.getSleepInterval() * Math.pow(2, attempts)); 126 return backoffTime; 127 } 128 } 129 130 public static class ExponentialBackoffPolicyWithLimit extends ExponentialBackoffPolicy { 131 @Override 132 public long getBackoffTime(RetryConfig config, int attempts) { 133 long backoffTime = super.getBackoffTime(config, attempts); 134 return config.getMaxSleepTime() > 0 ? Math.min(backoffTime, config.getMaxSleepTime()) : backoffTime; 135 } 136 } 137 138 private static final Logger LOG = LoggerFactory.getLogger(RetryCounter.class); 139 140 private RetryConfig retryConfig; 141 private int attempts; 142 143 public RetryCounter(int maxAttempts, long sleepInterval, TimeUnit timeUnit) { 144 this(new RetryConfig(maxAttempts, sleepInterval, -1, timeUnit, new ExponentialBackoffPolicy())); 145 } 146 147 public RetryCounter(RetryConfig retryConfig) { 148 this.attempts = 0; 149 this.retryConfig = retryConfig; 150 } 151 152 public int getMaxAttempts() { 153 return retryConfig.getMaxAttempts(); 154 } 155 156 /** 157 * Sleep for a back off time as supplied by the backoff policy, and increases the attempts 158 * @throws InterruptedException 159 */ 160 public void sleepUntilNextRetry() throws InterruptedException { 161 int attempts = getAttemptTimes(); 162 long sleepTime = getBackoffTime(); 163 LOG.trace("Sleeping {} ms before retry #{}...", sleepTime, attempts); 164 retryConfig.getTimeUnit().sleep(sleepTime); 165 useRetry(); 166 } 167 168 public boolean shouldRetry() { 169 return attempts < retryConfig.getMaxAttempts(); 170 } 171 172 public void useRetry() { 173 attempts++; 174 } 175 176 public boolean isRetry() { 177 return attempts > 0; 178 } 179 180 public int getAttemptTimes() { 181 return attempts; 182 } 183 184 public long getBackoffTime() { 185 return this.retryConfig.backoffPolicy.getBackoffTime(this.retryConfig, getAttemptTimes()); 186 } 187 188 public long getBackoffTimeAndIncrementAttempts() { 189 long backoffTime = getBackoffTime(); 190 useRetry(); 191 return backoffTime; 192 } 193}