1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.io.IOException;
25 import java.lang.management.ManagementFactory;
26 import java.lang.management.MemoryUsage;
27 import java.nio.ByteBuffer;
28 import java.util.Map;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.hadoop.conf.Configuration;
33 import org.apache.hadoop.fs.FileSystem;
34 import org.apache.hadoop.fs.Path;
35 import org.apache.hadoop.hbase.HBaseConfiguration;
36 import org.apache.hadoop.hbase.HBaseTestingUtility;
37 import org.apache.hadoop.hbase.HConstants;
38 import org.apache.hadoop.hbase.testclassification.LargeTests;
39 import org.apache.hadoop.hbase.io.hfile.bucket.BucketCache;
40 import org.apache.hadoop.hbase.io.util.HeapMemorySizeUtil;
41 import org.apache.hadoop.hbase.util.Threads;
42 import org.junit.After;
43 import org.junit.Before;
44 import org.junit.Test;
45 import org.junit.experimental.categories.Category;
46
47
48
49
50
51
52
53
54 @Category(LargeTests.class)
55 public class TestCacheConfig {
56 private static final Log LOG = LogFactory.getLog(TestCacheConfig.class);
57 private Configuration conf;
58
59 static class Deserializer implements CacheableDeserializer<Cacheable> {
60 private final Cacheable cacheable;
61 private int deserializedIdentifier = 0;
62
63 Deserializer(final Cacheable c) {
64 deserializedIdentifier = CacheableDeserializerIdManager.registerDeserializer(this);
65 this.cacheable = c;
66 }
67
68 @Override
69 public int getDeserialiserIdentifier() {
70 return deserializedIdentifier;
71 }
72
73 @Override
74 public Cacheable deserialize(ByteBuffer b, boolean reuse) throws IOException {
75 LOG.info("Deserialized " + b + ", reuse=" + reuse);
76 return cacheable;
77 }
78
79 @Override
80 public Cacheable deserialize(ByteBuffer b) throws IOException {
81 LOG.info("Deserialized " + b);
82 return cacheable;
83 }
84 };
85
86 static class IndexCacheEntry extends DataCacheEntry {
87 private static IndexCacheEntry SINGLETON = new IndexCacheEntry();
88
89 public IndexCacheEntry() {
90 super(SINGLETON);
91 }
92
93 @Override
94 public BlockType getBlockType() {
95 return BlockType.ROOT_INDEX;
96 }
97 }
98
99 static class DataCacheEntry implements Cacheable {
100 private static final int SIZE = 1;
101 private static DataCacheEntry SINGLETON = new DataCacheEntry();
102 final CacheableDeserializer<Cacheable> deserializer;
103
104 DataCacheEntry() {
105 this(SINGLETON);
106 }
107
108 DataCacheEntry(final Cacheable c) {
109 this.deserializer = new Deserializer(c);
110 }
111
112 @Override
113 public String toString() {
114 return "size=" + SIZE + ", type=" + getBlockType();
115 };
116
117 @Override
118 public long heapSize() {
119 return SIZE;
120 }
121
122 @Override
123 public int getSerializedLength() {
124 return SIZE;
125 }
126
127 @Override
128 public void serialize(ByteBuffer destination) {
129 LOG.info("Serialized " + this + " to " + destination);
130 }
131
132 @Override
133 public CacheableDeserializer<Cacheable> getDeserializer() {
134 return this.deserializer;
135 }
136
137 @Override
138 public BlockType getBlockType() {
139 return BlockType.DATA;
140 }
141 };
142
143 static class MetaCacheEntry extends DataCacheEntry {
144 @Override
145 public BlockType getBlockType() {
146 return BlockType.INTERMEDIATE_INDEX;
147 }
148 }
149
150 @Before
151 public void setUp() throws Exception {
152 CacheConfig.GLOBAL_BLOCK_CACHE_INSTANCE = null;
153 this.conf = HBaseConfiguration.create();
154 }
155
156 @After
157 public void tearDown() throws Exception {
158
159 CacheConfig.GLOBAL_BLOCK_CACHE_INSTANCE = null;
160 }
161
162
163
164
165
166
167
168 void basicBlockCacheOps(final CacheConfig cc, final boolean doubling,
169 final boolean sizing) {
170 assertTrue(cc.isBlockCacheEnabled());
171 assertTrue(CacheConfig.DEFAULT_IN_MEMORY == cc.isInMemory());
172 BlockCache bc = cc.getBlockCache();
173 BlockCacheKey bck = new BlockCacheKey("f", 0);
174 Cacheable c = new DataCacheEntry();
175
176 long initialBlockCount = bc.getBlockCount();
177 bc.cacheBlock(bck, c, cc.isInMemory(), cc.isCacheDataInL1());
178 assertEquals(doubling? 2: 1, bc.getBlockCount() - initialBlockCount);
179 bc.evictBlock(bck);
180 assertEquals(initialBlockCount, bc.getBlockCount());
181
182
183 if (sizing) {
184 long originalSize = bc.getCurrentSize();
185 bc.cacheBlock(bck, c, cc.isInMemory(), cc.isCacheDataInL1());
186 assertTrue(bc.getCurrentSize() > originalSize);
187 bc.evictBlock(bck);
188 long size = bc.getCurrentSize();
189 assertEquals(originalSize, size);
190 }
191 }
192
193
194
195
196
197
198 private long cacheDataBlock(final CacheConfig cc, final String filename) {
199 BlockCacheKey bck = new BlockCacheKey(filename, 0);
200 Cacheable c = new DataCacheEntry();
201
202 cc.getBlockCache().cacheBlock(bck, c, cc.isInMemory(), cc.isCacheDataInL1());
203 return cc.getBlockCache().getBlockCount();
204 }
205
206 @Test
207 public void testCacheConfigDefaultLRUBlockCache() {
208 CacheConfig cc = new CacheConfig(this.conf);
209 assertTrue(cc.isBlockCacheEnabled());
210 assertTrue(CacheConfig.DEFAULT_IN_MEMORY == cc.isInMemory());
211 basicBlockCacheOps(cc, false, true);
212 assertTrue(cc.getBlockCache() instanceof LruBlockCache);
213 }
214
215
216
217
218 @Test
219 public void testOffHeapBucketCacheConfig() {
220 this.conf.set(HConstants.BUCKET_CACHE_IOENGINE_KEY, "offheap");
221 doBucketCacheConfigTest();
222 }
223
224 @Test
225 public void testOnHeapBucketCacheConfig() {
226 this.conf.set(HConstants.BUCKET_CACHE_IOENGINE_KEY, "heap");
227 doBucketCacheConfigTest();
228 }
229
230 @Test
231 public void testFileBucketCacheConfig() throws IOException {
232 HBaseTestingUtility htu = new HBaseTestingUtility(this.conf);
233 try {
234 Path p = new Path(htu.getDataTestDir(), "bc.txt");
235 FileSystem fs = FileSystem.get(this.conf);
236 fs.create(p).close();
237 this.conf.set(HConstants.BUCKET_CACHE_IOENGINE_KEY, "file:" + p);
238 doBucketCacheConfigTest();
239 } finally {
240 htu.cleanupTestDir();
241 }
242 }
243
244 private void doBucketCacheConfigTest() {
245 final int bcSize = 100;
246 this.conf.setInt(HConstants.BUCKET_CACHE_SIZE_KEY, bcSize);
247 CacheConfig cc = new CacheConfig(this.conf);
248 basicBlockCacheOps(cc, false, false);
249 assertTrue(cc.getBlockCache() instanceof CombinedBlockCache);
250
251 CombinedBlockCache cbc = (CombinedBlockCache)cc.getBlockCache();
252 BlockCache [] bcs = cbc.getBlockCaches();
253 assertTrue(bcs[0] instanceof LruBlockCache);
254 LruBlockCache lbc = (LruBlockCache)bcs[0];
255 assertEquals(HeapMemorySizeUtil.getLruCacheSize(this.conf), lbc.getMaxSize());
256 assertTrue(bcs[1] instanceof BucketCache);
257 BucketCache bc = (BucketCache)bcs[1];
258
259 assertEquals(bcSize, bc.getMaxSize() / (1024 * 1024));
260 }
261
262
263
264
265
266 @Test (timeout=10000)
267 public void testBucketCacheConfigL1L2Setup() {
268 this.conf.set(HConstants.BUCKET_CACHE_IOENGINE_KEY, "offheap");
269
270
271 this.conf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0.001f);
272 long lruExpectedSize = HeapMemorySizeUtil.getLruCacheSize(this.conf);
273 final int bcSize = 100;
274 long bcExpectedSize = 100 * 1024 * 1024;
275 assertTrue(lruExpectedSize < bcExpectedSize);
276 this.conf.setInt(HConstants.BUCKET_CACHE_SIZE_KEY, bcSize);
277 this.conf.setBoolean(CacheConfig.BUCKET_CACHE_COMBINED_KEY, false);
278 CacheConfig cc = new CacheConfig(this.conf);
279 basicBlockCacheOps(cc, false, false);
280 assertTrue(cc.getBlockCache() instanceof LruBlockCache);
281
282 LruBlockCache lbc = (LruBlockCache)cc.getBlockCache();
283 assertEquals(lruExpectedSize, lbc.getMaxSize());
284 BlockCache bc = lbc.getVictimHandler();
285
286 assertEquals(bcExpectedSize, ((BucketCache) bc).getMaxSize());
287
288 long initialL1BlockCount = lbc.getBlockCount();
289 long initialL2BlockCount = bc.getBlockCount();
290 Cacheable c = new DataCacheEntry();
291 BlockCacheKey bck = new BlockCacheKey("bck", 0);
292 lbc.cacheBlock(bck, c, false, false);
293 assertEquals(initialL1BlockCount + 1, lbc.getBlockCount());
294 assertEquals(initialL2BlockCount, bc.getBlockCount());
295
296 final long justTooBigSize = lbc.acceptableSize() + 1;
297 lbc.cacheBlock(new BlockCacheKey("bck2", 0), new DataCacheEntry() {
298 @Override
299 public long heapSize() {
300 return justTooBigSize;
301 }
302
303 @Override
304 public int getSerializedLength() {
305 return (int)heapSize();
306 }
307 });
308
309 while (initialL1BlockCount != lbc.getBlockCount()) Threads.sleep(10);
310 assertEquals(initialL1BlockCount, lbc.getBlockCount());
311 long count = bc.getBlockCount();
312 assertTrue(initialL2BlockCount + 1 <= count);
313 }
314
315
316
317
318
319 @Test
320 public void testCacheDataInL1() {
321 this.conf.set(HConstants.BUCKET_CACHE_IOENGINE_KEY, "offheap");
322 this.conf.setInt(HConstants.BUCKET_CACHE_SIZE_KEY, 100);
323 CacheConfig cc = new CacheConfig(this.conf);
324 assertTrue(cc.getBlockCache() instanceof CombinedBlockCache);
325 CombinedBlockCache cbc = (CombinedBlockCache)cc.getBlockCache();
326
327 cacheDataBlock(cc, "1");
328 LruBlockCache lrubc = (LruBlockCache)cbc.getBlockCaches()[0];
329 assertDataBlockCount(lrubc, 0);
330
331 cc.setCacheDataInL1(true);
332 cacheDataBlock(cc, "2");
333 assertDataBlockCount(lrubc, 1);
334 cc.setCacheDataInL1(false);
335 cacheDataBlock(cc, "3");
336 assertDataBlockCount(lrubc, 1);
337 }
338
339 private void assertDataBlockCount(final LruBlockCache bc, final int expected) {
340 Map<BlockType, Integer> blocks = bc.getBlockTypeCountsForTest();
341 assertEquals(expected, blocks == null? 0:
342 blocks.get(BlockType.DATA) == null? 0:
343 blocks.get(BlockType.DATA).intValue());
344 }
345 }