1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.io.hfile;
22
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.EnumMap;
27 import java.util.List;
28 import java.util.Random;
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.HBaseTestingUtility;
36 import org.apache.hadoop.hbase.KeyValue;
37 import org.apache.hadoop.hbase.regionserver.StoreFile;
38 import org.apache.hadoop.hbase.util.BloomFilterFactory;
39 import org.junit.After;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.junit.runners.Parameterized;
44 import org.junit.runners.Parameterized.Parameters;
45 import static org.junit.Assert.*;
46
47
48
49
50
51 @RunWith(Parameterized.class)
52 public class TestCacheOnWrite {
53
54 private static final Log LOG = LogFactory.getLog(TestCacheOnWrite.class);
55
56 private static final HBaseTestingUtility TEST_UTIL =
57 new HBaseTestingUtility();
58 private Configuration conf;
59 private CacheConfig cacheConf;
60 private FileSystem fs;
61 private Random rand = new Random(12983177L);
62 private Path storeFilePath;
63 private Compression.Algorithm compress;
64 private CacheOnWriteType cowType;
65 private BlockCache blockCache;
66 private String testName;
67
68 private static final int DATA_BLOCK_SIZE = 2048;
69 private static final int NUM_KV = 25000;
70 private static final int INDEX_BLOCK_SIZE = 512;
71 private static final int BLOOM_BLOCK_SIZE = 4096;
72
73
74 private static final int NUM_VALID_KEY_TYPES =
75 KeyValue.Type.values().length - 2;
76
77 private static enum CacheOnWriteType {
78 DATA_BLOCKS(BlockType.DATA, CacheConfig.CACHE_BLOCKS_ON_WRITE_KEY),
79 BLOOM_BLOCKS(BlockType.BLOOM_CHUNK,
80 CacheConfig.CACHE_BLOOM_BLOCKS_ON_WRITE_KEY),
81 INDEX_BLOCKS(BlockType.LEAF_INDEX,
82 CacheConfig.CACHE_INDEX_BLOCKS_ON_WRITE_KEY);
83
84 private final String confKey;
85 private final BlockType inlineBlockType;
86
87 private CacheOnWriteType(BlockType inlineBlockType, String confKey) {
88 this.inlineBlockType = inlineBlockType;
89 this.confKey = confKey;
90 }
91
92 public boolean shouldBeCached(BlockType blockType) {
93 return blockType == inlineBlockType
94 || blockType == BlockType.INTERMEDIATE_INDEX
95 && inlineBlockType == BlockType.LEAF_INDEX;
96 }
97
98 public void modifyConf(Configuration conf) {
99 for (CacheOnWriteType cowType : CacheOnWriteType.values())
100 conf.setBoolean(cowType.confKey, cowType == this);
101 }
102
103 }
104
105 public TestCacheOnWrite(CacheOnWriteType cowType,
106 Compression.Algorithm compress) {
107 this.cowType = cowType;
108 this.compress = compress;
109 testName = "[cacheOnWrite=" + cowType + ", compress=" + compress + "]";
110 System.out.println(testName);
111 }
112
113 @Parameters
114 public static Collection<Object[]> getParameters() {
115 List<Object[]> cowTypes = new ArrayList<Object[]>();
116 for (CacheOnWriteType cowType : CacheOnWriteType.values())
117 for (Compression.Algorithm compress :
118 HBaseTestingUtility.COMPRESSION_ALGORITHMS) {
119 cowTypes.add(new Object[] { cowType, compress });
120 }
121 return cowTypes;
122 }
123
124 @Before
125 public void setUp() throws IOException {
126 conf = TEST_UTIL.getConfiguration();
127 conf.setInt(HFile.FORMAT_VERSION_KEY, HFile.MAX_FORMAT_VERSION);
128 conf.setInt(HFileBlockIndex.MAX_CHUNK_SIZE_KEY, INDEX_BLOCK_SIZE);
129 conf.setInt(BloomFilterFactory.IO_STOREFILE_BLOOM_BLOCK_SIZE,
130 BLOOM_BLOCK_SIZE);
131 conf.setBoolean(CacheConfig.CACHE_BLOCKS_ON_WRITE_KEY,
132 cowType.shouldBeCached(BlockType.DATA));
133 conf.setBoolean(CacheConfig.CACHE_INDEX_BLOCKS_ON_WRITE_KEY,
134 cowType.shouldBeCached(BlockType.LEAF_INDEX));
135 conf.setBoolean(CacheConfig.CACHE_BLOOM_BLOCKS_ON_WRITE_KEY,
136 cowType.shouldBeCached(BlockType.BLOOM_CHUNK));
137 cowType.modifyConf(conf);
138 fs = FileSystem.get(conf);
139 cacheConf = new CacheConfig(conf);
140 blockCache = cacheConf.getBlockCache();
141 System.out.println("setUp()");
142 }
143
144 @After
145 public void tearDown() {
146 blockCache.evictBlocksByPrefix("");
147 }
148
149 @Test
150 public void testCacheOnWrite() throws IOException {
151 writeStoreFile();
152 readStoreFile();
153 }
154
155 private void readStoreFile() throws IOException {
156 HFileReaderV2 reader = (HFileReaderV2) HFile.createReader(fs,
157 storeFilePath, cacheConf);
158 LOG.info("HFile information: " + reader);
159 HFileScanner scanner = reader.getScanner(false, false);
160 assertTrue(testName, scanner.seekTo());
161
162 long offset = 0;
163 HFileBlock prevBlock = null;
164 EnumMap<BlockType, Integer> blockCountByType =
165 new EnumMap<BlockType, Integer>(BlockType.class);
166
167 while (offset < reader.getTrailer().getLoadOnOpenDataOffset()) {
168 long onDiskSize = -1;
169 if (prevBlock != null) {
170 onDiskSize = prevBlock.getNextBlockOnDiskSizeWithHeader();
171 }
172
173 HFileBlock block = reader.readBlock(offset, onDiskSize, false, true,
174 false);
175 String blockCacheKey = HFile.getBlockCacheKey(reader.getName(), offset);
176 boolean isCached = blockCache.getBlock(blockCacheKey, true) != null;
177 boolean shouldBeCached = cowType.shouldBeCached(block.getBlockType());
178 assertEquals(testName + " " + block, shouldBeCached, isCached);
179 prevBlock = block;
180 offset += block.getOnDiskSizeWithHeader();
181 BlockType bt = block.getBlockType();
182 Integer count = blockCountByType.get(bt);
183 blockCountByType.put(bt, (count == null ? 0 : count) + 1);
184 }
185
186 LOG.info("Block count by type: " + blockCountByType);
187 String countByType = blockCountByType.toString();
188 assertEquals(
189 "{DATA=1379, LEAF_INDEX=173, BLOOM_CHUNK=9, INTERMEDIATE_INDEX=24}",
190 countByType);
191
192 reader.close();
193 }
194
195 public static KeyValue.Type generateKeyType(Random rand) {
196 if (rand.nextBoolean()) {
197
198 return KeyValue.Type.Put;
199 } else {
200 KeyValue.Type keyType =
201 KeyValue.Type.values()[1 + rand.nextInt(NUM_VALID_KEY_TYPES)];
202 if (keyType == KeyValue.Type.Minimum || keyType == KeyValue.Type.Maximum)
203 {
204 throw new RuntimeException("Generated an invalid key type: " + keyType
205 + ". " + "Probably the layout of KeyValue.Type has changed.");
206 }
207 return keyType;
208 }
209 }
210
211 public void writeStoreFile() throws IOException {
212 Path storeFileParentDir = new Path(TEST_UTIL.getDataTestDir(),
213 "test_cache_on_write");
214 StoreFile.Writer sfw = StoreFile.createWriter(fs, storeFileParentDir,
215 DATA_BLOCK_SIZE, compress, KeyValue.COMPARATOR, conf,
216 cacheConf, StoreFile.BloomType.ROWCOL, NUM_KV);
217
218 final int rowLen = 32;
219 for (int i = 0; i < NUM_KV; ++i) {
220 byte[] k = TestHFileWriterV2.randomOrderedKey(rand, i);
221 byte[] v = TestHFileWriterV2.randomValue(rand);
222 int cfLen = rand.nextInt(k.length - rowLen + 1);
223 KeyValue kv = new KeyValue(
224 k, 0, rowLen,
225 k, rowLen, cfLen,
226 k, rowLen + cfLen, k.length - rowLen - cfLen,
227 rand.nextLong(),
228 generateKeyType(rand),
229 v, 0, v.length);
230 sfw.append(kv);
231 }
232
233 sfw.close();
234 storeFilePath = sfw.getPath();
235 }
236
237 }