View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.regionserver;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertFalse;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.fail;
25  
26  import java.lang.management.ManagementFactory;
27  import java.util.Iterator;
28  
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.hbase.ChoreService;
31  import org.apache.hadoop.hbase.CoordinatedStateManager;
32  import org.apache.hadoop.hbase.HBaseConfiguration;
33  import org.apache.hadoop.hbase.HBaseTestingUtility;
34  import org.apache.hadoop.hbase.HConstants;
35  import org.apache.hadoop.hbase.Server;
36  import org.apache.hadoop.hbase.ServerName;
37  import org.apache.hadoop.hbase.testclassification.SmallTests;
38  import org.apache.hadoop.hbase.Waiter;
39  import org.apache.hadoop.hbase.client.ClusterConnection;
40  import org.apache.hadoop.hbase.io.hfile.BlockCache;
41  import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
42  import org.apache.hadoop.hbase.io.hfile.CacheStats;
43  import org.apache.hadoop.hbase.io.hfile.Cacheable;
44  import org.apache.hadoop.hbase.io.hfile.CachedBlock;
45  import org.apache.hadoop.hbase.io.hfile.ResizableBlockCache;
46  import org.apache.hadoop.hbase.io.util.HeapMemorySizeUtil;
47  import org.apache.hadoop.hbase.regionserver.HeapMemoryManager.TunerContext;
48  import org.apache.hadoop.hbase.regionserver.HeapMemoryManager.TunerResult;
49  import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
50  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
51  import org.junit.Test;
52  import org.junit.experimental.categories.Category;
53  
54  @Category(SmallTests.class)
55  public class TestHeapMemoryManager {
56  
57    private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
58  
59    private long maxHeapSize = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax();
60  
61    @Test
62    public void testAutoTunerShouldBeOffWhenMaxMinRangesForMemstoreIsNotGiven() throws Exception {
63      Configuration conf = HBaseConfiguration.create();
64      conf.setFloat(HeapMemorySizeUtil.MEMSTORE_SIZE_KEY, 0.02f);
65      conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.75f);
66      conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.03f);
67      HeapMemoryManager manager = new HeapMemoryManager(new BlockCacheStub(0),
68          new MemstoreFlusherStub(0), new RegionServerStub(conf), new RegionServerAccountingStub());
69      assertFalse(manager.isTunerOn());
70    }
71  
72    @Test
73    public void testAutoTunerShouldBeOffWhenMaxMinRangesForBlockCacheIsNotGiven() throws Exception {
74      Configuration conf = HBaseConfiguration.create();
75      conf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0.02f);
76      conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
77      conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.03f);
78      HeapMemoryManager manager = new HeapMemoryManager(new BlockCacheStub(0),
79          new MemstoreFlusherStub(0), new RegionServerStub(conf), new RegionServerAccountingStub());
80      assertFalse(manager.isTunerOn());
81    }
82  
83    @Test
84    public void testWhenMemstoreAndBlockCacheMaxMinChecksFails() throws Exception {
85      BlockCacheStub blockCache = new BlockCacheStub(0);
86      MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub(0);
87      Configuration conf = HBaseConfiguration.create();
88      conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
89      conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.06f);
90      try {
91        new HeapMemoryManager(blockCache, memStoreFlusher,
92            new RegionServerStub(conf), new RegionServerAccountingStub());
93        fail();
94      } catch (RuntimeException e) {
95      }
96      conf = HBaseConfiguration.create();
97      conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.2f);
98      conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
99      try {
100       new HeapMemoryManager(blockCache, memStoreFlusher,
101           new RegionServerStub(conf), new RegionServerAccountingStub());
102       fail();
103     } catch (RuntimeException e) {
104     }
105   }
106 
107   @Test
108   public void testWhenClusterIsWriteHeavyWithEmptyMemstore() throws Exception {
109     BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
110     MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
111     RegionServerAccountingStub regionServerAccounting = new RegionServerAccountingStub();
112     // Empty block cache and memstore
113     blockCache.setTestBlockSize(0);
114     regionServerAccounting.setTestMemstoreSize(0);
115     Configuration conf = HBaseConfiguration.create();
116     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
117     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.10f);
118     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
119     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.05f);
120     conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
121     conf.setInt(DefaultHeapMemoryTuner.NUM_PERIODS_TO_IGNORE, 0);
122     // Let the system start with default values for memstore heap and block cache size.
123     HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
124         new RegionServerStub(conf), regionServerAccounting);
125     long oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
126     long oldBlockCacheSize = blockCache.maxSize;
127     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
128     heapMemoryManager.start(choreService);
129     memStoreFlusher.flushType = FlushType.ABOVE_HIGHER_MARK;
130     memStoreFlusher.requestFlush(null, false);
131     memStoreFlusher.requestFlush(null, false);
132     memStoreFlusher.requestFlush(null, false);
133     memStoreFlusher.flushType = FlushType.ABOVE_LOWER_MARK;
134     memStoreFlusher.requestFlush(null, false);
135     // Allow the tuner to run once and do necessary memory up
136     Thread.sleep(1500);
137     // No changes should be made by tuner as we already have lot of empty space
138     assertEquals(oldMemstoreHeapSize, memStoreFlusher.memstoreSize);
139     assertEquals(oldBlockCacheSize, blockCache.maxSize);
140   }
141 
142   @Test
143   public void testWhenClusterIsReadHeavyWithEmptyBlockCache() throws Exception {
144     BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
145     MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
146     RegionServerAccountingStub regionServerAccounting = new RegionServerAccountingStub();
147     // Empty block cache and memstore
148     blockCache.setTestBlockSize(0);
149     regionServerAccounting.setTestMemstoreSize(0);
150     Configuration conf = HBaseConfiguration.create();
151     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
152     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.10f);
153     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
154     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.05f);
155     conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
156     conf.setInt(DefaultHeapMemoryTuner.NUM_PERIODS_TO_IGNORE, 0);
157     // Let the system start with default values for memstore heap and block cache size.
158     HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
159         new RegionServerStub(conf), regionServerAccounting);
160     long oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
161     long oldBlockCacheSize = blockCache.maxSize;
162     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
163     heapMemoryManager.start(choreService);
164     blockCache.evictBlock(null);
165     blockCache.evictBlock(null);
166     blockCache.evictBlock(null);
167     // Allow the tuner to run once and do necessary memory up
168     Thread.sleep(1500);
169     // No changes should be made by tuner as we already have lot of empty space
170     assertEquals(oldMemstoreHeapSize, memStoreFlusher.memstoreSize);
171     assertEquals(oldBlockCacheSize, blockCache.maxSize);
172   }
173 
174   @Test
175   public void testWhenClusterIsWriteHeavy() throws Exception {
176     BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
177     MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
178     RegionServerAccountingStub regionServerAccounting = new RegionServerAccountingStub();
179     // Empty block cache and but nearly filled memstore
180     blockCache.setTestBlockSize(0);
181     regionServerAccounting.setTestMemstoreSize((long) (maxHeapSize * 0.4 * 0.8));
182     Configuration conf = HBaseConfiguration.create();
183     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
184     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.10f);
185     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
186     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.05f);
187     conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
188     conf.setInt(DefaultHeapMemoryTuner.NUM_PERIODS_TO_IGNORE, 0);
189     // Let the system start with default values for memstore heap and block cache size.
190     HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
191         new RegionServerStub(conf), regionServerAccounting);
192     long oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
193     long oldBlockCacheSize = blockCache.maxSize;
194     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
195     heapMemoryManager.start(choreService);
196     memStoreFlusher.flushType = FlushType.ABOVE_LOWER_MARK;
197     memStoreFlusher.requestFlush(null, false);
198     memStoreFlusher.requestFlush(null, false);
199     memStoreFlusher.requestFlush(null, false);
200     memStoreFlusher.requestFlush(null, false);
201     // Allow the tuner to run once and do necessary memory up
202     waitForTune(memStoreFlusher, memStoreFlusher.memstoreSize);
203     assertHeapSpaceDelta(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE, oldMemstoreHeapSize,
204         memStoreFlusher.memstoreSize);
205     assertHeapSpaceDelta(-(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE), oldBlockCacheSize,
206         blockCache.maxSize);
207     oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
208     oldBlockCacheSize = blockCache.maxSize;
209     // Do some more flushes before the next run of HeapMemoryTuner
210     memStoreFlusher.flushType = FlushType.ABOVE_LOWER_MARK;
211     memStoreFlusher.requestFlush(null, false);
212     memStoreFlusher.requestFlush(null, false);
213     // Allow the tuner to run once and do necessary memory up
214     waitForTune(memStoreFlusher, memStoreFlusher.memstoreSize);
215     assertHeapSpaceDelta(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE, oldMemstoreHeapSize,
216         memStoreFlusher.memstoreSize);
217     assertHeapSpaceDelta(-(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE), oldBlockCacheSize,
218         blockCache.maxSize);
219   }
220 
221   @Test
222   public void testWhenClusterIsReadHeavy() throws Exception {
223     BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
224     MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
225     RegionServerAccountingStub regionServerAccounting = new RegionServerAccountingStub();
226     // Empty memstore and but nearly filled block cache
227     blockCache.setTestBlockSize((long) (maxHeapSize * 0.4 * 0.8));
228     regionServerAccounting.setTestMemstoreSize(0);
229     Configuration conf = HBaseConfiguration.create();
230     conf.setFloat(HeapMemorySizeUtil.MEMSTORE_SIZE_LOWER_LIMIT_KEY, 0.7f);
231     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
232     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.10f);
233     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
234     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.05f);
235     conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
236     conf.setInt(DefaultHeapMemoryTuner.NUM_PERIODS_TO_IGNORE, 0);
237     // Let the system start with default values for memstore heap and block cache size.
238     HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
239         new RegionServerStub(conf), new RegionServerAccountingStub());
240     long oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
241     long oldBlockCacheSize = blockCache.maxSize;
242     long oldMemstoreLowerMarkSize = 7 * oldMemstoreHeapSize / 10;
243     long maxTuneSize = oldMemstoreHeapSize -  (oldMemstoreLowerMarkSize + oldMemstoreHeapSize) / 2;
244     float maxStepValue = (maxTuneSize * 1.0f) / oldMemstoreHeapSize;
245     maxStepValue = maxStepValue > DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE ?
246         DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE:maxStepValue;
247     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
248     heapMemoryManager.start(choreService);
249     blockCache.evictBlock(null);
250     blockCache.evictBlock(null);
251     blockCache.evictBlock(null);
252     // Allow the tuner to run once and do necessary memory up
253     waitForTune(memStoreFlusher, memStoreFlusher.memstoreSize);
254     assertHeapSpaceDelta(-maxStepValue, oldMemstoreHeapSize, memStoreFlusher.memstoreSize);
255     assertHeapSpaceDelta(maxStepValue, oldBlockCacheSize, blockCache.maxSize);
256     oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
257     oldBlockCacheSize = blockCache.maxSize;
258     oldMemstoreLowerMarkSize = 7 * oldMemstoreHeapSize / 10;
259     maxTuneSize = oldMemstoreHeapSize -  (oldMemstoreLowerMarkSize + oldMemstoreHeapSize) / 2;
260     maxStepValue = (maxTuneSize * 1.0f) / oldMemstoreHeapSize;
261     maxStepValue = maxStepValue > DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE ?
262         DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE:maxStepValue;
263     // Do some more evictions before the next run of HeapMemoryTuner
264     blockCache.evictBlock(null);
265     // Allow the tuner to run once and do necessary memory up
266     waitForTune(memStoreFlusher, memStoreFlusher.memstoreSize);
267     assertHeapSpaceDelta(-maxStepValue, oldMemstoreHeapSize, memStoreFlusher.memstoreSize);
268     assertHeapSpaceDelta(maxStepValue, oldBlockCacheSize, blockCache.maxSize);
269   }
270 
271   @Test
272   public void testWhenClusterIsHavingMoreWritesThanReads() throws Exception {
273     BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
274     MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
275     RegionServerAccountingStub regionServerAccounting = new RegionServerAccountingStub();
276     // Both memstore and block cache are nearly filled
277     blockCache.setTestBlockSize(0);
278     regionServerAccounting.setTestMemstoreSize((long) (maxHeapSize * 0.4 * 0.8));
279     blockCache.setTestBlockSize((long) (maxHeapSize * 0.4 * 0.8));
280     Configuration conf = HBaseConfiguration.create();
281     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
282     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.10f);
283     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
284     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.05f);
285     conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
286     conf.setInt(DefaultHeapMemoryTuner.NUM_PERIODS_TO_IGNORE, 0);
287     // Let the system start with default values for memstore heap and block cache size.
288     HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
289         new RegionServerStub(conf), regionServerAccounting);
290     long oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
291     long oldBlockCacheSize = blockCache.maxSize;
292     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
293     heapMemoryManager.start(choreService);
294     memStoreFlusher.flushType = FlushType.ABOVE_LOWER_MARK;
295     memStoreFlusher.requestFlush(null, false);
296     memStoreFlusher.requestFlush(null, false);
297     memStoreFlusher.requestFlush(null, false);
298     blockCache.evictBlock(null);
299     // Allow the tuner to run once and do necessary memory up
300     Thread.sleep(1500);
301     // No changes should happen as there is undefined increase in flushes and evictions
302     assertEquals(oldMemstoreHeapSize, memStoreFlusher.memstoreSize);
303     assertEquals(oldBlockCacheSize, blockCache.maxSize);
304     // Do some more flushes before the next run of HeapMemoryTuner
305     memStoreFlusher.flushType = FlushType.ABOVE_LOWER_MARK;
306     memStoreFlusher.requestFlush(null, false);
307     memStoreFlusher.requestFlush(null, false);
308     memStoreFlusher.requestFlush(null, false);
309     // Allow the tuner to run once and do necessary memory up
310     waitForTune(memStoreFlusher, memStoreFlusher.memstoreSize);
311     assertHeapSpaceDelta(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE, oldMemstoreHeapSize,
312         memStoreFlusher.memstoreSize);
313     assertHeapSpaceDelta(-(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE), oldBlockCacheSize,
314         blockCache.maxSize);
315   }
316 
317   @Test
318   public void testBlockedFlushesIncreaseMemstoreInSteadyState() throws Exception {
319     BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
320     MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
321     RegionServerAccountingStub regionServerAccounting = new RegionServerAccountingStub();
322     // Both memstore and block cache are nearly filled
323     blockCache.setTestBlockSize(0);
324     regionServerAccounting.setTestMemstoreSize((long) (maxHeapSize * 0.4 * 0.8));
325     blockCache.setTestBlockSize((long) (maxHeapSize * 0.4 * 0.8));
326     Configuration conf = HBaseConfiguration.create();
327     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
328     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.10f);
329     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
330     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.05f);
331     conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
332     conf.setInt(DefaultHeapMemoryTuner.NUM_PERIODS_TO_IGNORE, 0);
333     // Let the system start with default values for memstore heap and block cache size.
334     HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
335         new RegionServerStub(conf), regionServerAccounting);
336     long oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
337     long oldBlockCacheSize = blockCache.maxSize;
338     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
339     heapMemoryManager.start(choreService);
340     memStoreFlusher.flushType = FlushType.ABOVE_LOWER_MARK;
341     memStoreFlusher.requestFlush(null, false);
342     memStoreFlusher.requestFlush(null, false);
343     memStoreFlusher.requestFlush(null, false);
344     blockCache.evictBlock(null);
345     blockCache.evictBlock(null);
346     // Allow the tuner to run once and do necessary memory up
347     Thread.sleep(1500);
348     // No changes should happen as there is undefined increase in flushes and evictions
349     assertEquals(oldMemstoreHeapSize, memStoreFlusher.memstoreSize);
350     assertEquals(oldBlockCacheSize, blockCache.maxSize);
351     // Flushes that block updates
352     memStoreFlusher.flushType = FlushType.ABOVE_HIGHER_MARK;
353     memStoreFlusher.requestFlush(null, false);
354     blockCache.evictBlock(null);
355     blockCache.evictBlock(null);
356     blockCache.evictBlock(null);
357     blockCache.evictBlock(null);
358     // Allow the tuner to run once and do necessary memory up
359     Thread.sleep(1500);
360     assertHeapSpaceDelta(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE, oldMemstoreHeapSize,
361         memStoreFlusher.memstoreSize);
362     assertHeapSpaceDelta(-(DefaultHeapMemoryTuner.DEFAULT_MAX_STEP_VALUE), oldBlockCacheSize,
363         blockCache.maxSize);
364   }
365 
366   @Test
367   public void testPluggingInHeapMemoryTuner() throws Exception {
368     BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
369     MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
370     Configuration conf = HBaseConfiguration.create();
371     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.78f);
372     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.05f);
373     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.75f);
374     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.02f);
375     conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
376     conf.setInt(DefaultHeapMemoryTuner.NUM_PERIODS_TO_IGNORE, 0);
377     conf.setClass(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_CLASS, CustomHeapMemoryTuner.class,
378         HeapMemoryTuner.class);
379     // Let the system start with default values for memstore heap and block cache size.
380     HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
381         new RegionServerStub(conf), new RegionServerAccountingStub());
382     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
383     heapMemoryManager.start(choreService);
384     // Now we wants to be in write mode. Set bigger memstore size from CustomHeapMemoryTuner
385     CustomHeapMemoryTuner.memstoreSize = 0.78f;
386     CustomHeapMemoryTuner.blockCacheSize = 0.02f;
387     // Allow the tuner to run once and do necessary memory up
388     waitForTune(memStoreFlusher, memStoreFlusher.memstoreSize);
389     assertHeapSpace(0.78f, memStoreFlusher.memstoreSize);// Memstore
390     assertHeapSpace(0.02f, blockCache.maxSize);// BlockCache
391     // Now we wants to be in read mode. Set bigger memstore size from CustomHeapMemoryTuner
392     CustomHeapMemoryTuner.blockCacheSize = 0.75f;
393     CustomHeapMemoryTuner.memstoreSize = 0.05f;
394     // Allow the tuner to run once and do necessary memory up
395     waitForTune(memStoreFlusher, memStoreFlusher.memstoreSize);
396     assertHeapSpace(0.75f, blockCache.maxSize);// BlockCache
397     assertHeapSpace(0.05f, memStoreFlusher.memstoreSize);// Memstore
398   }
399 
400   @Test
401   public void testWhenSizeGivenByHeapTunerGoesOutsideRange() throws Exception {
402     BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
403     MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
404     Configuration conf = HBaseConfiguration.create();
405     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.7f);
406     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.1f);
407     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
408     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.1f);
409     conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
410     conf.setInt(DefaultHeapMemoryTuner.NUM_PERIODS_TO_IGNORE, 0);
411     conf.setClass(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_CLASS, CustomHeapMemoryTuner.class,
412         HeapMemoryTuner.class);
413     HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
414         new RegionServerStub(conf), new RegionServerAccountingStub());
415     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
416     heapMemoryManager.start(choreService);
417     CustomHeapMemoryTuner.memstoreSize = 0.78f;
418     CustomHeapMemoryTuner.blockCacheSize = 0.02f;
419     Thread.sleep(1500); // Allow the tuner to run once and do necessary memory up
420     // Even if the tuner says to set the memstore to 78%, HBase makes it as 70% as that is the
421     // upper bound. Same with block cache as 10% is the lower bound.
422     assertHeapSpace(0.7f, memStoreFlusher.memstoreSize);
423     assertHeapSpace(0.1f, blockCache.maxSize);
424   }
425 
426   @Test
427   public void testWhenCombinedHeapSizesFromTunerGoesOutSideMaxLimit() throws Exception {
428     BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
429     MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
430     Configuration conf = HBaseConfiguration.create();
431     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.7f);
432     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.1f);
433     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
434     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.1f);
435     conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
436     conf.setInt(DefaultHeapMemoryTuner.NUM_PERIODS_TO_IGNORE, 0);
437     conf.setClass(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_CLASS, CustomHeapMemoryTuner.class,
438         HeapMemoryTuner.class);
439     HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
440         new RegionServerStub(conf), new RegionServerAccountingStub());
441     long oldMemstoreSize = memStoreFlusher.memstoreSize;
442     long oldBlockCacheSize = blockCache.maxSize;
443     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
444     heapMemoryManager.start(choreService);
445     CustomHeapMemoryTuner.memstoreSize = 0.7f;
446     CustomHeapMemoryTuner.blockCacheSize = 0.3f;
447     // Allow the tuner to run once and do necessary memory up
448     Thread.sleep(1500);
449     assertEquals(oldMemstoreSize, memStoreFlusher.memstoreSize);
450     assertEquals(oldBlockCacheSize, blockCache.maxSize);
451   }
452 
453   @Test
454   public void testWhenL2BlockCacheIsOnHeap() throws Exception {
455     HeapMemoryManager heapMemoryManager = null;
456     BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
457     MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.3));
458     Configuration conf = HBaseConfiguration.create();
459     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.7f);
460     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.1f);
461     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
462     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.1f);
463     conf.setInt(DefaultHeapMemoryTuner.NUM_PERIODS_TO_IGNORE, 0);
464     conf.setFloat(HeapMemorySizeUtil.MEMSTORE_SIZE_KEY, 0.4F);
465     conf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0.3F);
466     conf.setFloat(HConstants.BUCKET_CACHE_SIZE_KEY, 0.1F);
467     conf.set(HConstants.BUCKET_CACHE_IOENGINE_KEY, "heap");
468 
469     conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
470     conf.setClass(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_CLASS, CustomHeapMemoryTuner.class,
471         HeapMemoryTuner.class);
472 
473     try {
474       heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
475           new RegionServerStub(conf), new RegionServerAccountingStub());
476       fail("Should have failed as the collective heap memory need is above 80%");
477     } catch (Exception e) {
478     }
479 
480     // Change the max/min ranges for memstore and bock cache so as to pass the criteria check
481     conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.6f);
482     conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.6f);
483     heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
484         new RegionServerStub(conf), new RegionServerAccountingStub());
485     long oldMemstoreSize = memStoreFlusher.memstoreSize;
486     long oldBlockCacheSize = blockCache.maxSize;
487     final ChoreService choreService = new ChoreService("TEST_SERVER_NAME");
488     heapMemoryManager.start(choreService);
489     CustomHeapMemoryTuner.memstoreSize = 0.4f;
490     CustomHeapMemoryTuner.blockCacheSize = 0.4f;
491     // Allow the tuner to run once and do necessary memory up
492    Thread.sleep(1500);
493     // The size should not get changes as the collection of memstore size and L1 and L2 block cache
494     // size will cross the ax allowed 80% mark
495     assertEquals(oldMemstoreSize, memStoreFlusher.memstoreSize);
496     assertEquals(oldBlockCacheSize, blockCache.maxSize);
497     CustomHeapMemoryTuner.memstoreSize = 0.1f;
498     CustomHeapMemoryTuner.blockCacheSize = 0.5f;
499     // Allow the tuner to run once and do necessary memory up
500     waitForTune(memStoreFlusher, memStoreFlusher.memstoreSize);
501     assertHeapSpace(0.1f, memStoreFlusher.memstoreSize);
502     assertHeapSpace(0.5f, blockCache.maxSize);
503   }
504 
505   private void assertHeapSpace(float expectedHeapPercentage, long currentHeapSpace) {
506     long expected = (long) (this.maxHeapSize * expectedHeapPercentage);
507     assertEquals(expected, currentHeapSpace);
508   }
509 
510   private void assertHeapSpaceDelta(double expectedDeltaPercent, long oldHeapSpace, long newHeapSpace) {
511     double expctedMinDelta = (double) (this.maxHeapSize * expectedDeltaPercent);
512     // Tolerable error
513     double error = 0.95;
514     if (expectedDeltaPercent > 0) {
515       assertTrue(expctedMinDelta*error <= (double)(newHeapSpace - oldHeapSpace));
516       assertTrue(expctedMinDelta/error >= (double)(newHeapSpace - oldHeapSpace));
517     } else {
518       assertTrue(-expctedMinDelta*error <= (double)(oldHeapSpace - newHeapSpace));
519       assertTrue(-expctedMinDelta/error >= (double)(oldHeapSpace - newHeapSpace));
520     }
521   }
522 
523 
524   private void waitForTune(final MemstoreFlusherStub memStoreFlusher,
525                            final long oldMemstoreHeapSize) throws Exception {
526     // Allow the tuner to run once and do necessary memory up
527     UTIL.waitFor(10000, new Waiter.Predicate<Exception>() {
528       @Override
529       public boolean evaluate() throws Exception {
530         return oldMemstoreHeapSize != memStoreFlusher.memstoreSize;
531       }
532     });
533   }
534 
535   private static class BlockCacheStub implements ResizableBlockCache {
536     CacheStats stats = new CacheStats("test");
537     long maxSize = 0;
538     private long testBlockSize = 0;
539 
540     public BlockCacheStub(long size){
541       this.maxSize = size;
542     }
543 
544     @Override
545     public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean inMemory,
546         boolean cacheDataInL1) {
547 
548     }
549 
550     @Override
551     public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf) {
552 
553     }
554 
555     @Override
556     public Cacheable getBlock(BlockCacheKey cacheKey, boolean caching, boolean repeat,
557         boolean updateCacheMetrics) {
558       return null;
559     }
560 
561     @Override
562     public boolean evictBlock(BlockCacheKey cacheKey) {
563       stats.evicted(0, cacheKey != null ? cacheKey.isPrimary() : true);
564       return false;
565     }
566 
567     @Override
568     public int evictBlocksByHfileName(String hfileName) {
569       stats.evicted(0, true); // Just assuming only one block for file here.
570       return 0;
571     }
572 
573     @Override
574     public CacheStats getStats() {
575       return this.stats;
576     }
577 
578     @Override
579     public void shutdown() {
580 
581     }
582 
583     @Override
584     public long size() {
585       return 0;
586     }
587 
588     @Override
589     public long getFreeSize() {
590       return 0;
591     }
592 
593     @Override
594     public long getCurrentSize() {
595       return this.testBlockSize;
596     }
597 
598     @Override
599     public long getBlockCount() {
600       return 0;
601     }
602 
603     @Override
604     public void setMaxSize(long size) {
605       this.maxSize = size;
606     }
607 
608     @Override
609     public Iterator<CachedBlock> iterator() {
610       return null;
611     }
612 
613     @Override
614     public BlockCache[] getBlockCaches() {
615       return null;
616     }
617 
618 	public void setTestBlockSize(long testBlockSize) {
619 		this.testBlockSize = testBlockSize;
620 	}
621   }
622 
623   private static class MemstoreFlusherStub implements FlushRequester {
624 
625     long memstoreSize;
626 
627     FlushRequestListener listener;
628 
629     FlushType flushType = FlushType.NORMAL;
630 
631     public MemstoreFlusherStub(long memstoreSize) {
632       this.memstoreSize = memstoreSize;
633     }
634 
635     @Override
636     public void requestFlush(Region region, boolean forceFlushAllStores) {
637       this.listener.flushRequested(flushType, region);
638     }
639 
640     @Override
641     public void requestDelayedFlush(Region region, long delay, boolean forceFlushAllStores) {
642 
643     }
644 
645     @Override
646     public void registerFlushRequestListener(FlushRequestListener listener) {
647       this.listener = listener;
648     }
649 
650     @Override
651     public boolean unregisterFlushRequestListener(FlushRequestListener listener) {
652       return false;
653     }
654 
655     @Override
656     public void setGlobalMemstoreLimit(long globalMemStoreSize) {
657       this.memstoreSize = globalMemStoreSize;
658     }
659   }
660 
661   private static class RegionServerStub implements Server {
662     private Configuration conf;
663     private boolean stopped = false;
664 
665     public RegionServerStub(Configuration conf) {
666       this.conf = conf;
667     }
668 
669     @Override
670     public void abort(String why, Throwable e) {
671 
672     }
673 
674     @Override
675     public boolean isAborted() {
676       return false;
677     }
678 
679     @Override
680     public void stop(String why) {
681       this.stopped = true;
682     }
683 
684     @Override
685     public boolean isStopped() {
686       return this.stopped;
687     }
688 
689     @Override
690     public Configuration getConfiguration() {
691       return this.conf;
692     }
693 
694     @Override
695     public ZooKeeperWatcher getZooKeeper() {
696       return null;
697     }
698 
699     @Override
700     public CoordinatedStateManager getCoordinatedStateManager() {
701       return null;
702     }
703 
704     @Override
705     public ClusterConnection getConnection() {
706       return null;
707     }
708 
709     @Override
710     public MetaTableLocator getMetaTableLocator() {
711       return null;
712     }
713 
714     @Override
715     public ServerName getServerName() {
716       return ServerName.valueOf("server1",4000,12345);
717     }
718 
719     @Override
720     public ChoreService getChoreService() {
721       return null;
722     }
723   }
724 
725   static class CustomHeapMemoryTuner implements HeapMemoryTuner {
726     static float blockCacheSize = 0.4f;
727     static float memstoreSize = 0.4f;
728 
729     @Override
730     public Configuration getConf() {
731       return null;
732     }
733 
734     @Override
735     public void setConf(Configuration arg0) {
736 
737     }
738 
739     @Override
740     public TunerResult tune(TunerContext context) {
741       TunerResult result = new TunerResult(true);
742       result.setBlockCacheSize(blockCacheSize);
743       result.setMemstoreSize(memstoreSize);
744       return result;
745     }
746   }
747 
748   private static class RegionServerAccountingStub extends RegionServerAccounting {
749     private long testMemstoreSize = 0;
750     @Override
751     public long getGlobalMemstoreSize() {
752       return testMemstoreSize;
753     }
754     public void setTestMemstoreSize(long testMemstoreSize) {
755       this.testMemstoreSize = testMemstoreSize;
756     }
757   }
758 }