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}