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
529
530 @VisibleForTesting
531 static boolean blockCacheDisabled = false;
532
533
534
535
536
537 private static LruBlockCache getL1(final Configuration c) {
538 final long lruCacheSize = HeapMemorySizeUtil.getLruCacheSize(c);
539 if (lruCacheSize < 0) {
540 blockCacheDisabled = true;
541 }
542 if (blockCacheDisabled) return null;
543 int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
544 LOG.info("Allocating LruBlockCache size=" +
545 StringUtils.byteDesc(lruCacheSize) + ", blockSize=" + StringUtils.byteDesc(blockSize));
546 return new LruBlockCache(lruCacheSize, blockSize, true, c);
547 }
548
549
550
551
552
553
554 private static BlockCache getL2(final Configuration c) {
555 final boolean useExternal = c.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
556 if (LOG.isDebugEnabled()) {
557 LOG.debug("Trying to use " + (useExternal?" External":" Internal") + " l2 cache");
558 }
559
560
561 if (useExternal) {
562 return getExternalBlockcache(c);
563 }
564
565
566 return getBucketCache(c);
567
568 }
569
570 private static BlockCache getExternalBlockcache(Configuration c) {
571 Class klass = null;
572
573
574 try {
575 klass = ExternalBlockCaches.valueOf(c.get(EXTERNAL_BLOCKCACHE_CLASS_KEY, "memcache")).clazz;
576 } catch (IllegalArgumentException exception) {
577 try {
578 klass = c.getClass(EXTERNAL_BLOCKCACHE_CLASS_KEY, Class.forName(
579 "org.apache.hadoop.hbase.io.hfile.MemcachedBlockCache"));
580 } catch (ClassNotFoundException e) {
581 return null;
582 }
583 }
584
585
586 try {
587 LOG.info("Creating external block cache of type: " + klass);
588 return (BlockCache) ReflectionUtils.newInstance(klass, c);
589 } catch (Exception e) {
590 LOG.warn("Error creating external block cache", e);
591 }
592 return null;
593
594 }
595
596 private static BlockCache getBucketCache(Configuration c) {
597
598 String bucketCacheIOEngineName = c.get(BUCKET_CACHE_IOENGINE_KEY, null);
599 if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) return null;
600
601 int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
602 final long bucketCacheSize = HeapMemorySizeUtil.getBucketCacheSize(c);
603 if (bucketCacheSize <= 0) {
604 throw new IllegalStateException("bucketCacheSize <= 0; Check " +
605 BUCKET_CACHE_SIZE_KEY + " setting and/or server java heap size");
606 }
607 if (c.get("hbase.bucketcache.percentage.in.combinedcache") != null) {
608 LOG.warn("Configuration 'hbase.bucketcache.percentage.in.combinedcache' is no longer "
609 + "respected. See comments in http://hbase.apache.org/book.html#_changes_of_note");
610 }
611 int writerThreads = c.getInt(BUCKET_CACHE_WRITER_THREADS_KEY,
612 DEFAULT_BUCKET_CACHE_WRITER_THREADS);
613 int writerQueueLen = c.getInt(BUCKET_CACHE_WRITER_QUEUE_KEY,
614 DEFAULT_BUCKET_CACHE_WRITER_QUEUE);
615 String persistentPath = c.get(BUCKET_CACHE_PERSISTENT_PATH_KEY);
616 String[] configuredBucketSizes = c.getStrings(BUCKET_CACHE_BUCKETS_KEY);
617 int [] bucketSizes = null;
618 if (configuredBucketSizes != null) {
619 bucketSizes = new int[configuredBucketSizes.length];
620 for (int i = 0; i < configuredBucketSizes.length; i++) {
621 bucketSizes[i] = Integer.parseInt(configuredBucketSizes[i].trim());
622 }
623 }
624 BucketCache bucketCache = null;
625 try {
626 int ioErrorsTolerationDuration = c.getInt(
627 "hbase.bucketcache.ioengine.errors.tolerated.duration",
628 BucketCache.DEFAULT_ERROR_TOLERATION_DURATION);
629
630 bucketCache = new BucketCache(bucketCacheIOEngineName,
631 bucketCacheSize, blockSize, bucketSizes, writerThreads, writerQueueLen, persistentPath,
632 ioErrorsTolerationDuration);
633 } catch (IOException ioex) {
634 LOG.error("Can't instantiate bucket cache", ioex); throw new RuntimeException(ioex);
635 }
636 return bucketCache;
637 }
638
639
640
641
642
643
644
645
646 public static synchronized BlockCache instantiateBlockCache(Configuration conf) {
647 if (GLOBAL_BLOCK_CACHE_INSTANCE != null) return GLOBAL_BLOCK_CACHE_INSTANCE;
648 if (blockCacheDisabled) return null;
649 LruBlockCache l1 = getL1(conf);
650
651 if (blockCacheDisabled) return null;
652 BlockCache l2 = getL2(conf);
653 if (l2 == null) {
654 GLOBAL_BLOCK_CACHE_INSTANCE = l1;
655 } else {
656 boolean useExternal = conf.getBoolean(EXTERNAL_BLOCKCACHE_KEY, EXTERNAL_BLOCKCACHE_DEFAULT);
657 boolean combinedWithLru = conf.getBoolean(BUCKET_CACHE_COMBINED_KEY,
658 DEFAULT_BUCKET_CACHE_COMBINED);
659 if (useExternal) {
660 GLOBAL_BLOCK_CACHE_INSTANCE = new InclusiveCombinedBlockCache(l1, l2);
661 } else {
662 if (combinedWithLru) {
663 GLOBAL_BLOCK_CACHE_INSTANCE = new CombinedBlockCache(l1, l2);
664 } else {
665
666
667
668
669 GLOBAL_BLOCK_CACHE_INSTANCE = l1;
670 }
671 }
672 l1.setVictimCache(l2);
673 }
674 return GLOBAL_BLOCK_CACHE_INSTANCE;
675 }
676 }