View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
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.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.util.StringUtils;
36  
37  import com.google.common.annotations.VisibleForTesting;
38  
39  /**
40   * Stores all of the cache objects and configuration for a single HFile.
41   */
42  @InterfaceAudience.Private
43  public class CacheConfig {
44    private static final Log LOG = LogFactory.getLog(CacheConfig.class.getName());
45  
46    /**
47     * Configuration key to cache data blocks on write. There are separate
48     * switches for bloom blocks and non-root index blocks.
49     */
50    public static final String CACHE_BLOCKS_ON_WRITE_KEY =
51        "hbase.rs.cacheblocksonwrite";
52  
53    /**
54     * Configuration key to cache leaf and intermediate-level index blocks on
55     * write.
56     */
57    public static final String CACHE_INDEX_BLOCKS_ON_WRITE_KEY =
58        "hfile.block.index.cacheonwrite";
59  
60    /**
61     * Configuration key to cache compound bloom filter blocks on write.
62     */
63    public static final String CACHE_BLOOM_BLOCKS_ON_WRITE_KEY =
64        "hfile.block.bloom.cacheonwrite";
65  
66    /**
67     * Configuration key to cache data blocks in compressed and/or encrypted format.
68     */
69    public static final String CACHE_DATA_BLOCKS_COMPRESSED_KEY =
70        "hbase.block.data.cachecompressed";
71  
72    /**
73     * Configuration key to evict all blocks of a given file from the block cache
74     * when the file is closed.
75     */
76    public static final String EVICT_BLOCKS_ON_CLOSE_KEY =
77        "hbase.rs.evictblocksonclose";
78  
79    /**
80     * Configuration keys for Bucket cache
81     */
82  
83    /**
84     * If the chosen ioengine can persist its state across restarts, the path to the file to
85     * persist to.
86     */
87    public static final String BUCKET_CACHE_PERSISTENT_PATH_KEY = 
88        "hbase.bucketcache.persistent.path";
89  
90    /**
91     * If the bucket cache is used in league with the lru on-heap block cache (meta blocks such
92     * as indices and blooms are kept in the lru blockcache and the data blocks in the
93     * bucket cache).
94     */
95    public static final String BUCKET_CACHE_COMBINED_KEY = 
96        "hbase.bucketcache.combinedcache.enabled";
97  
98    public static final String BUCKET_CACHE_WRITER_THREADS_KEY = "hbase.bucketcache.writer.threads";
99    public static final String BUCKET_CACHE_WRITER_QUEUE_KEY = 
100       "hbase.bucketcache.writer.queuelength";
101 
102   /**
103    * A comma-delimited array of values for use as bucket sizes.
104    */
105   public static final String BUCKET_CACHE_BUCKETS_KEY = "hbase.bucketcache.bucket.sizes";
106 
107   /**
108    * Defaults for Bucket cache
109    */
110   public static final boolean DEFAULT_BUCKET_CACHE_COMBINED = true;
111   public static final int DEFAULT_BUCKET_CACHE_WRITER_THREADS = 3;
112   public static final int DEFAULT_BUCKET_CACHE_WRITER_QUEUE = 64;
113 
114  /**
115    * Configuration key to prefetch all blocks of a given file into the block cache
116    * when the file is opened.
117    */
118   public static final String PREFETCH_BLOCKS_ON_OPEN_KEY =
119       "hbase.rs.prefetchblocksonopen";
120 
121   /**
122    * The target block size used by blockcache instances. Defaults to
123    * {@link HConstants#DEFAULT_BLOCKSIZE}.
124    * TODO: this config point is completely wrong, as it's used to determine the
125    * target block size of BlockCache instances. Rename.
126    */
127   public static final String BLOCKCACHE_BLOCKSIZE_KEY = "hbase.offheapcache.minblocksize";
128 
129   // Defaults
130 
131   public static final boolean DEFAULT_CACHE_DATA_ON_READ = true;
132   public static final boolean DEFAULT_CACHE_DATA_ON_WRITE = false;
133   public static final boolean DEFAULT_IN_MEMORY = false;
134   public static final boolean DEFAULT_CACHE_INDEXES_ON_WRITE = false;
135   public static final boolean DEFAULT_CACHE_BLOOMS_ON_WRITE = false;
136   public static final boolean DEFAULT_EVICT_ON_CLOSE = false;
137   public static final boolean DEFAULT_CACHE_DATA_COMPRESSED = false;
138   public static final boolean DEFAULT_PREFETCH_ON_OPEN = false;
139 
140   /** Local reference to the block cache, null if completely disabled */
141   private final BlockCache blockCache;
142 
143   /**
144    * Whether blocks should be cached on read (default is on if there is a
145    * cache but this can be turned off on a per-family or per-request basis).
146    * If off we will STILL cache meta blocks; i.e. INDEX and BLOOM types.
147    * This cannot be disabled.
148    */
149   private boolean cacheDataOnRead;
150 
151   /** Whether blocks should be flagged as in-memory when being cached */
152   private final boolean inMemory;
153 
154   /** Whether data blocks should be cached when new files are written */
155   private boolean cacheDataOnWrite;
156 
157   /** Whether index blocks should be cached when new files are written */
158   private final boolean cacheIndexesOnWrite;
159 
160   /** Whether compound bloom filter blocks should be cached on write */
161   private final boolean cacheBloomsOnWrite;
162 
163   /** Whether blocks of a file should be evicted when the file is closed */
164   private boolean evictOnClose;
165 
166   /** Whether data blocks should be stored in compressed and/or encrypted form in the cache */
167   private final boolean cacheDataCompressed;
168 
169   /** Whether data blocks should be prefetched into the cache */
170   private final boolean prefetchOnOpen;
171 
172   /**
173    * If true and if more than one tier in this cache deploy -- e.g. CombinedBlockCache has an L1
174    * and an L2 tier -- then cache data blocks up in the L1 tier (The meta blocks are likely being
175    * cached up in L1 already.  At least this is the case if CombinedBlockCache).
176    */
177   private boolean cacheDataInL1;
178 
179   /**
180    * Create a cache configuration using the specified configuration object and
181    * family descriptor.
182    * @param conf hbase configuration
183    * @param family column family configuration
184    */
185   public CacheConfig(Configuration conf, HColumnDescriptor family) {
186     this(CacheConfig.instantiateBlockCache(conf),
187         family.isBlockCacheEnabled(),
188         family.isInMemory(),
189         // For the following flags we enable them regardless of per-schema settings
190         // if they are enabled in the global configuration.
191         conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY,
192             DEFAULT_CACHE_DATA_ON_WRITE) || family.shouldCacheDataOnWrite(),
193         conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY,
194             DEFAULT_CACHE_INDEXES_ON_WRITE) || family.shouldCacheIndexesOnWrite(),
195         conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY,
196             DEFAULT_CACHE_BLOOMS_ON_WRITE) || family.shouldCacheBloomsOnWrite(),
197         conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY,
198             DEFAULT_EVICT_ON_CLOSE) || family.shouldEvictBlocksOnClose(),
199         conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
200         conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY,
201             DEFAULT_PREFETCH_ON_OPEN) || family.shouldPrefetchBlocksOnOpen(),
202         conf.getBoolean(HColumnDescriptor.CACHE_DATA_IN_L1,
203             HColumnDescriptor.DEFAULT_CACHE_DATA_IN_L1) || family.shouldCacheDataInL1()
204      );
205   }
206 
207   /**
208    * Create a cache configuration using the specified configuration object and
209    * defaults for family level settings.
210    * @param conf hbase configuration
211    */
212   public CacheConfig(Configuration conf) {
213     this(CacheConfig.instantiateBlockCache(conf),
214         DEFAULT_CACHE_DATA_ON_READ,
215         DEFAULT_IN_MEMORY, // This is a family-level setting so can't be set
216                            // strictly from conf
217         conf.getBoolean(CACHE_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_DATA_ON_WRITE),
218         conf.getBoolean(CACHE_INDEX_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_INDEXES_ON_WRITE),
219         conf.getBoolean(CACHE_BLOOM_BLOCKS_ON_WRITE_KEY, DEFAULT_CACHE_BLOOMS_ON_WRITE),
220         conf.getBoolean(EVICT_BLOCKS_ON_CLOSE_KEY, DEFAULT_EVICT_ON_CLOSE),
221         conf.getBoolean(CACHE_DATA_BLOCKS_COMPRESSED_KEY, DEFAULT_CACHE_DATA_COMPRESSED),
222         conf.getBoolean(PREFETCH_BLOCKS_ON_OPEN_KEY, DEFAULT_PREFETCH_ON_OPEN),
223         conf.getBoolean(HColumnDescriptor.CACHE_DATA_IN_L1,
224           HColumnDescriptor.DEFAULT_CACHE_DATA_IN_L1)
225      );
226   }
227 
228   /**
229    * Create a block cache configuration with the specified cache and
230    * configuration parameters.
231    * @param blockCache reference to block cache, null if completely disabled
232    * @param cacheDataOnRead whether DATA blocks should be cached on read (we always cache INDEX
233    * blocks and BLOOM blocks; this cannot be disabled).
234    * @param inMemory whether blocks should be flagged as in-memory
235    * @param cacheDataOnWrite whether data blocks should be cached on write
236    * @param cacheIndexesOnWrite whether index blocks should be cached on write
237    * @param cacheBloomsOnWrite whether blooms should be cached on write
238    * @param evictOnClose whether blocks should be evicted when HFile is closed
239    * @param cacheDataCompressed whether to store blocks as compressed in the cache
240    * @param prefetchOnOpen whether to prefetch blocks upon open
241    * @param cacheDataInL1 If more than one cache tier deployed, if true, cache this column families
242    * data blocks up in the L1 tier.
243    */
244   CacheConfig(final BlockCache blockCache,
245       final boolean cacheDataOnRead, final boolean inMemory,
246       final boolean cacheDataOnWrite, final boolean cacheIndexesOnWrite,
247       final boolean cacheBloomsOnWrite, final boolean evictOnClose,
248       final boolean cacheDataCompressed, final boolean prefetchOnOpen,
249       final boolean cacheDataInL1) {
250     this.blockCache = blockCache;
251     this.cacheDataOnRead = cacheDataOnRead;
252     this.inMemory = inMemory;
253     this.cacheDataOnWrite = cacheDataOnWrite;
254     this.cacheIndexesOnWrite = cacheIndexesOnWrite;
255     this.cacheBloomsOnWrite = cacheBloomsOnWrite;
256     this.evictOnClose = evictOnClose;
257     this.cacheDataCompressed = cacheDataCompressed;
258     this.prefetchOnOpen = prefetchOnOpen;
259     this.cacheDataInL1 = cacheDataInL1;
260     LOG.info(this);
261   }
262 
263   /**
264    * Constructs a cache configuration copied from the specified configuration.
265    * @param cacheConf
266    */
267   public CacheConfig(CacheConfig cacheConf) {
268     this(cacheConf.blockCache, cacheConf.cacheDataOnRead, cacheConf.inMemory,
269         cacheConf.cacheDataOnWrite, cacheConf.cacheIndexesOnWrite,
270         cacheConf.cacheBloomsOnWrite, cacheConf.evictOnClose,
271         cacheConf.cacheDataCompressed, cacheConf.prefetchOnOpen,
272         cacheConf.cacheDataInL1);
273   }
274 
275   /**
276    * Checks whether the block cache is enabled.
277    */
278   public boolean isBlockCacheEnabled() {
279     return this.blockCache != null;
280   }
281 
282   /**
283    * Returns the block cache.
284    * @return the block cache, or null if caching is completely disabled
285    */
286   public BlockCache getBlockCache() {
287     return this.blockCache;
288   }
289 
290   /**
291    * Returns whether the DATA blocks of this HFile should be cached on read or not (we always
292    * cache the meta blocks, the INDEX and BLOOM blocks).
293    * @return true if blocks should be cached on read, false if not
294    */
295   public boolean shouldCacheDataOnRead() {
296     return isBlockCacheEnabled() && cacheDataOnRead;
297   }
298 
299   /**
300    * Should we cache a block of a particular category? We always cache
301    * important blocks such as index blocks, as long as the block cache is
302    * available.
303    */
304   public boolean shouldCacheBlockOnRead(BlockCategory category) {
305     return isBlockCacheEnabled()
306         && (cacheDataOnRead ||
307             category == BlockCategory.INDEX ||
308             category == BlockCategory.BLOOM ||
309             (prefetchOnOpen &&
310                 (category != BlockCategory.META &&
311                  category != BlockCategory.UNKNOWN)));
312   }
313 
314   /**
315    * @return true if blocks in this file should be flagged as in-memory
316    */
317   public boolean isInMemory() {
318     return isBlockCacheEnabled() && this.inMemory;
319   }
320 
321   /**
322    * @return True if cache data blocks in L1 tier (if more than one tier in block cache deploy).
323    */
324   public boolean isCacheDataInL1() {
325     return isBlockCacheEnabled() && this.cacheDataInL1;
326   }
327 
328   /**
329    * @return true if data blocks should be written to the cache when an HFile is
330    *         written, false if not
331    */
332   public boolean shouldCacheDataOnWrite() {
333     return isBlockCacheEnabled() && this.cacheDataOnWrite;
334   }
335 
336   /**
337    * Only used for testing.
338    * @param cacheDataOnWrite whether data blocks should be written to the cache
339    *                         when an HFile is written
340    */
341   @VisibleForTesting
342   public void setCacheDataOnWrite(boolean cacheDataOnWrite) {
343     this.cacheDataOnWrite = cacheDataOnWrite;
344   }
345 
346   /**
347    * Only used for testing.
348    * @param cacheDataInL1 Whether to cache data blocks up in l1 (if a multi-tier cache
349    * implementation).
350    */
351   @VisibleForTesting
352   public void setCacheDataInL1(boolean cacheDataInL1) {
353     this.cacheDataInL1 = cacheDataInL1;
354   }
355 
356   /**
357    * @return true if index blocks should be written to the cache when an HFile
358    *         is written, false if not
359    */
360   public boolean shouldCacheIndexesOnWrite() {
361     return isBlockCacheEnabled() && this.cacheIndexesOnWrite;
362   }
363 
364   /**
365    * @return true if bloom blocks should be written to the cache when an HFile
366    *         is written, false if not
367    */
368   public boolean shouldCacheBloomsOnWrite() {
369     return isBlockCacheEnabled() && this.cacheBloomsOnWrite;
370   }
371 
372   /**
373    * @return true if blocks should be evicted from the cache when an HFile
374    *         reader is closed, false if not
375    */
376   public boolean shouldEvictOnClose() {
377     return isBlockCacheEnabled() && this.evictOnClose;
378   }
379 
380   /**
381    * Only used for testing.
382    * @param evictOnClose whether blocks should be evicted from the cache when an
383    *                     HFile reader is closed
384    */
385   public void setEvictOnClose(boolean evictOnClose) {
386     this.evictOnClose = evictOnClose;
387   }
388 
389   /**
390    * @return true if data blocks should be compressed in the cache, false if not
391    */
392   public boolean shouldCacheDataCompressed() {
393     return isBlockCacheEnabled() && this.cacheDataCompressed;
394   }
395 
396   /**
397    * @return true if this {@link BlockCategory} should be compressed in blockcache, false otherwise
398    */
399   public boolean shouldCacheCompressed(BlockCategory category) {
400     if (!isBlockCacheEnabled()) return false;
401     switch (category) {
402       case DATA:
403         return this.cacheDataCompressed;
404       default:
405         return false;
406     }
407   }
408 
409   /**
410    * @return true if blocks should be prefetched into the cache on open, false if not
411    */
412   public boolean shouldPrefetchOnOpen() {
413     return isBlockCacheEnabled() && this.prefetchOnOpen;
414   }
415 
416   @Override
417   public String toString() {
418     if (!isBlockCacheEnabled()) {
419       return "CacheConfig:disabled";
420     }
421     return "blockCache=" + getBlockCache() +
422       ", cacheDataOnRead=" + shouldCacheDataOnRead() +
423       ", cacheDataOnWrite=" + shouldCacheDataOnWrite() +
424       ", cacheIndexesOnWrite=" + shouldCacheIndexesOnWrite() +
425       ", cacheBloomsOnWrite=" + shouldCacheBloomsOnWrite() +
426       ", cacheEvictOnClose=" + shouldEvictOnClose() +
427       ", cacheDataCompressed=" + shouldCacheDataCompressed() +
428       ", prefetchOnOpen=" + shouldPrefetchOnOpen();
429   }
430 
431   // Static block cache reference and methods
432 
433   /**
434    * Static reference to the block cache, or null if no caching should be used
435    * at all.
436    */
437   // Clear this if in tests you'd make more than one block cache instance.
438   @VisibleForTesting
439   static BlockCache GLOBAL_BLOCK_CACHE_INSTANCE;
440 
441   /** Boolean whether we have disabled the block cache entirely. */
442   @VisibleForTesting
443   static boolean blockCacheDisabled = false;
444 
445   static long getLruCacheSize(final Configuration conf, final MemoryUsage mu) {
446     float cachePercentage = conf.getFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY,
447       HConstants.HFILE_BLOCK_CACHE_SIZE_DEFAULT);
448     if (cachePercentage <= 0.0001f) {
449       blockCacheDisabled = true;
450       return -1;
451     }
452     if (cachePercentage > 1.0) {
453       throw new IllegalArgumentException(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY +
454         " must be between 0.0 and 1.0, and not > 1.0");
455     }
456 
457     // Calculate the amount of heap to give the heap.
458     return (long) (mu.getMax() * cachePercentage);
459   }
460 
461   /**
462    * @param c Configuration to use.
463    * @param mu JMX Memory Bean
464    * @return An L1 instance.  Currently an instance of LruBlockCache.
465    */
466   private static LruBlockCache getL1(final Configuration c, final MemoryUsage mu) {
467     long lruCacheSize = getLruCacheSize(c, mu);
468     if (lruCacheSize < 0) return null;
469     int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
470     LOG.info("Allocating LruBlockCache size=" +
471       StringUtils.byteDesc(lruCacheSize) + ", blockSize=" + StringUtils.byteDesc(blockSize));
472     return new LruBlockCache(lruCacheSize, blockSize, true, c);
473   }
474 
475   /**
476    * @param c Configuration to use.
477    * @param mu JMX Memory Bean
478    * @return Returns L2 block cache instance (for now it is BucketCache BlockCache all the time)
479    * or null if not supposed to be a L2.
480    */
481   private static BucketCache getL2(final Configuration c, final MemoryUsage mu) {
482     // Check for L2.  ioengine name must be non-null.
483     String bucketCacheIOEngineName = c.get(BUCKET_CACHE_IOENGINE_KEY, null);
484     if (bucketCacheIOEngineName == null || bucketCacheIOEngineName.length() <= 0) return null;
485 
486     int blockSize = c.getInt(BLOCKCACHE_BLOCKSIZE_KEY, HConstants.DEFAULT_BLOCKSIZE);
487     float bucketCachePercentage = c.getFloat(BUCKET_CACHE_SIZE_KEY, 0F);
488     long bucketCacheSize = (long) (bucketCachePercentage < 1? mu.getMax() * bucketCachePercentage:
489       bucketCachePercentage * 1024 * 1024);
490     if (bucketCacheSize <= 0) {
491       throw new IllegalStateException("bucketCacheSize <= 0; Check " +
492         BUCKET_CACHE_SIZE_KEY + " setting and/or server java heap size");
493     }
494     int writerThreads = c.getInt(BUCKET_CACHE_WRITER_THREADS_KEY,
495       DEFAULT_BUCKET_CACHE_WRITER_THREADS);
496     int writerQueueLen = c.getInt(BUCKET_CACHE_WRITER_QUEUE_KEY,
497       DEFAULT_BUCKET_CACHE_WRITER_QUEUE);
498     String persistentPath = c.get(BUCKET_CACHE_PERSISTENT_PATH_KEY);
499     String[] configuredBucketSizes = c.getStrings(BUCKET_CACHE_BUCKETS_KEY);
500     int [] bucketSizes = null;
501     if (configuredBucketSizes != null) {
502       bucketSizes = new int[configuredBucketSizes.length];
503       for (int i = 0; i < configuredBucketSizes.length; i++) {
504         bucketSizes[i] = Integer.parseInt(configuredBucketSizes[i].trim());
505       }
506     }
507     BucketCache bucketCache = null;
508     try {
509       int ioErrorsTolerationDuration = c.getInt(
510         "hbase.bucketcache.ioengine.errors.tolerated.duration",
511         BucketCache.DEFAULT_ERROR_TOLERATION_DURATION);
512       // Bucket cache logs its stats on creation internal to the constructor.
513       bucketCache = new BucketCache(bucketCacheIOEngineName,
514         bucketCacheSize, blockSize, bucketSizes, writerThreads, writerQueueLen, persistentPath,
515         ioErrorsTolerationDuration);
516     } catch (IOException ioex) {
517       LOG.error("Can't instantiate bucket cache", ioex); throw new RuntimeException(ioex);
518     }
519     return bucketCache;
520   }
521 
522   /**
523    * Returns the block cache or <code>null</code> in case none should be used.
524    * Sets GLOBAL_BLOCK_CACHE_INSTANCE
525    *
526    * @param conf  The current configuration.
527    * @return The block cache or <code>null</code>.
528    */
529   public static synchronized BlockCache instantiateBlockCache(Configuration conf) {
530     if (GLOBAL_BLOCK_CACHE_INSTANCE != null) return GLOBAL_BLOCK_CACHE_INSTANCE;
531     if (blockCacheDisabled) return null;
532     MemoryUsage mu = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
533     LruBlockCache l1 = getL1(conf, mu);
534     // blockCacheDisabled is set as a side-effect of getL1(), so check it again after the call.
535     if (blockCacheDisabled) return null;
536     BucketCache l2 = getL2(conf, mu);
537     if (l2 == null) {
538       GLOBAL_BLOCK_CACHE_INSTANCE = l1;
539     } else {
540       boolean combinedWithLru = conf.getBoolean(BUCKET_CACHE_COMBINED_KEY,
541         DEFAULT_BUCKET_CACHE_COMBINED);
542       if (combinedWithLru) {
543         GLOBAL_BLOCK_CACHE_INSTANCE = new CombinedBlockCache(l1, l2);
544       } else {
545         // L1 and L2 are not 'combined'.  They are connected via the LruBlockCache victimhandler
546         // mechanism.  It is a little ugly but works according to the following: when the
547         // background eviction thread runs, blocks evicted from L1 will go to L2 AND when we get
548         // a block from the L1 cache, if not in L1, we will search L2.
549         l1.setVictimCache(l2);
550         GLOBAL_BLOCK_CACHE_INSTANCE = l1;
551       }
552     }
553     return GLOBAL_BLOCK_CACHE_INSTANCE;
554   }
555 }