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
042  @ClassRule
043  public static final HBaseClassTestRule CLASS_RULE =
044      HBaseClassTestRule.forClass(TestClientExponentialBackoff.class);
045
046  ServerName server = Mockito.mock(ServerName.class);
047  byte[] regionname = Bytes.toBytes("region");
048
049  @Test
050  public void testNulls() {
051    Configuration conf = new Configuration(false);
052    ExponentialClientBackoffPolicy backoff = new ExponentialClientBackoffPolicy(conf);
053    assertEquals(0, backoff.getBackoffTime(null, null, null));
054
055    // server name doesn't matter to calculation, but check it now anyways
056    assertEquals(0, backoff.getBackoffTime(server, null, null));
057    assertEquals(0, backoff.getBackoffTime(server, regionname, null));
058
059    // check when no stats for the region yet
060    ServerStatistics stats = new ServerStatistics();
061    assertEquals(0, backoff.getBackoffTime(server, regionname, stats));
062  }
063
064  @Test
065  public void testMaxLoad() {
066    Configuration conf = new Configuration(false);
067    ExponentialClientBackoffPolicy backoff = new ExponentialClientBackoffPolicy(conf);
068
069    ServerStatistics stats = new ServerStatistics();
070    update(stats, 100);
071    assertEquals(ExponentialClientBackoffPolicy.DEFAULT_MAX_BACKOFF, backoff.getBackoffTime(server,
072        regionname, stats));
073
074    // another policy with a different max timeout
075    long max = 100;
076    conf.setLong(ExponentialClientBackoffPolicy.MAX_BACKOFF_KEY, max);
077    ExponentialClientBackoffPolicy backoffShortTimeout = new ExponentialClientBackoffPolicy(conf);
078    assertEquals(max, backoffShortTimeout.getBackoffTime(server, regionname, stats));
079
080    // test beyond 100 still doesn't exceed the max
081    update(stats, 101);
082    assertEquals(ExponentialClientBackoffPolicy.DEFAULT_MAX_BACKOFF, backoff.getBackoffTime(server,
083        regionname, stats));
084    assertEquals(max, backoffShortTimeout.getBackoffTime(server, regionname, stats));
085
086    // and that when we are below 100, its less than the max timeout
087    update(stats, 99);
088    assertTrue(backoff.getBackoffTime(server,
089        regionname, stats) < ExponentialClientBackoffPolicy.DEFAULT_MAX_BACKOFF);
090    assertTrue(backoffShortTimeout.getBackoffTime(server, regionname, stats) < max);
091  }
092
093  /**
094   * Make sure that we get results in the order that we expect - backoff for a load of 1 should
095   * less than backoff for 10, which should be less than that for 50.
096   */
097  @Test
098  public void testResultOrdering() {
099    Configuration conf = new Configuration(false);
100    // make the max timeout really high so we get differentiation between load factors
101    conf.setLong(ExponentialClientBackoffPolicy.MAX_BACKOFF_KEY, Integer.MAX_VALUE);
102    ExponentialClientBackoffPolicy backoff = new ExponentialClientBackoffPolicy(conf);
103
104    ServerStatistics stats = new ServerStatistics();
105    long previous = backoff.getBackoffTime(server, regionname, stats);
106    for (int i = 1; i <= 100; i++) {
107      update(stats, i);
108      long next = backoff.getBackoffTime(server, regionname, stats);
109      assertTrue(
110          "Previous backoff time" + previous + " >= " + next + ", the next backoff time for " +
111              "load " + i, previous < next);
112      previous = next;
113    }
114  }
115
116  @Test
117  public void testHeapOccupancyPolicy() {
118    Configuration conf = new Configuration(false);
119    ExponentialClientBackoffPolicy backoff = new ExponentialClientBackoffPolicy(conf);
120
121    ServerStatistics stats = new ServerStatistics();
122    long backoffTime;
123
124    update(stats, 0, 95, 0);
125    backoffTime = backoff.getBackoffTime(server, regionname, stats);
126    assertTrue("Heap occupancy at low watermark had no effect", backoffTime > 0);
127
128    long previous = backoffTime;
129    update(stats, 0, 96, 0);
130    backoffTime = backoff.getBackoffTime(server, regionname, stats);
131    assertTrue("Increase above low watermark should have increased backoff",
132      backoffTime > previous);
133
134    update(stats, 0, 98, 0);
135    backoffTime = backoff.getBackoffTime(server, regionname, stats);
136    assertEquals("We should be using max backoff when at high watermark",
137      ExponentialClientBackoffPolicy.DEFAULT_MAX_BACKOFF, backoffTime);
138  }
139
140  @Test
141  public void testCompactionPressurePolicy() {
142    Configuration conf = new Configuration(false);
143    ExponentialClientBackoffPolicy backoff = new ExponentialClientBackoffPolicy(conf);
144
145    ServerStatistics stats = new ServerStatistics();
146    long backoffTime;
147
148    update(stats, 0, 0, 0);
149    backoffTime = backoff.getBackoffTime(server, regionname, stats);
150    assertTrue("Compaction pressure has no effect", backoffTime == 0);
151
152        long previous = backoffTime;
153    update(stats, 0, 0, 50);
154    backoffTime = backoff.getBackoffTime(server, regionname, stats);
155    assertTrue("Compaction pressure should be bigger",
156            backoffTime > previous);
157
158    update(stats, 0, 0, 100);
159    backoffTime = backoff.getBackoffTime(server, regionname, stats);
160    assertEquals("under heavy compaction pressure",
161      ExponentialClientBackoffPolicy.DEFAULT_MAX_BACKOFF, backoffTime);
162  }
163
164  private void update(ServerStatistics stats, int load) {
165    ClientProtos.RegionLoadStats stat = ClientProtos.RegionLoadStats.newBuilder()
166        .setMemStoreLoad
167            (load).build();
168    stats.update(regionname, ProtobufUtil.createRegionLoadStats(stat));
169  }
170
171  private void update(ServerStatistics stats, int memstoreLoad, int heapOccupancy,
172                      int compactionPressure) {
173    ClientProtos.RegionLoadStats stat = ClientProtos.RegionLoadStats.newBuilder()
174        .setMemStoreLoad(memstoreLoad)
175        .setHeapOccupancy(heapOccupancy)
176        .setCompactionPressure(compactionPressure)
177            .build();
178    stats.update(regionname, ProtobufUtil.createRegionLoadStats(stat));
179  }
180}