1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.CoordinatedStateManager;
31 import org.apache.hadoop.hbase.HBaseConfiguration;
32 import org.apache.hadoop.hbase.HConstants;
33 import org.apache.hadoop.hbase.Server;
34 import org.apache.hadoop.hbase.ServerName;
35 import org.apache.hadoop.hbase.testclassification.SmallTests;
36 import org.apache.hadoop.hbase.client.ClusterConnection;
37 import org.apache.hadoop.hbase.io.hfile.BlockCache;
38 import org.apache.hadoop.hbase.io.hfile.BlockCacheKey;
39 import org.apache.hadoop.hbase.io.hfile.CacheStats;
40 import org.apache.hadoop.hbase.io.hfile.Cacheable;
41 import org.apache.hadoop.hbase.io.hfile.CachedBlock;
42 import org.apache.hadoop.hbase.io.hfile.ResizableBlockCache;
43 import org.apache.hadoop.hbase.io.util.HeapMemorySizeUtil;
44 import org.apache.hadoop.hbase.regionserver.HeapMemoryManager.TunerContext;
45 import org.apache.hadoop.hbase.regionserver.HeapMemoryManager.TunerResult;
46 import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
47 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
48 import org.junit.Test;
49 import org.junit.experimental.categories.Category;
50
51 @Category(SmallTests.class)
52 public class TestHeapMemoryManager {
53
54 private long maxHeapSize = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax();
55
56 @Test
57 public void testAutoTunerShouldBeOffWhenMaxMinRangesForMemstoreIsNotGiven() throws Exception {
58 Configuration conf = HBaseConfiguration.create();
59 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.75f);
60 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.05f);
61 HeapMemoryManager manager = new HeapMemoryManager(new BlockCacheStub(0),
62 new MemstoreFlusherStub(0), new RegionServerStub(conf));
63 assertFalse(manager.isTunerOn());
64 }
65
66 @Test
67 public void testAutoTunerShouldBeOffWhenMaxMinRangesForBlockCacheIsNotGiven() throws Exception {
68 Configuration conf = HBaseConfiguration.create();
69 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
70 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.05f);
71 HeapMemoryManager manager = new HeapMemoryManager(new BlockCacheStub(0),
72 new MemstoreFlusherStub(0), new RegionServerStub(conf));
73 assertFalse(manager.isTunerOn());
74 }
75
76 @Test
77 public void testWhenMemstoreAndBlockCacheMaxMinChecksFails() throws Exception {
78 BlockCacheStub blockCache = new BlockCacheStub(0);
79 MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub(0);
80 Configuration conf = HBaseConfiguration.create();
81 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
82 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.06f);
83 try {
84 new HeapMemoryManager(blockCache, memStoreFlusher, new RegionServerStub(conf));
85 fail();
86 } catch (RuntimeException e) {
87 }
88 conf = HBaseConfiguration.create();
89 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.2f);
90 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
91 try {
92 new HeapMemoryManager(blockCache, memStoreFlusher, new RegionServerStub(conf));
93 fail();
94 } catch (RuntimeException e) {
95 }
96 }
97
98 @Test
99 public void testWhenClusterIsWriteHeavy() throws Exception {
100 BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
101 MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
102 Configuration conf = HBaseConfiguration.create();
103 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
104 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.10f);
105 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
106 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.05f);
107 conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
108
109 HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
110 new RegionServerStub(conf));
111 long oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
112 long oldBlockCacheSize = blockCache.maxSize;
113 heapMemoryManager.start();
114 memStoreFlusher.flushType = FlushType.ABOVE_HIGHER_MARK;
115 memStoreFlusher.requestFlush(null);
116 memStoreFlusher.requestFlush(null);
117 memStoreFlusher.requestFlush(null);
118 memStoreFlusher.flushType = FlushType.ABOVE_LOWER_MARK;
119 memStoreFlusher.requestFlush(null);
120 Thread.sleep(1500);
121 assertHeapSpaceDelta(DefaultHeapMemoryTuner.DEFAULT_STEP_VALUE, oldMemstoreHeapSize,
122 memStoreFlusher.memstoreSize);
123 assertHeapSpaceDelta(-(DefaultHeapMemoryTuner.DEFAULT_STEP_VALUE), oldBlockCacheSize,
124 blockCache.maxSize);
125 oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
126 oldBlockCacheSize = blockCache.maxSize;
127
128 memStoreFlusher.flushType = FlushType.ABOVE_HIGHER_MARK;
129 memStoreFlusher.requestFlush(null);
130 memStoreFlusher.requestFlush(null);
131 Thread.sleep(1500);
132 assertHeapSpaceDelta(DefaultHeapMemoryTuner.DEFAULT_STEP_VALUE, oldMemstoreHeapSize,
133 memStoreFlusher.memstoreSize);
134 assertHeapSpaceDelta(-(DefaultHeapMemoryTuner.DEFAULT_STEP_VALUE), oldBlockCacheSize,
135 blockCache.maxSize);
136 }
137
138 @Test
139 public void testWhenClusterIsReadHeavy() throws Exception {
140 BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
141 MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
142 Configuration conf = HBaseConfiguration.create();
143 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.75f);
144 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.10f);
145 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
146 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.05f);
147 conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
148
149 HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
150 new RegionServerStub(conf));
151 long oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
152 long oldBlockCacheSize = blockCache.maxSize;
153 heapMemoryManager.start();
154 blockCache.evictBlock(null);
155 blockCache.evictBlock(null);
156 blockCache.evictBlock(null);
157 Thread.sleep(1500);
158 assertHeapSpaceDelta(-(DefaultHeapMemoryTuner.DEFAULT_STEP_VALUE), oldMemstoreHeapSize,
159 memStoreFlusher.memstoreSize);
160 assertHeapSpaceDelta(DefaultHeapMemoryTuner.DEFAULT_STEP_VALUE, oldBlockCacheSize,
161 blockCache.maxSize);
162 oldMemstoreHeapSize = memStoreFlusher.memstoreSize;
163 oldBlockCacheSize = blockCache.maxSize;
164
165 blockCache.evictBlock(null);
166 Thread.sleep(1500);
167 assertHeapSpaceDelta(-(DefaultHeapMemoryTuner.DEFAULT_STEP_VALUE), oldMemstoreHeapSize,
168 memStoreFlusher.memstoreSize);
169 assertHeapSpaceDelta(DefaultHeapMemoryTuner.DEFAULT_STEP_VALUE, oldBlockCacheSize,
170 blockCache.maxSize);
171 }
172
173 @Test
174 public void testPluggingInHeapMemoryTuner() throws Exception {
175 BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
176 MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
177 Configuration conf = HBaseConfiguration.create();
178 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.78f);
179 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.05f);
180 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.75f);
181 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.02f);
182 conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
183 conf.setClass(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_CLASS, CustomHeapMemoryTuner.class,
184 HeapMemoryTuner.class);
185
186 HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
187 new RegionServerStub(conf));
188 heapMemoryManager.start();
189
190 CustomHeapMemoryTuner.memstoreSize = 0.78f;
191 CustomHeapMemoryTuner.blockCacheSize = 0.02f;
192 Thread.sleep(1500);
193 assertHeapSpace(0.78f, memStoreFlusher.memstoreSize);
194 assertHeapSpace(0.02f, blockCache.maxSize);
195
196 CustomHeapMemoryTuner.blockCacheSize = 0.75f;
197 CustomHeapMemoryTuner.memstoreSize = 0.05f;
198 Thread.sleep(1500);
199 assertHeapSpace(0.75f, blockCache.maxSize);
200 assertHeapSpace(0.05f, memStoreFlusher.memstoreSize);
201 }
202
203 @Test
204 public void testWhenSizeGivenByHeapTunerGoesOutsideRange() throws Exception {
205 BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
206 MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
207 Configuration conf = HBaseConfiguration.create();
208 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.7f);
209 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.1f);
210 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
211 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.1f);
212 conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
213 conf.setClass(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_CLASS, CustomHeapMemoryTuner.class,
214 HeapMemoryTuner.class);
215 HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
216 new RegionServerStub(conf));
217 heapMemoryManager.start();
218 CustomHeapMemoryTuner.memstoreSize = 0.78f;
219 CustomHeapMemoryTuner.blockCacheSize = 0.02f;
220 Thread.sleep(1500);
221
222
223 assertHeapSpace(0.7f, memStoreFlusher.memstoreSize);
224 assertHeapSpace(0.1f, blockCache.maxSize);
225 }
226
227 @Test
228 public void testWhenCombinedHeapSizesFromTunerGoesOutSideMaxLimit() throws Exception {
229 BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
230 MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.4));
231 Configuration conf = HBaseConfiguration.create();
232 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.7f);
233 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.1f);
234 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
235 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.1f);
236 conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
237 conf.setClass(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_CLASS, CustomHeapMemoryTuner.class,
238 HeapMemoryTuner.class);
239 HeapMemoryManager heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher,
240 new RegionServerStub(conf));
241 long oldMemstoreSize = memStoreFlusher.memstoreSize;
242 long oldBlockCacheSize = blockCache.maxSize;
243 heapMemoryManager.start();
244 CustomHeapMemoryTuner.memstoreSize = 0.7f;
245 CustomHeapMemoryTuner.blockCacheSize = 0.3f;
246 Thread.sleep(1500);
247 assertEquals(oldMemstoreSize, memStoreFlusher.memstoreSize);
248 assertEquals(oldBlockCacheSize, blockCache.maxSize);
249 }
250
251 @Test
252 public void testWhenL2BlockCacheIsOnHeap() throws Exception {
253 HeapMemoryManager heapMemoryManager = null;
254 BlockCacheStub blockCache = new BlockCacheStub((long) (maxHeapSize * 0.4));
255 MemstoreFlusherStub memStoreFlusher = new MemstoreFlusherStub((long) (maxHeapSize * 0.3));
256 Configuration conf = HBaseConfiguration.create();
257 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.7f);
258 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MIN_RANGE_KEY, 0.1f);
259 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.7f);
260 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MIN_RANGE_KEY, 0.1f);
261
262 conf.setFloat(HeapMemorySizeUtil.MEMSTORE_SIZE_KEY, 0.4F);
263 conf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0.3F);
264 conf.setFloat(HConstants.BUCKET_CACHE_SIZE_KEY, 0.1F);
265 conf.set(HConstants.BUCKET_CACHE_IOENGINE_KEY, "heap");
266
267 conf.setLong(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_PERIOD, 1000);
268 conf.setClass(HeapMemoryManager.HBASE_RS_HEAP_MEMORY_TUNER_CLASS, CustomHeapMemoryTuner.class,
269 HeapMemoryTuner.class);
270
271 try {
272 heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher, new RegionServerStub(
273 conf));
274 fail("Should have failed as the collective heap memory need is above 80%");
275 } catch (Exception e) {
276 }
277
278
279 conf.setFloat(HeapMemoryManager.MEMSTORE_SIZE_MAX_RANGE_KEY, 0.6f);
280 conf.setFloat(HeapMemoryManager.BLOCK_CACHE_SIZE_MAX_RANGE_KEY, 0.6f);
281 heapMemoryManager = new HeapMemoryManager(blockCache, memStoreFlusher, new RegionServerStub(
282 conf));
283 long oldMemstoreSize = memStoreFlusher.memstoreSize;
284 long oldBlockCacheSize = blockCache.maxSize;
285 heapMemoryManager.start();
286 CustomHeapMemoryTuner.memstoreSize = 0.4f;
287 CustomHeapMemoryTuner.blockCacheSize = 0.4f;
288 Thread.sleep(1500);
289
290
291 assertEquals(oldMemstoreSize, memStoreFlusher.memstoreSize);
292 assertEquals(oldBlockCacheSize, blockCache.maxSize);
293 CustomHeapMemoryTuner.memstoreSize = 0.1f;
294 CustomHeapMemoryTuner.blockCacheSize = 0.5f;
295 Thread.sleep(1500);
296 assertHeapSpace(0.1f, memStoreFlusher.memstoreSize);
297 assertHeapSpace(0.5f, blockCache.maxSize);
298 }
299
300 private void assertHeapSpace(float expectedHeapPercentage, long currentHeapSpace) {
301 long expected = (long) (this.maxHeapSize * expectedHeapPercentage);
302 assertEquals(expected, currentHeapSpace);
303 }
304
305 private void assertHeapSpaceDelta(float expectedDeltaPercent, long oldHeapSpace, long newHeapSpace) {
306 long expctedMinDelta = (long) (this.maxHeapSize * expectedDeltaPercent);
307 if (expectedDeltaPercent > 0) {
308 assertTrue(expctedMinDelta <= (newHeapSpace - oldHeapSpace));
309 } else {
310 assertTrue(expctedMinDelta <= (oldHeapSpace - newHeapSpace));
311 }
312 }
313
314 private static class BlockCacheStub implements ResizableBlockCache {
315 CacheStats stats = new CacheStats("test");
316 long maxSize = 0;
317
318 public BlockCacheStub(long size){
319 this.maxSize = size;
320 }
321
322 @Override
323 public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf, boolean inMemory,
324 boolean cacheDataInL1) {
325
326 }
327
328 @Override
329 public void cacheBlock(BlockCacheKey cacheKey, Cacheable buf) {
330
331 }
332
333 @Override
334 public Cacheable getBlock(BlockCacheKey cacheKey, boolean caching, boolean repeat,
335 boolean updateCacheMetrics) {
336 return null;
337 }
338
339 @Override
340 public boolean evictBlock(BlockCacheKey cacheKey) {
341 stats.evicted(0);
342 return false;
343 }
344
345 @Override
346 public int evictBlocksByHfileName(String hfileName) {
347 stats.evicted(0);
348 return 0;
349 }
350
351 @Override
352 public CacheStats getStats() {
353 return this.stats;
354 }
355
356 @Override
357 public void shutdown() {
358
359 }
360
361 @Override
362 public long size() {
363 return 0;
364 }
365
366 @Override
367 public long getFreeSize() {
368 return 0;
369 }
370
371 @Override
372 public long getCurrentSize() {
373 return 0;
374 }
375
376 @Override
377 public long getBlockCount() {
378 return 0;
379 }
380
381 @Override
382 public void setMaxSize(long size) {
383 this.maxSize = size;
384 }
385
386 @Override
387 public Iterator<CachedBlock> iterator() {
388 return null;
389 }
390
391 @Override
392 public BlockCache[] getBlockCaches() {
393 return null;
394 }
395 }
396
397 private static class MemstoreFlusherStub implements FlushRequester {
398
399 long memstoreSize;
400
401 FlushRequestListener listener;
402
403 FlushType flushType = FlushType.NORMAL;
404
405 public MemstoreFlusherStub(long memstoreSize) {
406 this.memstoreSize = memstoreSize;
407 }
408
409 @Override
410 public void requestFlush(HRegion region) {
411 this.listener.flushRequested(flushType, region);
412 }
413
414 @Override
415 public void requestDelayedFlush(HRegion region, long delay) {
416
417 }
418
419 @Override
420 public void registerFlushRequestListener(FlushRequestListener listener) {
421 this.listener = listener;
422 }
423
424 @Override
425 public boolean unregisterFlushRequestListener(FlushRequestListener listener) {
426 return false;
427 }
428
429 @Override
430 public void setGlobalMemstoreLimit(long globalMemStoreSize) {
431 this.memstoreSize = globalMemStoreSize;
432 }
433 }
434
435 private static class RegionServerStub implements Server {
436 private Configuration conf;
437 private boolean stopped = false;
438
439 public RegionServerStub(Configuration conf) {
440 this.conf = conf;
441 }
442
443 @Override
444 public void abort(String why, Throwable e) {
445
446 }
447
448 @Override
449 public boolean isAborted() {
450 return false;
451 }
452
453 @Override
454 public void stop(String why) {
455 this.stopped = true;
456 }
457
458 @Override
459 public boolean isStopped() {
460 return this.stopped;
461 }
462
463 @Override
464 public Configuration getConfiguration() {
465 return this.conf;
466 }
467
468 @Override
469 public ZooKeeperWatcher getZooKeeper() {
470 return null;
471 }
472
473 @Override
474 public CoordinatedStateManager getCoordinatedStateManager() {
475 return null;
476 }
477
478 @Override
479 public ClusterConnection getConnection() {
480 return null;
481 }
482
483 @Override
484 public MetaTableLocator getMetaTableLocator() {
485 return null;
486 }
487
488 @Override
489 public ServerName getServerName() {
490 return ServerName.valueOf("server1",4000,12345);
491 }
492 }
493
494 static class CustomHeapMemoryTuner implements HeapMemoryTuner {
495 static float blockCacheSize = 0.4f;
496 static float memstoreSize = 0.4f;
497
498 @Override
499 public Configuration getConf() {
500 return null;
501 }
502
503 @Override
504 public void setConf(Configuration arg0) {
505
506 }
507
508 @Override
509 public TunerResult tune(TunerContext context) {
510 TunerResult result = new TunerResult(true);
511 result.setBlockCacheSize(blockCacheSize);
512 result.setMemstoreSize(memstoreSize);
513 return result;
514 }
515 }
516 }