1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.io.hfile;
19
20 import static org.apache.hadoop.hbase.HConstants.BUCKET_CACHE_IOENGINE_KEY;
21 import static org.apache.hadoop.hbase.HConstants.BUCKET_CACHE_SIZE_KEY;
22
23 import java.io.IOException;
24 import java.lang.management.ManagementFactory;
25 import java.lang.management.MemoryUsage;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.hbase.classification.InterfaceAudience;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.hbase.HColumnDescriptor;
32 import org.apache.hadoop.hbase.HConstants;
33 import org.apache.hadoop.hbase.io.hfile.BlockType.BlockCategory;
34 import org.apache.hadoop.hbase.io.hfile.bucket.BucketCache;
35 import org.apache.hadoop.hbase.io.util.HeapMemorySizeUtil;
36 import org.apache.hadoop.hbase.util.ReflectionUtils;
37 import org.apache.hadoop.util.StringUtils;
38
39 import com.google.common.annotations.VisibleForTesting;
40
41
42
43
44 @InterfaceAudience.Private
45 public class CacheConfig {
46 private static final Log LOG = LogFactory.getLog(CacheConfig.class.getName());
47
48
49
50
51
52 public static final String CACHE_BLOCKS_ON_WRITE_KEY =
53 "hbase.rs.cacheblocksonwrite";
54
55
56
57
58
59 public static final String CACHE_INDEX_BLOCKS_ON_WRITE_KEY =
60 "hfile.block.index.cacheonwrite";
61
62
63
64
65 public static final String CACHE_BLOOM_BLOCKS_ON_WRITE_KEY =
66 "hfile.block.bloom.cacheonwrite";
67
68
69
70
71 public static final String CACHE_DATA_BLOCKS_COMPRESSED_KEY =
72 "hbase.block.data.cachecompressed";
73
74
75
76
77
78 public static final String EVICT_BLOCKS_ON_CLOSE_KEY =
79 "hbase.rs.evictblocksonclose";
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95 public static final String BUCKET_CACHE_PERSISTENT_PATH_KEY =
96 "hbase.bucketcache.persistent.path";
97
98
99
100
101
102
103 public static final String BUCKET_CACHE_COMBINED_KEY =
104 "hbase.bucketcache.combinedcache.enabled";
105
106 public static final String BUCKET_CACHE_WRITER_THREADS_KEY = "hbase.bucketcache.writer.threads";
107 public static final String BUCKET_CACHE_WRITER_QUEUE_KEY =
108 "hbase.bucketcache.writer.queuelength";
109
110
111
112
113 public static final String BUCKET_CACHE_BUCKETS_KEY = "hbase.bucketcache.bucket.sizes";
114
115
116
117
118 public static final boolean DEFAULT_BUCKET_CACHE_COMBINED = true;
119 public static final int DEFAULT_BUCKET_CACHE_WRITER_THREADS = 3;
120 public static final int DEFAULT_BUCKET_CACHE_WRITER_QUEUE = 64;
121
122
123
124
125
126 public static final String PREFETCH_BLOCKS_ON_OPEN_KEY =
127 "hbase.rs.prefetchblocksonopen";
128
129
130
131
132
133
134
135 public static final String BLOCKCACHE_BLOCKSIZE_KEY = "hbase.offheapcache.minblocksize";
136
137 private static final String EXTERNAL_BLOCKCACHE_KEY = "hbase.blockcache.use.external";
138 private static final boolean EXTERNAL_BLOCKCACHE_DEFAULT = false;
139
140 private static final String EXTERNAL_BLOCKCACHE_CLASS_KEY="hbase.blockcache.external.class";
141 private static final String DROP_BEHIND_CACHE_COMPACTION_KEY="hbase.hfile.drop.behind.compaction";
142 private static final boolean DROP_BEHIND_CACHE_COMPACTION_DEFAULT = true;
143
144
145
146
147
148 private static enum ExternalBlockCaches {
149 memcached("org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache");
150
151 Class<? extends BlockCache> clazz;
152 ExternalBlockCaches(String clazzName) {
153 try {
154 clazz = (Class<? extends BlockCache>) Class.forName(clazzName);
155 } catch (ClassNotFoundException cnef) {
156 clazz = null;
157 }
158 }
159 ExternalBlockCaches(Class<? extends BlockCache> clazz) {
160 this.clazz = clazz;
161 }
162 }
163
164
165 public static final boolean DEFAULT_CACHE_DATA_ON_READ = true;
166 public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
167 public static final boolean DEFAULT_IN_MEMORY = false;
168 public static final boolean DEFAULT_CACHE_INDEXES_ON_WRITE = false;
169 public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
170 public static final boolean DEFAULT_EVICT_ON_CLOSE = false;
171 public static final boolean DEFAULT_CACHE_DATA_COMPRESSED = false;
172 public static final boolean DEFAULT_PREFETCH_ON_OPEN = false;
173
174
175 private final BlockCache blockCache;
176
177
178
179
180
181
182
183 private boolean cacheDataOnRead;
184
185
186 private final boolean inMemory;
187
188
189 private boolean cacheDataOnWrite;
190
191
192 private final boolean cacheIndexesOnWrite;
193
194
195 private final boolean cacheBloomsOnWrite;
196
197
198 private boolean evictOnClose;
199
200
201 private final boolean cacheDataCompressed;
202
203
204 private final boolean prefetchOnOpen;
205
206
207
208
209
210
211 private boolean cacheDataInL1;
212
213 private final boolean dropBehindCompaction;
214
215
216
217
218
219
220
221 public CacheConfig(Configuration conf, HColumnDescriptor family) {
222 this(CacheConfig.instantiateBlockCache(conf),
223 family.isBlockCacheEnabled(),
224 family.isInMemory(),
225
226
227 conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY,
228 DEFAULT_CACHE_DATA_ON_WRITE) || family.isCacheDataOnWrite(),
229 conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY,
230 DEFAULT_CACHE_INDEXES_ON_WRITE) || family.isCacheIndexesOnWrite(),
231 conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY,
232 DEFAULT_CACHE_BLOOMS_ON_WRITE) || family.isCacheBloomsOnWrite(),
233 conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY,
234 DEFAULT_EVICT_ON_CLOSE) || family.isEvictBlocksOnClose(),
235 conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
236 conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY,
237 DEFAULT_PREFETCH_ON_OPEN) || family.isPrefetchBlocksOnOpen(),
238 conf.getBoolean(HColumnDescriptor.CACHE_DATA_IN_L1,
239 HColumnDescriptor.DEFAULT_CACHE_DATA_IN_L1) || family.isCacheDataInL1(),
240 conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY,DROP_BEHIND_CACHE_COMPACTION_DEFAULT)
241 );
242 LOG.info("Created cacheConfig for " + family.getNameAsString() + ": " + this);
243 }
244
245
246
247
248
249
250
251
252 public CacheConfig(Configuration conf) {
253 this(CacheConfig.instantiateBlockCache(conf),
254 DEFAULT_CACHE_DATA_ON_READ,
255 DEFAULT_IN_MEMORY,
256
257 conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_DATA_ON_WRITE),
258 conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_INDEXES_ON_WRITE),
259 conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_BLOOMS_ON_WRITE),
260 conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, DEFAULT_EVICT_ON_CLOSE),
261 conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
262 conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY, DEFAULT_PREFETCH_ON_OPEN),
263 conf.getBoolean(HColumnDescriptor.CACHE_DATA_IN_L1,
264 HColumnDescriptor.DEFAULT_CACHE_DATA_IN_L1),
265 conf.getBoolean(DROP_BEHIND_CACHE_COMPACTION_KEY,DROP_BEHIND_CACHE_COMPACTION_DEFAULT)
266 );
267 LOG.info("Created cacheConfig: " + this);
268 }
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286 CacheConfig(final BlockCache blockCache,
287 final boolean cacheDataOnRead, final boolean inMemory,
288 final boolean cacheDataOnWrite, final boolean cacheIndexesOnWrite,
289 final boolean cacheBloomsOnWrite, final boolean evictOnClose,
290 final boolean cacheDataCompressed, final boolean prefetchOnOpen,
291 final boolean cacheDataInL1, final boolean dropBehindCompaction) {
292 this.blockCache = blockCache;
293 this.cacheDataOnRead = cacheDataOnRead;
294 this.inMemory = inMemory;
295 this.cacheDataOnWrite = cacheDataOnWrite;
296 this.cacheIndexesOnWrite = cacheIndexesOnWrite;
297 this.cacheBloomsOnWrite = cacheBloomsOnWrite;
298 this.evictOnClose = evictOnClose;
299 this.cacheDataCompressed = cacheDataCompressed;
300 this.prefetchOnOpen = prefetchOnOpen;
301 this.cacheDataInL1 = cacheDataInL1;
302 this.dropBehindCompaction = dropBehindCompaction;
303 }
304
305
306
307
308
309 public CacheConfig(CacheConfig cacheConf) {
310 this(cacheConf.blockCache, cacheConf.cacheDataOnRead, cacheConf.inMemory,
311 cacheConf.cacheDataOnWrite, cacheConf.cacheIndexesOnWrite,
312 cacheConf.cacheBloomsOnWrite, cacheConf.evictOnClose,
313 cacheConf.cacheDataCompressed, cacheConf.prefetchOnOpen,
314 cacheConf.cacheDataInL1, cacheConf.dropBehindCompaction);
315 }
316
317
318
319
320 public boolean isBlockCacheEnabled() {
321 return this.blockCache != null;
322 }
323
324
325
326
327
328 public BlockCache getBlockCache() {
329 return this.blockCache;
330 }
331
332
333
334
335
336
337 public boolean shouldCacheDataOnRead() {
338 return isBlockCacheEnabled() && cacheDataOnRead;
339 }
340
341 public boolean shouldDropBehindCompaction() {
342 return dropBehindCompaction;
343 }
344
345
346
347
348
349
350 public boolean shouldCacheBlockOnRead(BlockCategory category) {
351 return isBlockCacheEnabled()
352 && (cacheDataOnRead ||
353 category == BlockCategory.INDEX ||
354 category == BlockCategory.BLOOM ||
355 (prefetchOnOpen &&
356 (category != BlockCategory.META &&
357 category != BlockCategory.UNKNOWN)));
358 }
359
360
361
362
363 public boolean isInMemory() {
364 return isBlockCacheEnabled() && this.inMemory;
365 }
366
367
368
369
370 public boolean isCacheDataInL1() {
371 return isBlockCacheEnabled() && this.cacheDataInL1;
372 }
373
374
375
376
377
378 public boolean shouldCacheDataOnWrite() {
379 return isBlockCacheEnabled() && this.cacheDataOnWrite;
380 }
381
382
383
384
385
386
387 @VisibleForTesting
388 public void setCacheDataOnWrite(boolean cacheDataOnWrite) {
389 this.cacheDataOnWrite = cacheDataOnWrite;
390 }
391
392
393
394
395
396
397 @VisibleForTesting
398 public void setCacheDataInL1(boolean cacheDataInL1) {
399 this.cacheDataInL1 = cacheDataInL1;
400 }
401
402
403
404
405
406 public boolean shouldCacheIndexesOnWrite() {
407 return isBlockCacheEnabled() && this.cacheIndexesOnWrite;
408 }
409
410
411
412
413
414 public boolean shouldCacheBloomsOnWrite() {
415 return isBlockCacheEnabled() && this.cacheBloomsOnWrite;
416 }
417
418
419
420
421
422 public boolean shouldEvictOnClose() {
423 return isBlockCacheEnabled() && this.evictOnClose;
424 }
425
426
427
428
429
430
431 public void setEvictOnClose(boolean evictOnClose) {
432 this.evictOnClose = evictOnClose;
433 }
434
435
436
437
438 public boolean shouldCacheDataCompressed() {
439 return isBlockCacheEnabled() && this.cacheDataCompressed;
440 }
441
442
443
444
445 public boolean shouldCacheCompressed(BlockCategory category) {
446 if (!isBlockCacheEnabled()) return false;
447 switch (category) {
448 case DATA:
449 return this.cacheDataCompressed;
450 default:
451 return false;
452 }
453 }
454
455
456
457
458 public boolean shouldPrefetchOnOpen() {
459 return isBlockCacheEnabled() && this.prefetchOnOpen;
460 }
461
462
463
464
465
466
467
468
469
470 public boolean shouldReadBlockFromCache(BlockType blockType) {
471 if (!isBlockCacheEnabled()) {
472 return false;
473 }
474 if (cacheDataOnRead) {
475 return true;
476 }
477 if (prefetchOnOpen) {
478 return true;
479 }
480 if (cacheDataOnWrite) {
481 return true;
482 }
483 if (blockType == null) {
484 return true;
485 }
486 if (blockType.getCategory() == BlockCategory.BLOOM ||
487 blockType.getCategory() == BlockCategory.INDEX) {
488 return true;
489 }
490 return false;
491 }
492
493
494
495
496
497 public boolean shouldLockOnCacheMiss(BlockType blockType) {
498 if (blockType == null) {
499 return true;
500 }
501 return shouldCacheBlockOnRead(blockType.getCategory());
502 }
503
504 @Override
505 public String toString() {
506 if (!isBlockCacheEnabled()) {
507 return "CacheConfig:disabled";
508 }
509 return "blockCache=" + getBlockCache() +
510 ", cacheDataOnRead=" + shouldCacheDataOnRead() +
511 ", cacheDataOnWrite=" + shouldCacheDataOnWrite() +
512 ", cacheIndexesOnWrite=" + shouldCacheIndexesOnWrite() +
513 ", cacheBloomsOnWrite=" + shouldCacheBloomsOnWrite() +
514 ", cacheEvictOnClose=" + shouldEvictOnClose() +
515 ", cacheDataCompressed=" + shouldCacheDataCompressed() +
516 ", prefetchOnOpen=" + shouldPrefetchOnOpen();
517 }
518
519
520
521
522
523
524
525
526 @VisibleForTesting
527 static BlockCache GLOBAL_BLOCK_CACHE_INSTANCE;
528 private static LruBlockCache GLOBAL_L1_CACHE_INSTANCE = null;
529 private static BlockCache GLOBAL_L2_CACHE_INSTANCE = null;
530
531
532 @VisibleForTesting
533 static boolean blockCacheDisabled = false;
534
535
536
537
538
539 private static synchronized LruBlockCache getL1(final Configuration c) {
540 if (GLOBAL_L1_CACHE_INSTANCE != null) return GLOBAL_L1_CACHE_INSTANCE;
541 final long lruCacheSize = HeapMemorySizeUtil.getLruCacheSize(c);
542 if (lruCacheSize < 0) {
543 blockCacheDisabled = true;
544 }
545 if (blockCacheDisabled) return null;
546 int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
547 LOG.info("Allocating LruBlockCache size=" +
548 StringUtils.byteDesc(lruCacheSize) + ", blockSize=" + StringUtils.byteDesc(blockSize));
549 GLOBAL_L1_CACHE_INSTANCE = new LruBlockCache(lruCacheSize, blockSize, true, c);
550 return GLOBAL_L1_CACHE_INSTANCE;
551 }
552
553
554
555
556
557
558 @VisibleForTesting
559 static BlockCache getL2(final Configuration c) {
560 final boolean useExternal = c.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
561 if (LOG.isDebugEnabled()) {
562 LOG.debug("Trying to use " + (useExternal?" External":" Internal") + " l2 cache");
563 }
564
565
566 if (useExternal) {
567 GLOBAL_L2_CACHE_INSTANCE = getExternalBlockcache(c);
568 } else {
569
570 GLOBAL_L2_CACHE_INSTANCE = getBucketCache(c);
571 }
572 return GLOBAL_L2_CACHE_INSTANCE;
573 }
574
575 public CacheStats getL1Stats() {
576 if (GLOBAL_L1_CACHE_INSTANCE != null) {
577 return GLOBAL_L1_CACHE_INSTANCE.getStats();
578 }
579 return null;
580 }
581
582 public CacheStats getL2Stats() {
583 if (GLOBAL_L2_CACHE_INSTANCE != null) {
584 return GLOBAL_L2_CACHE_INSTANCE.getStats();
585 }
586 return null;
587 }
588
589 private static BlockCache getExternalBlockcache(Configuration c) {
590 Class klass = null;
591
592
593 try {
594 klass = ExternalBlockCaches.valueOf(c.get(EXTERNAL_BLOCKCACHE_CLASS_KEY, "memcache")).clazz;
595 } catch (IllegalArgumentException exception) {
596 try {
597 klass = c.getClass(EXTERNAL_BLOCKCACHE_CLASS_KEY, Class.forName(
598 "org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache"));
599 } catch (ClassNotFoundException e) {
600 return null;
601 }
602 }
603
604
605 try {
606 LOG.info("Creating external block cache of type: " + klass);
607 return (BlockCache) ReflectionUtils.newInstance(klass, c);
608 } catch (Exception e) {
609 LOG.warn("Error creating external block cache", e);
610 }
611 return null;
612
613 }
614
615 private static BlockCache getBucketCache(Configuration c) {
616
617 String bucketCacheIOEngineName = c.get(BUCKET_CACHE_IOENGINE_KEY, null);
618 if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) return null;
619
620 int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
621 final long bucketCacheSize = HeapMemorySizeUtil.getBucketCacheSize(c);
622 if (bucketCacheSize <= 0) {
623 throw new IllegalStateException("bucketCacheSize <= 0; Check " +
624 BUCKET_CACHE_SIZE_KEY + " setting and/or server java heap size");
625 }
626 if (c.get("hbase.bucketcache.percentage.in.combinedcache") != null) {
627 LOG.warn("Configuration 'hbase.bucketcache.percentage.in.combinedcache' is no longer "
628 + "respected. See comments in http://hbase.apache.org/book.html#_changes_of_note");
629 }
630 int writerThreads = c.getInt(BUCKET_CACHE_WRITER_THREADS_KEY,
631 DEFAULT_BUCKET_CACHE_WRITER_THREADS);
632 int writerQueueLen = c.getInt(BUCKET_CACHE_WRITER_QUEUE_KEY,
633 DEFAULT_BUCKET_CACHE_WRITER_QUEUE);
634 String persistentPath = c.get(BUCKET_CACHE_PERSISTENT_PATH_KEY);
635 String[] configuredBucketSizes = c.getStrings(BUCKET_CACHE_BUCKETS_KEY);
636 int [] bucketSizes = null;
637 if (configuredBucketSizes != null) {
638 bucketSizes = new int[configuredBucketSizes.length];
639 for (int i = 0; i < configuredBucketSizes.length; i++) {
640 int bucketSize = Integer.parseInt(configuredBucketSizes[i].trim());
641 if (bucketSize % 256 != 0) {
642
643
644
645
646
647 throw new IllegalArgumentException("Illegal value: " + bucketSize + " configured for '"
648 + BUCKET_CACHE_BUCKETS_KEY + "'. All bucket sizes to be multiples of 256");
649 }
650 bucketSizes[i] = bucketSize;
651 }
652 }
653 BucketCache bucketCache = null;
654 try {
655 int ioErrorsTolerationDuration = c.getInt(
656 "hbase.bucketcache.ioengine.errors.tolerated.duration",
657 BucketCache.DEFAULT_ERROR_TOLERATION_DURATION);
658
659 bucketCache = new BucketCache(bucketCacheIOEngineName,
660 bucketCacheSize, blockSize, bucketSizes, writerThreads, writerQueueLen, persistentPath,
661 ioErrorsTolerationDuration, c);
662 } catch (IOException ioex) {
663 LOG.error("Can't instantiate bucket cache", ioex); throw new RuntimeException(ioex);
664 }
665 return bucketCache;
666 }
667
668
669
670
671
672
673
674
675 public static synchronized BlockCache instantiateBlockCache(Configuration conf) {
676 if (GLOBAL_BLOCK_CACHE_INSTANCE != null) return GLOBAL_BLOCK_CACHE_INSTANCE;
677 if (blockCacheDisabled) return null;
678 LruBlockCache l1 = getL1(conf);
679
680 if (blockCacheDisabled) return null;
681 BlockCache l2 = getL2(conf);
682 if (l2 == null) {
683 GLOBAL_BLOCK_CACHE_INSTANCE = l1;
684 } else {
685 boolean useExternal = conf.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
686 boolean combinedWithLru = conf.getBoolean(BUCKET_CACHE_COMBINED_KEY,
687 DEFAULT_BUCKET_CACHE_COMBINED);
688 if (useExternal) {
689 GLOBAL_BLOCK_CACHE_INSTANCE = new InclusiveCombinedBlockCache(l1, l2);
690 } else {
691 if (combinedWithLru) {
692 GLOBAL_BLOCK_CACHE_INSTANCE = new CombinedBlockCache(l1, l2);
693 } else {
694
695
696
697
698 GLOBAL_BLOCK_CACHE_INSTANCE = l1;
699 }
700 }
701 l1.setVictimCache(l2);
702 }
703 return GLOBAL_BLOCK_CACHE_INSTANCE;
704 }
705
706 @VisibleForTesting
707 static synchronized void clearGlobalInstances() {
708 GLOBAL_L1_CACHE_INSTANCE = null;
709 GLOBAL_L2_CACHE_INSTANCE = null;
710 GLOBAL_BLOCK_CACHE_INSTANCE = null;
711 }
712 }