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 java.io.DataInput;
22 import java.io.DataOutput;
23 import java.io.IOException;
24 import java.nio.ByteBuffer;
25 import java.util.Arrays;
26 import java.util.Map;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.fs.FSDataInputStream;
31 import org.apache.hadoop.fs.FSDataOutputStream;
32 import org.apache.hadoop.fs.FileStatus;
33 import org.apache.hadoop.fs.FileSystem;
34 import org.apache.hadoop.fs.Path;
35 import org.apache.hadoop.hbase.Cell;
36 import org.apache.hadoop.hbase.CellUtil;
37 import org.apache.hadoop.hbase.HBaseTestCase;
38 import org.apache.hadoop.hbase.HBaseTestingUtility;
39 import org.apache.hadoop.hbase.HConstants;
40 import org.apache.hadoop.hbase.KeyValue;
41 import org.apache.hadoop.hbase.KeyValue.Type;
42 import org.apache.hadoop.hbase.testclassification.SmallTests;
43 import org.apache.hadoop.hbase.Tag;
44 import org.apache.hadoop.hbase.io.compress.Compression;
45 import org.apache.hadoop.hbase.io.hfile.HFile.Reader;
46 import org.apache.hadoop.hbase.io.hfile.HFile.Writer;
47 import org.apache.hadoop.hbase.util.Bytes;
48 import org.apache.hadoop.io.Writable;
49 import org.junit.Assert;
50 import org.junit.Test;
51 import org.junit.experimental.categories.Category;
52 import org.mockito.Mockito;
53
54
55
56
57
58
59
60
61
62 @Category(SmallTests.class)
63 public class TestHFile extends HBaseTestCase {
64 private static final Log LOG = LogFactory.getLog(TestHFile.class);
65
66 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
67 private static String ROOT_DIR =
68 TEST_UTIL.getDataTestDir("TestHFile").toString();
69 private final int minBlockSize = 512;
70 private static String localFormatter = "%010d";
71 private static CacheConfig cacheConf = null;
72 private Map<String, Long> startingMetrics;
73
74 @Override
75 public void setUp() throws Exception {
76 super.setUp();
77 }
78
79 @Override
80 public void tearDown() throws Exception {
81 super.tearDown();
82 }
83
84
85
86
87
88
89
90 public void testEmptyHFile() throws IOException {
91 if (cacheConf == null) cacheConf = new CacheConfig(conf);
92 Path f = new Path(ROOT_DIR, getName());
93 HFileContext context = new HFileContextBuilder().withIncludesTags(false).build();
94 Writer w =
95 HFile.getWriterFactory(conf, cacheConf).withPath(fs, f).withFileContext(context).create();
96 w.close();
97 Reader r = HFile.createReader(fs, f, cacheConf, conf);
98 r.loadFileInfo();
99 assertNull(r.getFirstKey());
100 assertNull(r.getLastKey());
101 }
102
103 @Test
104 public void testCorruptOutOfOrderHFileWrite() throws IOException {
105 Path path = new Path(ROOT_DIR, "testCorruptOutOfOrderHFileWrite");
106 FSDataOutputStream mockedOutputStream = Mockito.mock(FSDataOutputStream.class);
107 String columnFamily = "MyColumnFamily";
108 String tableName = "MyTableName";
109 HFileContext fileContext = new HFileContextBuilder()
110 .withHFileName("testCorruptOutOfOrderHFileWriteHFile")
111 .withBlockSize(minBlockSize)
112 .withColumnFamily(Bytes.toBytes(columnFamily))
113 .withTableName(Bytes.toBytes(tableName))
114 .withHBaseCheckSum(false)
115 .withCompression(Compression.Algorithm.NONE)
116 .withCompressTags(false)
117 .build();
118 HFileWriterV3 writer = new HFileWriterV3(conf, cacheConf, fs, path, mockedOutputStream,
119 new KeyValue.KVComparator(), fileContext);
120 byte[] row = Bytes.toBytes("foo");
121 byte[] qualifier = Bytes.toBytes("qualifier");
122 byte[] cf = Bytes.toBytes(columnFamily);
123 byte[] val = Bytes.toBytes("fooVal");
124 long firstTS = 100L;
125 long secondTS = 101L;
126 Cell firstCell = CellUtil.createCell(row,cf, qualifier, firstTS, Type.Put.getCode(), val);
127 Cell secondCell= CellUtil.createCell(row,cf, qualifier, secondTS, Type.Put.getCode(), val);
128
129 writer.append(firstCell);
130 try {
131 writer.append(secondCell);
132 } catch(IOException ie){
133 String message = ie.getMessage();
134 Assert.assertTrue(message.contains("not lexically larger"));
135 Assert.assertTrue(message.contains(tableName));
136 Assert.assertTrue(message.contains(columnFamily));
137 return;
138 }
139 Assert.fail("Exception wasn't thrown even though Cells were appended in the wrong order!");
140 }
141
142
143
144 public void testCorrupt0LengthHFile() throws IOException {
145 if (cacheConf == null) cacheConf = new CacheConfig(conf);
146 Path f = new Path(ROOT_DIR, getName());
147 FSDataOutputStream fsos = fs.create(f);
148 fsos.close();
149
150 try {
151 Reader r = HFile.createReader(fs, f, cacheConf, conf);
152 } catch (CorruptHFileException che) {
153
154 return;
155 }
156 fail("Should have thrown exception");
157 }
158
159 public static void truncateFile(FileSystem fs, Path src, Path dst) throws IOException {
160 FileStatus fst = fs.getFileStatus(src);
161 long len = fst.getLen();
162 len = len / 2 ;
163
164
165 FSDataOutputStream fdos = fs.create(dst);
166 byte[] buf = new byte[(int)len];
167 FSDataInputStream fdis = fs.open(src);
168 fdis.read(buf);
169 fdos.write(buf);
170 fdis.close();
171 fdos.close();
172 }
173
174
175
176
177 public void testCorruptTruncatedHFile() throws IOException {
178 if (cacheConf == null) cacheConf = new CacheConfig(conf);
179 Path f = new Path(ROOT_DIR, getName());
180 HFileContext context = new HFileContextBuilder().build();
181 Writer w = HFile.getWriterFactory(conf, cacheConf).withPath(this.fs, f)
182 .withFileContext(context).create();
183 writeSomeRecords(w, 0, 100, false);
184 w.close();
185
186 Path trunc = new Path(f.getParent(), "trucated");
187 truncateFile(fs, w.getPath(), trunc);
188
189 try {
190 Reader r = HFile.createReader(fs, trunc, cacheConf, conf);
191 } catch (CorruptHFileException che) {
192
193 return;
194 }
195 fail("Should have thrown exception");
196 }
197
198
199
200 private int writeSomeRecords(Writer writer, int start, int n, boolean useTags)
201 throws IOException {
202 String value = "value";
203 KeyValue kv;
204 for (int i = start; i < (start + n); i++) {
205 String key = String.format(localFormatter, Integer.valueOf(i));
206 if (useTags) {
207 Tag t = new Tag((byte) 1, "myTag1");
208 Tag[] tags = new Tag[1];
209 tags[0] = t;
210 kv = new KeyValue(Bytes.toBytes(key), Bytes.toBytes("family"), Bytes.toBytes("qual"),
211 HConstants.LATEST_TIMESTAMP, Bytes.toBytes(value + key), tags);
212 writer.append(kv);
213 } else {
214 kv = new KeyValue(Bytes.toBytes(key), Bytes.toBytes("family"), Bytes.toBytes("qual"),
215 Bytes.toBytes(value + key));
216 writer.append(kv);
217 }
218 }
219 return (start + n);
220 }
221
222 private void readAllRecords(HFileScanner scanner) throws IOException {
223 readAndCheckbytes(scanner, 0, 100);
224 }
225
226
227 private int readAndCheckbytes(HFileScanner scanner, int start, int n)
228 throws IOException {
229 String value = "value";
230 int i = start;
231 for (; i < (start + n); i++) {
232 ByteBuffer key = scanner.getKey();
233 ByteBuffer val = scanner.getValue();
234 String keyStr = String.format(localFormatter, Integer.valueOf(i));
235 String valStr = value + keyStr;
236 KeyValue kv = new KeyValue(Bytes.toBytes(keyStr), Bytes.toBytes("family"),
237 Bytes.toBytes("qual"), Bytes.toBytes(valStr));
238 byte[] keyBytes = new KeyValue.KeyOnlyKeyValue(Bytes.toBytes(key), 0,
239 Bytes.toBytes(key).length).getKey();
240 assertTrue("bytes for keys do not match " + keyStr + " " +
241 Bytes.toString(Bytes.toBytes(key)),
242 Arrays.equals(kv.getKey(), keyBytes));
243 byte [] valBytes = Bytes.toBytes(val);
244 assertTrue("bytes for vals do not match " + valStr + " " +
245 Bytes.toString(valBytes),
246 Arrays.equals(Bytes.toBytes(valStr), valBytes));
247 if (!scanner.next()) {
248 break;
249 }
250 }
251 assertEquals(i, start + n - 1);
252 return (start + n);
253 }
254
255 private byte[] getSomeKey(int rowId) {
256 KeyValue kv = new KeyValue(String.format(localFormatter, Integer.valueOf(rowId)).getBytes(),
257 Bytes.toBytes("family"), Bytes.toBytes("qual"), HConstants.LATEST_TIMESTAMP, Type.Put);
258 return kv.getKey();
259 }
260
261 private void writeRecords(Writer writer, boolean useTags) throws IOException {
262 writeSomeRecords(writer, 0, 100, useTags);
263 writer.close();
264 }
265
266 private FSDataOutputStream createFSOutput(Path name) throws IOException {
267
268 FSDataOutputStream fout = fs.create(name);
269 return fout;
270 }
271
272
273
274
275
276 void basicWithSomeCodec(String codec, boolean useTags) throws IOException {
277 if (useTags) {
278 conf.setInt("hfile.format.version", 3);
279 }
280 if (cacheConf == null) cacheConf = new CacheConfig(conf);
281 Path ncTFile = new Path(ROOT_DIR, "basic.hfile." + codec.toString() + useTags);
282 FSDataOutputStream fout = createFSOutput(ncTFile);
283 HFileContext meta = new HFileContextBuilder()
284 .withBlockSize(minBlockSize)
285 .withCompression(AbstractHFileWriter.compressionByName(codec))
286 .build();
287 Writer writer = HFile.getWriterFactory(conf, cacheConf)
288 .withOutputStream(fout)
289 .withFileContext(meta)
290 .withComparator(new KeyValue.KVComparator())
291 .create();
292 LOG.info(writer);
293 writeRecords(writer, useTags);
294 fout.close();
295 FSDataInputStream fin = fs.open(ncTFile);
296 Reader reader = HFile.createReaderFromStream(ncTFile, fs.open(ncTFile),
297 fs.getFileStatus(ncTFile).getLen(), cacheConf, conf);
298 System.out.println(cacheConf.toString());
299
300 reader.loadFileInfo();
301
302 HFileScanner scanner = reader.getScanner(true, false);
303
304 scanner.seekTo();
305 readAllRecords(scanner);
306 int seekTo = scanner.seekTo(KeyValue.createKeyValueFromKey(getSomeKey(50)));
307 System.out.println(seekTo);
308 assertTrue("location lookup failed",
309 scanner.seekTo(KeyValue.createKeyValueFromKey(getSomeKey(50))) == 0);
310
311 ByteBuffer readKey = scanner.getKey();
312 assertTrue("seeked key does not match", Arrays.equals(getSomeKey(50),
313 Bytes.toBytes(readKey)));
314
315 scanner.seekTo(KeyValue.createKeyValueFromKey(getSomeKey(0)));
316 ByteBuffer val1 = scanner.getValue();
317 scanner.seekTo(KeyValue.createKeyValueFromKey(getSomeKey(0)));
318 ByteBuffer val2 = scanner.getValue();
319 assertTrue(Arrays.equals(Bytes.toBytes(val1), Bytes.toBytes(val2)));
320
321 reader.close();
322 fin.close();
323 fs.delete(ncTFile, true);
324 }
325
326 public void testTFileFeatures() throws IOException {
327 testTFilefeaturesInternals(false);
328 testTFilefeaturesInternals(true);
329 }
330
331 protected void testTFilefeaturesInternals(boolean useTags) throws IOException {
332 basicWithSomeCodec("none", useTags);
333 basicWithSomeCodec("gz", useTags);
334 }
335
336 private void writeNumMetablocks(Writer writer, int n) {
337 for (int i = 0; i < n; i++) {
338 writer.appendMetaBlock("HFileMeta" + i, new Writable() {
339 private int val;
340 public Writable setVal(int val) { this.val = val; return this; }
341
342 @Override
343 public void write(DataOutput out) throws IOException {
344 out.write(("something to test" + val).getBytes());
345 }
346
347 @Override
348 public void readFields(DataInput in) throws IOException { }
349 }.setVal(i));
350 }
351 }
352
353 private void someTestingWithMetaBlock(Writer writer) {
354 writeNumMetablocks(writer, 10);
355 }
356
357 private void readNumMetablocks(Reader reader, int n) throws IOException {
358 for (int i = 0; i < n; i++) {
359 ByteBuffer actual = reader.getMetaBlock("HFileMeta" + i, false);
360 ByteBuffer expected =
361 ByteBuffer.wrap(("something to test" + i).getBytes());
362 assertEquals("failed to match metadata",
363 Bytes.toStringBinary(expected), Bytes.toStringBinary(actual));
364 }
365 }
366
367 private void someReadingWithMetaBlock(Reader reader) throws IOException {
368 readNumMetablocks(reader, 10);
369 }
370
371 private void metablocks(final String compress) throws Exception {
372 if (cacheConf == null) cacheConf = new CacheConfig(conf);
373 Path mFile = new Path(ROOT_DIR, "meta.hfile");
374 FSDataOutputStream fout = createFSOutput(mFile);
375 HFileContext meta = new HFileContextBuilder()
376 .withCompression(AbstractHFileWriter.compressionByName(compress))
377 .withBlockSize(minBlockSize).build();
378 Writer writer = HFile.getWriterFactory(conf, cacheConf)
379 .withOutputStream(fout)
380 .withFileContext(meta)
381 .create();
382 someTestingWithMetaBlock(writer);
383 writer.close();
384 fout.close();
385 FSDataInputStream fin = fs.open(mFile);
386 Reader reader = HFile.createReaderFromStream(mFile, fs.open(mFile),
387 this.fs.getFileStatus(mFile).getLen(), cacheConf, conf);
388 reader.loadFileInfo();
389
390 assertFalse(reader.getScanner(false, false).seekTo());
391 someReadingWithMetaBlock(reader);
392 fs.delete(mFile, true);
393 reader.close();
394 fin.close();
395 }
396
397
398 public void testMetaBlocks() throws Exception {
399 metablocks("none");
400 metablocks("gz");
401 }
402
403 public void testNullMetaBlocks() throws Exception {
404 if (cacheConf == null) cacheConf = new CacheConfig(conf);
405 for (Compression.Algorithm compressAlgo :
406 HBaseTestingUtility.COMPRESSION_ALGORITHMS) {
407 Path mFile = new Path(ROOT_DIR, "nometa_" + compressAlgo + ".hfile");
408 FSDataOutputStream fout = createFSOutput(mFile);
409 HFileContext meta = new HFileContextBuilder().withCompression(compressAlgo)
410 .withBlockSize(minBlockSize).build();
411 Writer writer = HFile.getWriterFactory(conf, cacheConf)
412 .withOutputStream(fout)
413 .withFileContext(meta)
414 .create();
415 KeyValue kv = new KeyValue("foo".getBytes(), "f1".getBytes(), null, "value".getBytes());
416 writer.append(kv);
417 writer.close();
418 fout.close();
419 Reader reader = HFile.createReader(fs, mFile, cacheConf, conf);
420 reader.loadFileInfo();
421 assertNull(reader.getMetaBlock("non-existant", false));
422 }
423 }
424
425
426
427
428 public void testCompressionOrdinance() {
429 assertTrue(Compression.Algorithm.LZO.ordinal() == 0);
430 assertTrue(Compression.Algorithm.GZ.ordinal() == 1);
431 assertTrue(Compression.Algorithm.NONE.ordinal() == 2);
432 assertTrue(Compression.Algorithm.SNAPPY.ordinal() == 3);
433 assertTrue(Compression.Algorithm.LZ4.ordinal() == 4);
434 }
435
436 }
437