View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.client.backoff;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  import org.apache.hadoop.conf.Configuration;
23  import org.apache.hadoop.hbase.HConstants;
24  import org.apache.hadoop.hbase.ServerName;
25  import org.apache.hadoop.hbase.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.classification.InterfaceStability;
27  
28  import com.google.common.base.Preconditions;
29  
30  /**
31   * Simple exponential backoff policy on for the client that uses a  percent^4 times the
32   * max backoff to generate the backoff time.
33   */
34  @InterfaceAudience.Public
35  @InterfaceStability.Unstable
36  public class ExponentialClientBackoffPolicy implements ClientBackoffPolicy {
37  
38    private static final Log LOG = LogFactory.getLog(ExponentialClientBackoffPolicy.class);
39  
40    private static final long ONE_MINUTE = 60 * 1000;
41    public static final long DEFAULT_MAX_BACKOFF = 5 * ONE_MINUTE;
42    public static final String MAX_BACKOFF_KEY = "hbase.client.exponential-backoff.max";
43    private long maxBackoff;
44    private float heapOccupancyLowWatermark;
45    private float heapOccupancyHighWatermark;
46  
47    public ExponentialClientBackoffPolicy(Configuration conf) {
48      this.maxBackoff = conf.getLong(MAX_BACKOFF_KEY, DEFAULT_MAX_BACKOFF);
49      this.heapOccupancyLowWatermark = conf.getFloat(HConstants.HEAP_OCCUPANCY_LOW_WATERMARK_KEY,
50        HConstants.DEFAULT_HEAP_OCCUPANCY_LOW_WATERMARK);
51      this.heapOccupancyHighWatermark = conf.getFloat(HConstants.HEAP_OCCUPANCY_HIGH_WATERMARK_KEY,
52        HConstants.DEFAULT_HEAP_OCCUPANCY_HIGH_WATERMARK);
53    }
54  
55    @Override
56    public long getBackoffTime(ServerName serverName, byte[] region, ServerStatistics stats) {
57      // no stats for the server yet, so don't backoff
58      if (stats == null) {
59        return 0;
60      }
61  
62      ServerStatistics.RegionStatistics regionStats = stats.getStatsForRegion(region);
63      // no stats for the region yet - don't backoff
64      if (regionStats == null) {
65        return 0;
66      }
67  
68      // Factor in memstore load
69      double percent = regionStats.getMemstoreLoadPercent() / 100.0;
70  
71      // Factor in heap occupancy
72      float heapOccupancy = regionStats.getHeapOccupancyPercent() / 100.0f;
73  
74      // Factor in compaction pressure, 1.0 means heavy compaction pressure
75      float compactionPressure = regionStats.getCompactionPressure() / 100.0f;
76      if (heapOccupancy >= heapOccupancyLowWatermark) {
77        // If we are higher than the high watermark, we are already applying max
78        // backoff and cannot scale more (see scale() below)
79        if (heapOccupancy > heapOccupancyHighWatermark) {
80          heapOccupancy = heapOccupancyHighWatermark;
81        }
82        percent = Math.max(percent,
83            scale(heapOccupancy, heapOccupancyLowWatermark, heapOccupancyHighWatermark,
84                0.1, 1.0));
85      }
86      percent = Math.max(percent, compactionPressure);
87      // square the percent as a value less than 1. Closer we move to 100 percent,
88      // the percent moves to 1, but squaring causes the exponential curve
89      double multiplier = Math.pow(percent, 4.0);
90      if (multiplier > 1) {
91        multiplier = 1;
92      }
93      return (long) (multiplier * maxBackoff);
94    }
95  
96    /** Scale valueIn in the range [baseMin,baseMax] to the range [limitMin,limitMax] */
97    private static double scale(double valueIn, double baseMin, double baseMax, double limitMin,
98        double limitMax) {
99      Preconditions.checkArgument(baseMin <= baseMax, "Illegal source range [%s,%s]",
100         baseMin, baseMax);
101     Preconditions.checkArgument(limitMin <= limitMax, "Illegal target range [%s,%s]",
102         limitMin, limitMax);
103     Preconditions.checkArgument(valueIn >= baseMin && valueIn <= baseMax,
104         "Value %s must be within the range [%s,%s]", valueIn, baseMin, baseMax);
105     return ((limitMax - limitMin) * (valueIn - baseMin) / (baseMax - baseMin)) + limitMin;
106   }
107 }