View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.io.hfile;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.nio.ByteBuffer;
25  import java.util.Collection;
26  import java.util.Map;
27  import java.util.Random;
28  
29  import org.apache.hadoop.hbase.HBaseTestingUtility;
30  import org.apache.hadoop.hbase.SmallTests;
31  import org.apache.hadoop.hbase.io.HeapSize;
32  import org.apache.hadoop.hbase.io.hfile.LruBlockCache.EvictionThread;
33  import org.apache.hadoop.hbase.util.ClassSize;
34  import org.junit.After;
35  import org.junit.Before;
36  import org.junit.Test;
37  import org.junit.experimental.categories.Category;
38  import org.junit.runner.RunWith;
39  import org.junit.runners.Parameterized;
40  import org.junit.runners.Parameterized.Parameters;
41  
42  /**
43   * Tests the concurrent LruBlockCache.<p>
44   *
45   * Tests will ensure it grows and shrinks in size properly,
46   * evictions run when they're supposed to and do what they should,
47   * and that cached blocks are accessible when expected to be.
48   */
49  @Category(SmallTests.class)
50  public class TestLruBlockCache {
51  
52  
53    @Test
54    public void testBackgroundEvictionThread() throws Exception {
55      long maxSize = 100000;
56      long blockSize = calculateBlockSizeDefault(maxSize, 9); // room for 9, will evict
57  
58      LruBlockCache cache = new LruBlockCache(maxSize,blockSize);
59  
60      CachedItem [] blocks = generateFixedBlocks(10, blockSize, "block");
61  
62      EvictionThread evictionThread = cache.getEvictionThread();
63      assertTrue(evictionThread != null);
64  
65      // Make sure eviction thread has entered run method
66      while (!evictionThread.isEnteringRun()) {
67        Thread.sleep(1);
68      }
69      
70  
71      // Add all the blocks
72      for (CachedItem block : blocks) {
73        cache.cacheBlock(block.cacheKey, block);
74      }
75  
76      // Let the eviction run
77      int n = 0;
78      while(cache.getEvictionCount() == 0) {
79        Thread.sleep(200);
80        assertTrue(n++ < 20);
81      }
82      System.out.println("Background Evictions run: " + cache.getEvictionCount());
83  
84      // A single eviction run should have occurred
85      assertEquals(cache.getEvictionCount(), 1);
86    }
87  
88    @Test
89    public void testCacheSimple() throws Exception {
90  
91      long maxSize = 1000000;
92      long blockSize = calculateBlockSizeDefault(maxSize, 101);
93  
94      LruBlockCache cache = new LruBlockCache(maxSize, blockSize);
95  
96      CachedItem [] blocks = generateRandomBlocks(100, blockSize);
97  
98      long expectedCacheSize = cache.heapSize();
99  
100     // Confirm empty
101     for (CachedItem block : blocks) {
102       assertTrue(cache.getBlock(block.cacheKey, true, false) == null);
103     }
104 
105     // Add blocks
106     for (CachedItem block : blocks) {
107       cache.cacheBlock(block.cacheKey, block);
108       expectedCacheSize += block.cacheBlockHeapSize();
109     }
110 
111     // Verify correctly calculated cache heap size
112     assertEquals(expectedCacheSize, cache.heapSize());
113 
114     // Check if all blocks are properly cached and retrieved
115     for (CachedItem block : blocks) {
116       HeapSize buf = cache.getBlock(block.cacheKey, true, false);
117       assertTrue(buf != null);
118       assertEquals(buf.heapSize(), block.heapSize());
119     }
120 
121     // Verify correctly calculated cache heap size
122     assertEquals(expectedCacheSize, cache.heapSize());
123 
124     // Check if all blocks are properly cached and retrieved
125     for (CachedItem block : blocks) {
126       HeapSize buf = cache.getBlock(block.cacheKey, true, false);
127       assertTrue(buf != null);
128       assertEquals(buf.heapSize(), block.heapSize());
129     }
130 
131     // Expect no evictions
132     assertEquals(0, cache.getEvictionCount());
133     Thread t = new LruBlockCache.StatisticsThread(cache);
134     t.start();
135     t.join();
136   }
137 
138   @Test
139   public void testCacheEvictionSimple() throws Exception {
140 
141     long maxSize = 100000;
142     long blockSize = calculateBlockSizeDefault(maxSize, 10);
143 
144     LruBlockCache cache = new LruBlockCache(maxSize,blockSize,false);
145 
146     CachedItem [] blocks = generateFixedBlocks(10, blockSize, "block");
147 
148     long expectedCacheSize = cache.heapSize();
149 
150     // Add all the blocks
151     for (CachedItem block : blocks) {
152       cache.cacheBlock(block.cacheKey, block);
153       expectedCacheSize += block.cacheBlockHeapSize();
154     }
155 
156     // A single eviction run should have occurred
157     assertEquals(1, cache.getEvictionCount());
158 
159     // Our expected size overruns acceptable limit
160     assertTrue(expectedCacheSize >
161       (maxSize * LruBlockCache.DEFAULT_ACCEPTABLE_FACTOR));
162 
163     // But the cache did not grow beyond max
164     assertTrue(cache.heapSize() < maxSize);
165 
166     // And is still below the acceptable limit
167     assertTrue(cache.heapSize() <
168         (maxSize * LruBlockCache.DEFAULT_ACCEPTABLE_FACTOR));
169 
170     // All blocks except block 0  should be in the cache
171     assertTrue(cache.getBlock(blocks[0].cacheKey, true, false) == null);
172     for(int i=1;i<blocks.length;i++) {
173       assertEquals(cache.getBlock(blocks[i].cacheKey, true, false),
174           blocks[i]);
175     }
176   }
177 
178   @Test
179   public void testCacheEvictionTwoPriorities() throws Exception {
180 
181     long maxSize = 100000;
182     long blockSize = calculateBlockSizeDefault(maxSize, 10);
183 
184     LruBlockCache cache = new LruBlockCache(maxSize,blockSize,false);
185 
186     CachedItem [] singleBlocks = generateFixedBlocks(5, 10000, "single");
187     CachedItem [] multiBlocks = generateFixedBlocks(5, 10000, "multi");
188 
189     long expectedCacheSize = cache.heapSize();
190 
191     // Add and get the multi blocks
192     for (CachedItem block : multiBlocks) {
193       cache.cacheBlock(block.cacheKey, block);
194       expectedCacheSize += block.cacheBlockHeapSize();
195       assertEquals(cache.getBlock(block.cacheKey, true, false), block);
196     }
197 
198     // Add the single blocks (no get)
199     for (CachedItem block : singleBlocks) {
200       cache.cacheBlock(block.cacheKey, block);
201       expectedCacheSize += block.heapSize();
202     }
203 
204     // A single eviction run should have occurred
205     assertEquals(cache.getEvictionCount(), 1);
206 
207     // We expect two entries evicted
208     assertEquals(cache.getEvictedCount(), 2);
209 
210     // Our expected size overruns acceptable limit
211     assertTrue(expectedCacheSize >
212       (maxSize * LruBlockCache.DEFAULT_ACCEPTABLE_FACTOR));
213 
214     // But the cache did not grow beyond max
215     assertTrue(cache.heapSize() <= maxSize);
216 
217     // And is now below the acceptable limit
218     assertTrue(cache.heapSize() <=
219         (maxSize * LruBlockCache.DEFAULT_ACCEPTABLE_FACTOR));
220 
221     // We expect fairness across the two priorities.
222     // This test makes multi go barely over its limit, in-memory
223     // empty, and the rest in single.  Two single evictions and
224     // one multi eviction expected.
225     assertTrue(cache.getBlock(singleBlocks[0].cacheKey, true, false) == null);
226     assertTrue(cache.getBlock(multiBlocks[0].cacheKey, true, false) == null);
227 
228     // And all others to be cached
229     for(int i=1;i<4;i++) {
230       assertEquals(cache.getBlock(singleBlocks[i].cacheKey, true, false),
231           singleBlocks[i]);
232       assertEquals(cache.getBlock(multiBlocks[i].cacheKey, true, false),
233           multiBlocks[i]);
234     }
235   }
236 
237   @Test
238   public void testCacheEvictionThreePriorities() throws Exception {
239 
240     long maxSize = 100000;
241     long blockSize = calculateBlockSize(maxSize, 10);
242 
243     LruBlockCache cache = new LruBlockCache(maxSize, blockSize, false,
244         (int)Math.ceil(1.2*maxSize/blockSize),
245         LruBlockCache.DEFAULT_LOAD_FACTOR,
246         LruBlockCache.DEFAULT_CONCURRENCY_LEVEL,
247         0.98f, // min
248         0.99f, // acceptable
249         0.33f, // single
250         0.33f, // multi
251         0.34f);// memory
252 
253 
254     CachedItem [] singleBlocks = generateFixedBlocks(5, blockSize, "single");
255     CachedItem [] multiBlocks = generateFixedBlocks(5, blockSize, "multi");
256     CachedItem [] memoryBlocks = generateFixedBlocks(5, blockSize, "memory");
257 
258     long expectedCacheSize = cache.heapSize();
259 
260     // Add 3 blocks from each priority
261     for(int i=0;i<3;i++) {
262 
263       // Just add single blocks
264       cache.cacheBlock(singleBlocks[i].cacheKey, singleBlocks[i]);
265       expectedCacheSize += singleBlocks[i].cacheBlockHeapSize();
266 
267       // Add and get multi blocks
268       cache.cacheBlock(multiBlocks[i].cacheKey, multiBlocks[i]);
269       expectedCacheSize += multiBlocks[i].cacheBlockHeapSize();
270       cache.getBlock(multiBlocks[i].cacheKey, true, false);
271 
272       // Add memory blocks as such
273       cache.cacheBlock(memoryBlocks[i].cacheKey, memoryBlocks[i], true);
274       expectedCacheSize += memoryBlocks[i].cacheBlockHeapSize();
275 
276     }
277 
278     // Do not expect any evictions yet
279     assertEquals(0, cache.getEvictionCount());
280 
281     // Verify cache size
282     assertEquals(expectedCacheSize, cache.heapSize());
283 
284     // Insert a single block, oldest single should be evicted
285     cache.cacheBlock(singleBlocks[3].cacheKey, singleBlocks[3]);
286 
287     // Single eviction, one thing evicted
288     assertEquals(1, cache.getEvictionCount());
289     assertEquals(1, cache.getEvictedCount());
290 
291     // Verify oldest single block is the one evicted
292     assertEquals(null, cache.getBlock(singleBlocks[0].cacheKey, true, false));
293 
294     // Change the oldest remaining single block to a multi
295     cache.getBlock(singleBlocks[1].cacheKey, true, false);
296 
297     // Insert another single block
298     cache.cacheBlock(singleBlocks[4].cacheKey, singleBlocks[4]);
299 
300     // Two evictions, two evicted.
301     assertEquals(2, cache.getEvictionCount());
302     assertEquals(2, cache.getEvictedCount());
303 
304     // Oldest multi block should be evicted now
305     assertEquals(null, cache.getBlock(multiBlocks[0].cacheKey, true, false));
306 
307     // Insert another memory block
308     cache.cacheBlock(memoryBlocks[3].cacheKey, memoryBlocks[3], true);
309 
310     // Three evictions, three evicted.
311     assertEquals(3, cache.getEvictionCount());
312     assertEquals(3, cache.getEvictedCount());
313 
314     // Oldest memory block should be evicted now
315     assertEquals(null, cache.getBlock(memoryBlocks[0].cacheKey, true, false));
316 
317     // Add a block that is twice as big (should force two evictions)
318     CachedItem [] bigBlocks = generateFixedBlocks(3, blockSize*3, "big");
319     cache.cacheBlock(bigBlocks[0].cacheKey, bigBlocks[0]);
320 
321     // Four evictions, six evicted (inserted block 3X size, expect +3 evicted)
322     assertEquals(4, cache.getEvictionCount());
323     assertEquals(6, cache.getEvictedCount());
324 
325     // Expect three remaining singles to be evicted
326     assertEquals(null, cache.getBlock(singleBlocks[2].cacheKey, true, false));
327     assertEquals(null, cache.getBlock(singleBlocks[3].cacheKey, true, false));
328     assertEquals(null, cache.getBlock(singleBlocks[4].cacheKey, true, false));
329 
330     // Make the big block a multi block
331     cache.getBlock(bigBlocks[0].cacheKey, true, false);
332 
333     // Cache another single big block
334     cache.cacheBlock(bigBlocks[1].cacheKey, bigBlocks[1]);
335 
336     // Five evictions, nine evicted (3 new)
337     assertEquals(5, cache.getEvictionCount());
338     assertEquals(9, cache.getEvictedCount());
339 
340     // Expect three remaining multis to be evicted
341     assertEquals(null, cache.getBlock(singleBlocks[1].cacheKey, true, false));
342     assertEquals(null, cache.getBlock(multiBlocks[1].cacheKey, true, false));
343     assertEquals(null, cache.getBlock(multiBlocks[2].cacheKey, true, false));
344 
345     // Cache a big memory block
346     cache.cacheBlock(bigBlocks[2].cacheKey, bigBlocks[2], true);
347 
348     // Six evictions, twelve evicted (3 new)
349     assertEquals(6, cache.getEvictionCount());
350     assertEquals(12, cache.getEvictedCount());
351 
352     // Expect three remaining in-memory to be evicted
353     assertEquals(null, cache.getBlock(memoryBlocks[1].cacheKey, true, false));
354     assertEquals(null, cache.getBlock(memoryBlocks[2].cacheKey, true, false));
355     assertEquals(null, cache.getBlock(memoryBlocks[3].cacheKey, true, false));
356 
357 
358   }
359 
360   // test scan resistance
361   @Test
362   public void testScanResistance() throws Exception {
363 
364     long maxSize = 100000;
365     long blockSize = calculateBlockSize(maxSize, 10);
366 
367     LruBlockCache cache = new LruBlockCache(maxSize, blockSize, false,
368         (int)Math.ceil(1.2*maxSize/blockSize),
369         LruBlockCache.DEFAULT_LOAD_FACTOR,
370         LruBlockCache.DEFAULT_CONCURRENCY_LEVEL,
371         0.66f, // min
372         0.99f, // acceptable
373         0.33f, // single
374         0.33f, // multi
375         0.34f);// memory
376 
377     CachedItem [] singleBlocks = generateFixedBlocks(20, blockSize, "single");
378     CachedItem [] multiBlocks = generateFixedBlocks(5, blockSize, "multi");
379 
380     // Add 5 multi blocks
381     for (CachedItem block : multiBlocks) {
382       cache.cacheBlock(block.cacheKey, block);
383       cache.getBlock(block.cacheKey, true, false);
384     }
385 
386     // Add 5 single blocks
387     for(int i=0;i<5;i++) {
388       cache.cacheBlock(singleBlocks[i].cacheKey, singleBlocks[i]);
389     }
390 
391     // An eviction ran
392     assertEquals(1, cache.getEvictionCount());
393 
394     // To drop down to 2/3 capacity, we'll need to evict 4 blocks
395     assertEquals(4, cache.getEvictedCount());
396 
397     // Should have been taken off equally from single and multi
398     assertEquals(null, cache.getBlock(singleBlocks[0].cacheKey, true, false));
399     assertEquals(null, cache.getBlock(singleBlocks[1].cacheKey, true, false));
400     assertEquals(null, cache.getBlock(multiBlocks[0].cacheKey, true, false));
401     assertEquals(null, cache.getBlock(multiBlocks[1].cacheKey, true, false));
402 
403     // Let's keep "scanning" by adding single blocks.  From here on we only
404     // expect evictions from the single bucket.
405 
406     // Every time we reach 10 total blocks (every 4 inserts) we get 4 single
407     // blocks evicted.  Inserting 13 blocks should yield 3 more evictions and
408     // 12 more evicted.
409 
410     for(int i=5;i<18;i++) {
411       cache.cacheBlock(singleBlocks[i].cacheKey, singleBlocks[i]);
412     }
413 
414     // 4 total evictions, 16 total evicted
415     assertEquals(4, cache.getEvictionCount());
416     assertEquals(16, cache.getEvictedCount());
417 
418     // Should now have 7 total blocks
419     assertEquals(7, cache.size());
420 
421   }
422 
423   // test setMaxSize
424   @Test
425   public void testResizeBlockCache() throws Exception {
426 
427     long maxSize = 300000;
428     long blockSize = calculateBlockSize(maxSize, 31);
429 
430     LruBlockCache cache = new LruBlockCache(maxSize, blockSize, false,
431         (int)Math.ceil(1.2*maxSize/blockSize),
432         LruBlockCache.DEFAULT_LOAD_FACTOR,
433         LruBlockCache.DEFAULT_CONCURRENCY_LEVEL,
434         0.98f, // min
435         0.99f, // acceptable
436         0.33f, // single
437         0.33f, // multi
438         0.34f);// memory
439 
440     CachedItem [] singleBlocks = generateFixedBlocks(10, blockSize, "single");
441     CachedItem [] multiBlocks = generateFixedBlocks(10, blockSize, "multi");
442     CachedItem [] memoryBlocks = generateFixedBlocks(10, blockSize, "memory");
443 
444     // Add all blocks from all priorities
445     for(int i=0;i<10;i++) {
446 
447       // Just add single blocks
448       cache.cacheBlock(singleBlocks[i].cacheKey, singleBlocks[i]);
449 
450       // Add and get multi blocks
451       cache.cacheBlock(multiBlocks[i].cacheKey, multiBlocks[i]);
452       cache.getBlock(multiBlocks[i].cacheKey, true, false);
453 
454       // Add memory blocks as such
455       cache.cacheBlock(memoryBlocks[i].cacheKey, memoryBlocks[i], true);
456     }
457 
458     // Do not expect any evictions yet
459     assertEquals(0, cache.getEvictionCount());
460 
461     // Resize to half capacity plus an extra block (otherwise we evict an extra)
462     cache.setMaxSize((long)(maxSize * 0.5f));
463 
464     // Should have run a single eviction
465     assertEquals(1, cache.getEvictionCount());
466 
467     // And we expect 1/2 of the blocks to be evicted
468     assertEquals(15, cache.getEvictedCount());
469 
470     // And the oldest 5 blocks from each category should be gone
471     for(int i=0;i<5;i++) {
472       assertEquals(null, cache.getBlock(singleBlocks[i].cacheKey, true, false));
473       assertEquals(null, cache.getBlock(multiBlocks[i].cacheKey, true, false));
474       assertEquals(null, cache.getBlock(memoryBlocks[i].cacheKey, true, false));
475     }
476 
477     // And the newest 5 blocks should still be accessible
478     for(int i=5;i<10;i++) {
479       assertEquals(singleBlocks[i], cache.getBlock(singleBlocks[i].cacheKey, true, false));
480       assertEquals(multiBlocks[i], cache.getBlock(multiBlocks[i].cacheKey, true, false));
481       assertEquals(memoryBlocks[i], cache.getBlock(memoryBlocks[i].cacheKey, true, false));
482     }
483   }
484 
485   // test metricsPastNPeriods
486   @Test
487   public void testPastNPeriodsMetrics() throws Exception {
488    double delta = 0.01;
489 
490     // 3 total periods
491     CacheStats stats = new CacheStats(3);
492 
493     // No accesses, should be 0
494     stats.rollMetricsPeriod();
495     assertEquals(0.0, stats.getHitRatioPastNPeriods(), delta);
496     assertEquals(0.0, stats.getHitCachingRatioPastNPeriods(), delta);
497 
498     // period 1, 1 hit caching, 1 hit non-caching, 2 miss non-caching
499     // should be (2/4)=0.5 and (1/1)=1
500     stats.hit(false);
501     stats.hit(true);
502     stats.miss(false);
503     stats.miss(false);
504     stats.rollMetricsPeriod();
505     assertEquals(0.5, stats.getHitRatioPastNPeriods(), delta);
506     assertEquals(1.0, stats.getHitCachingRatioPastNPeriods(), delta);
507 
508     // period 2, 1 miss caching, 3 miss non-caching
509     // should be (2/8)=0.25 and (1/2)=0.5
510     stats.miss(true);
511     stats.miss(false);
512     stats.miss(false);
513     stats.miss(false);
514     stats.rollMetricsPeriod();
515     assertEquals(0.25, stats.getHitRatioPastNPeriods(), delta);
516     assertEquals(0.5, stats.getHitCachingRatioPastNPeriods(), delta);
517 
518     // period 3, 2 hits of each type
519     // should be (6/12)=0.5 and (3/4)=0.75
520     stats.hit(false);
521     stats.hit(true);
522     stats.hit(false);
523     stats.hit(true);
524     stats.rollMetricsPeriod();
525     assertEquals(0.5, stats.getHitRatioPastNPeriods(), delta);
526     assertEquals(0.75, stats.getHitCachingRatioPastNPeriods(), delta);
527 
528     // period 4, evict period 1, two caching misses
529     // should be (4/10)=0.4 and (2/5)=0.4
530     stats.miss(true);
531     stats.miss(true);
532     stats.rollMetricsPeriod();
533     assertEquals(0.4, stats.getHitRatioPastNPeriods(), delta);
534     assertEquals(0.4, stats.getHitCachingRatioPastNPeriods(), delta);
535 
536     // period 5, evict period 2, 2 caching misses, 2 non-caching hit
537     // should be (6/10)=0.6 and (2/6)=1/3
538     stats.miss(true);
539     stats.miss(true);
540     stats.hit(false);
541     stats.hit(false);
542     stats.rollMetricsPeriod();
543     assertEquals(0.6, stats.getHitRatioPastNPeriods(), delta);
544     assertEquals((double)1/3, stats.getHitCachingRatioPastNPeriods(), delta);
545 
546     // period 6, evict period 3
547     // should be (2/6)=1/3 and (0/4)=0
548     stats.rollMetricsPeriod();
549     assertEquals((double)1/3, stats.getHitRatioPastNPeriods(), delta);
550     assertEquals(0.0, stats.getHitCachingRatioPastNPeriods(), delta);
551 
552     // period 7, evict period 4
553     // should be (2/4)=0.5 and (0/2)=0
554     stats.rollMetricsPeriod();
555     assertEquals(0.5, stats.getHitRatioPastNPeriods(), delta);
556     assertEquals(0.0, stats.getHitCachingRatioPastNPeriods(), delta);
557 
558     // period 8, evict period 5
559     // should be 0 and 0
560     stats.rollMetricsPeriod();
561     assertEquals(0.0, stats.getHitRatioPastNPeriods(), delta);
562     assertEquals(0.0, stats.getHitCachingRatioPastNPeriods(), delta);
563 
564     // period 9, one of each
565     // should be (2/4)=0.5 and (1/2)=0.5
566     stats.miss(true);
567     stats.miss(false);
568     stats.hit(true);
569     stats.hit(false);
570     stats.rollMetricsPeriod();
571     assertEquals(0.5, stats.getHitRatioPastNPeriods(), delta);
572     assertEquals(0.5, stats.getHitCachingRatioPastNPeriods(), delta);
573   }
574 
575   private CachedItem [] generateFixedBlocks(int numBlocks, int size, String pfx) {
576     CachedItem [] blocks = new CachedItem[numBlocks];
577     for(int i=0;i<numBlocks;i++) {
578       blocks[i] = new CachedItem(pfx + i, size);
579     }
580     return blocks;
581   }
582 
583   private CachedItem [] generateFixedBlocks(int numBlocks, long size, String pfx) {
584     return generateFixedBlocks(numBlocks, (int)size, pfx);
585   }
586 
587   private CachedItem [] generateRandomBlocks(int numBlocks, long maxSize) {
588     CachedItem [] blocks = new CachedItem[numBlocks];
589     Random r = new Random();
590     for(int i=0;i<numBlocks;i++) {
591       blocks[i] = new CachedItem("block" + i, r.nextInt((int)maxSize)+1);
592     }
593     return blocks;
594   }
595 
596   private long calculateBlockSize(long maxSize, int numBlocks) {
597     long roughBlockSize = maxSize / numBlocks;
598     int numEntries = (int)Math.ceil((1.2)*maxSize/roughBlockSize);
599     long totalOverhead = LruBlockCache.CACHE_FIXED_OVERHEAD +
600         ClassSize.CONCURRENT_HASHMAP +
601         (numEntries * ClassSize.CONCURRENT_HASHMAP_ENTRY) +
602         (LruBlockCache.DEFAULT_CONCURRENCY_LEVEL * ClassSize.CONCURRENT_HASHMAP_SEGMENT);
603     long negateBlockSize = (long)(totalOverhead/numEntries);
604     negateBlockSize += CachedBlock.PER_BLOCK_OVERHEAD;
605     return ClassSize.align((long)Math.floor((roughBlockSize - negateBlockSize)*0.99f));
606   }
607 
608   private long calculateBlockSizeDefault(long maxSize, int numBlocks) {
609     long roughBlockSize = maxSize / numBlocks;
610     int numEntries = (int)Math.ceil((1.2)*maxSize/roughBlockSize);
611     long totalOverhead = LruBlockCache.CACHE_FIXED_OVERHEAD +
612         ClassSize.CONCURRENT_HASHMAP +
613         (numEntries * ClassSize.CONCURRENT_HASHMAP_ENTRY) +
614         (LruBlockCache.DEFAULT_CONCURRENCY_LEVEL * ClassSize.CONCURRENT_HASHMAP_SEGMENT);
615     long negateBlockSize = totalOverhead / numEntries;
616     negateBlockSize += CachedBlock.PER_BLOCK_OVERHEAD;
617     return ClassSize.align((long)Math.floor((roughBlockSize - negateBlockSize)*
618         LruBlockCache.DEFAULT_ACCEPTABLE_FACTOR));
619   }
620 
621   private static class CachedItem implements Cacheable {
622     BlockCacheKey cacheKey;
623     int size;
624 
625     CachedItem(String blockName, int size) {
626       this.cacheKey = new BlockCacheKey(blockName, 0);
627       this.size = size;
628     }
629 
630     /** The size of this item reported to the block cache layer */
631     @Override
632     public long heapSize() {
633       return ClassSize.align(size);
634     }
635 
636     /** Size of the cache block holding this item. Used for verification. */
637     public long cacheBlockHeapSize() {
638       return CachedBlock.PER_BLOCK_OVERHEAD
639           + ClassSize.align(cacheKey.heapSize())
640           + ClassSize.align(size);
641     }
642 
643     @Override
644     public int getSerializedLength() {
645       return 0;
646     }
647 
648     @Override
649     public CacheableDeserializer<Cacheable> getDeserializer() {
650       return null;
651     }
652 
653     @Override
654     public void serialize(ByteBuffer destination) {
655     }
656     
657     @Override
658     public BlockType getBlockType() {
659       return BlockType.DATA;
660     }
661 
662   }
663 
664 }
665