001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertTrue;
022
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.hbase.HBaseClassTestRule;
025import org.apache.hadoop.hbase.ServerName;
026import org.apache.hadoop.hbase.client.backoff.ExponentialClientBackoffPolicy;
027import org.apache.hadoop.hbase.client.backoff.ServerStatistics;
028import org.apache.hadoop.hbase.testclassification.ClientTests;
029import org.apache.hadoop.hbase.testclassification.SmallTests;
030import org.apache.hadoop.hbase.util.Bytes;
031import org.junit.ClassRule;
032import org.junit.Test;
033import org.junit.experimental.categories.Category;
034import org.mockito.Mockito;
035
036import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
037import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos;
038
039@Category({ClientTests.class, SmallTests.class})
040public class TestClientExponentialBackoff {
041  @ClassRule
042  public static final HBaseClassTestRule CLASS_RULE =
043      HBaseClassTestRule.forClass(TestClientExponentialBackoff.class);
044
045  ServerName server = Mockito.mock(ServerName.class);
046  byte[] regionname = Bytes.toBytes("region");
047
048  @Test
049  public void testNulls() {
050    Configuration conf = new Configuration(false);
051    ExponentialClientBackoffPolicy backoff = new ExponentialClientBackoffPolicy(conf);
052    assertEquals(0, backoff.getBackoffTime(null, null, null));
053
054    // server name doesn't matter to calculation, but check it now anyways
055    assertEquals(0, backoff.getBackoffTime(server, null, null));
056    assertEquals(0, backoff.getBackoffTime(server, regionname, null));
057
058    // check when no stats for the region yet
059    ServerStatistics stats = new ServerStatistics();
060    assertEquals(0, backoff.getBackoffTime(server, regionname, stats));
061  }
062
063  @Test
064  public void testMaxLoad() {
065    Configuration conf = new Configuration(false);
066    ExponentialClientBackoffPolicy backoff = new ExponentialClientBackoffPolicy(conf);
067
068    ServerStatistics stats = new ServerStatistics();
069    update(stats, 100);
070    assertEquals(ExponentialClientBackoffPolicy.DEFAULT_MAX_BACKOFF, backoff.getBackoffTime(server,
071        regionname, stats));
072
073    // another policy with a different max timeout
074    long max = 100;
075    conf.setLong(ExponentialClientBackoffPolicy.MAX_BACKOFF_KEY, max);
076    ExponentialClientBackoffPolicy backoffShortTimeout = new ExponentialClientBackoffPolicy(conf);
077    assertEquals(max, backoffShortTimeout.getBackoffTime(server, regionname, stats));
078
079    // test beyond 100 still doesn't exceed the max
080    update(stats, 101);
081    assertEquals(ExponentialClientBackoffPolicy.DEFAULT_MAX_BACKOFF, backoff.getBackoffTime(server,
082        regionname, stats));
083    assertEquals(max, backoffShortTimeout.getBackoffTime(server, regionname, stats));
084
085    // and that when we are below 100, its less than the max timeout
086    update(stats, 99);
087    assertTrue(backoff.getBackoffTime(server,
088        regionname, stats) < ExponentialClientBackoffPolicy.DEFAULT_MAX_BACKOFF);
089    assertTrue(backoffShortTimeout.getBackoffTime(server, regionname, stats) < max);
090  }
091
092  /**
093   * Make sure that we get results in the order that we expect - backoff for a load of 1 should
094   * less than backoff for 10, which should be less than that for 50.
095   */
096  @Test
097  public void testResultOrdering() {
098    Configuration conf = new Configuration(false);
099    // make the max timeout really high so we get differentiation between load factors
100    conf.setLong(ExponentialClientBackoffPolicy.MAX_BACKOFF_KEY, Integer.MAX_VALUE);
101    ExponentialClientBackoffPolicy backoff = new ExponentialClientBackoffPolicy(conf);
102
103    ServerStatistics stats = new ServerStatistics();
104    long previous = backoff.getBackoffTime(server, regionname, stats);
105    for (int i = 1; i <= 100; i++) {
106      update(stats, i);
107      long next = backoff.getBackoffTime(server, regionname, stats);
108      assertTrue(
109          "Previous backoff time" + previous + " >= " + next + ", the next backoff time for " +
110              "load " + i, previous < next);
111      previous = next;
112    }
113  }
114
115  @Test
116  public void testHeapOccupancyPolicy() {
117    Configuration conf = new Configuration(false);
118    ExponentialClientBackoffPolicy backoff = new ExponentialClientBackoffPolicy(conf);
119
120    ServerStatistics stats = new ServerStatistics();
121    long backoffTime;
122
123    update(stats, 0, 95, 0);
124    backoffTime = backoff.getBackoffTime(server, regionname, stats);
125    assertTrue("Heap occupancy at low watermark had no effect", backoffTime > 0);
126
127    long previous = backoffTime;
128    update(stats, 0, 96, 0);
129    backoffTime = backoff.getBackoffTime(server, regionname, stats);
130    assertTrue("Increase above low watermark should have increased backoff",
131      backoffTime > previous);
132
133    update(stats, 0, 98, 0);
134    backoffTime = backoff.getBackoffTime(server, regionname, stats);
135    assertEquals("We should be using max backoff when at high watermark",
136      ExponentialClientBackoffPolicy.DEFAULT_MAX_BACKOFF, backoffTime);
137  }
138
139  @Test
140  public void testCompactionPressurePolicy() {
141    Configuration conf = new Configuration(false);
142    ExponentialClientBackoffPolicy backoff = new ExponentialClientBackoffPolicy(conf);
143
144    ServerStatistics stats = new ServerStatistics();
145    long backoffTime;
146
147    update(stats, 0, 0, 0);
148    backoffTime = backoff.getBackoffTime(server, regionname, stats);
149    assertTrue("Compaction pressure has no effect", backoffTime == 0);
150
151    long previous = backoffTime;
152    update(stats, 0, 0, 50);
153    backoffTime = backoff.getBackoffTime(server, regionname, stats);
154    assertTrue("Compaction pressure should be bigger",
155            backoffTime > previous);
156
157    update(stats, 0, 0, 100);
158    backoffTime = backoff.getBackoffTime(server, regionname, stats);
159    assertEquals("under heavy compaction pressure",
160      ExponentialClientBackoffPolicy.DEFAULT_MAX_BACKOFF, backoffTime);
161  }
162
163  private void update(ServerStatistics stats, int load) {
164    ClientProtos.RegionLoadStats stat = ClientProtos.RegionLoadStats.newBuilder()
165        .setMemStoreLoad(load).build();
166    stats.update(regionname, ProtobufUtil.createRegionLoadStats(stat));
167  }
168
169  private void update(ServerStatistics stats, int memstoreLoad, int heapOccupancy,
170                      int compactionPressure) {
171    ClientProtos.RegionLoadStats stat = ClientProtos.RegionLoadStats.newBuilder()
172        .setMemStoreLoad(memstoreLoad)
173        .setHeapOccupancy(heapOccupancy)
174        .setCompactionPressure(compactionPressure)
175            .build();
176    stats.update(regionname, ProtobufUtil.createRegionLoadStats(stat));
177  }
178}