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