1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import java.io.IOException;
22 import java.nio.ByteBuffer;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.TreeSet;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.hadoop.conf.Configuration;
34 import org.apache.hadoop.fs.FileSystem;
35 import org.apache.hadoop.fs.Path;
36 import org.apache.hadoop.hbase.Cell;
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.HRegionInfo;
41 import org.apache.hadoop.hbase.KeyValue;
42 import org.apache.hadoop.hbase.KeyValueUtil;
43 import org.apache.hadoop.hbase.testclassification.SmallTests;
44 import org.apache.hadoop.hbase.TableName;
45 import org.apache.hadoop.hbase.client.Scan;
46 import org.apache.hadoop.hbase.io.HFileLink;
47 import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
48 import org.apache.hadoop.hbase.io.hfile.BlockCache;
49 import org.apache.hadoop.hbase.io.hfile.CacheConfig;
50 import org.apache.hadoop.hbase.io.hfile.CacheStats;
51 import org.apache.hadoop.hbase.io.hfile.HFileContext;
52 import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder;
53 import org.apache.hadoop.hbase.io.hfile.HFileDataBlockEncoder;
54 import org.apache.hadoop.hbase.io.hfile.HFileDataBlockEncoderImpl;
55 import org.apache.hadoop.hbase.io.hfile.HFileScanner;
56 import org.apache.hadoop.hbase.util.BloomFilterFactory;
57 import org.apache.hadoop.hbase.util.Bytes;
58 import org.apache.hadoop.hbase.util.ChecksumType;
59 import org.apache.hadoop.hbase.util.FSUtils;
60 import org.junit.experimental.categories.Category;
61 import org.mockito.Mockito;
62
63 import com.google.common.base.Joiner;
64 import com.google.common.collect.Iterables;
65 import com.google.common.collect.Lists;
66
67
68
69
70 @Category(SmallTests.class)
71 public class TestStoreFile extends HBaseTestCase {
72 static final Log LOG = LogFactory.getLog(TestStoreFile.class);
73 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
74 private CacheConfig cacheConf = new CacheConfig(TEST_UTIL.getConfiguration());
75 private static String ROOT_DIR = TEST_UTIL.getDataTestDir("TestStoreFile").toString();
76 private static final ChecksumType CKTYPE = ChecksumType.CRC32;
77 private static final int CKBYTES = 512;
78 private static String TEST_FAMILY = "cf";
79
80 @Override
81 public void setUp() throws Exception {
82 super.setUp();
83 }
84
85 @Override
86 public void tearDown() throws Exception {
87 super.tearDown();
88 }
89
90
91
92
93
94
95 public void testBasicHalfMapFile() throws Exception {
96 final HRegionInfo hri =
97 new HRegionInfo(TableName.valueOf("testBasicHalfMapFileTb"));
98 HRegionFileSystem regionFs = HRegionFileSystem.createRegionOnFileSystem(
99 conf, fs, new Path(this.testDir, hri.getTable().getNameAsString()), hri);
100
101 HFileContext meta = new HFileContextBuilder().withBlockSize(2*1024).build();
102 StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
103 .withFilePath(regionFs.createTempName())
104 .withFileContext(meta)
105 .build();
106 writeStoreFile(writer);
107
108 Path sfPath = regionFs.commitStoreFile(TEST_FAMILY, writer.getPath());
109 StoreFile sf = new StoreFile(this.fs, sfPath, conf, cacheConf,
110 BloomType.NONE);
111 checkHalfHFile(regionFs, sf);
112 }
113
114 private void writeStoreFile(final StoreFile.Writer writer) throws IOException {
115 writeStoreFile(writer, Bytes.toBytes(getName()), Bytes.toBytes(getName()));
116 }
117
118
119 byte[] SPLITKEY = new byte[] { (LAST_CHAR + FIRST_CHAR)/2, FIRST_CHAR};
120
121
122
123
124
125
126
127 public static void writeStoreFile(final StoreFile.Writer writer, byte[] fam, byte[] qualifier)
128 throws IOException {
129 long now = System.currentTimeMillis();
130 try {
131 for (char d = FIRST_CHAR; d <= LAST_CHAR; d++) {
132 for (char e = FIRST_CHAR; e <= LAST_CHAR; e++) {
133 byte[] b = new byte[] { (byte) d, (byte) e };
134 writer.append(new KeyValue(b, fam, qualifier, now, b));
135 }
136 }
137 } finally {
138 writer.close();
139 }
140 }
141
142
143
144
145
146
147 public void testReference() throws IOException {
148 final HRegionInfo hri = new HRegionInfo(TableName.valueOf("testReferenceTb"));
149 HRegionFileSystem regionFs = HRegionFileSystem.createRegionOnFileSystem(
150 conf, fs, new Path(this.testDir, hri.getTable().getNameAsString()), hri);
151
152 HFileContext meta = new HFileContextBuilder().withBlockSize(8 * 1024).build();
153
154 StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
155 .withFilePath(regionFs.createTempName())
156 .withFileContext(meta)
157 .build();
158 writeStoreFile(writer);
159
160 Path hsfPath = regionFs.commitStoreFile(TEST_FAMILY, writer.getPath());
161 StoreFile hsf = new StoreFile(this.fs, hsfPath, conf, cacheConf,
162 BloomType.NONE);
163 StoreFile.Reader reader = hsf.createReader();
164
165
166
167
168 KeyValue kv = KeyValue.createKeyValueFromKey(reader.midkey());
169 byte [] midRow = kv.getRow();
170 kv = KeyValue.createKeyValueFromKey(reader.getLastKey());
171 byte [] finalRow = kv.getRow();
172 hsf.closeReader(true);
173
174
175 HRegionInfo splitHri = new HRegionInfo(hri.getTable(), null, midRow);
176 Path refPath = splitStoreFile(regionFs, splitHri, TEST_FAMILY, hsf, midRow, true);
177 StoreFile refHsf = new StoreFile(this.fs, refPath, conf, cacheConf,
178 BloomType.NONE);
179
180
181 HFileScanner s = refHsf.createReader().getScanner(false, false);
182 for(boolean first = true; (!s.isSeeked() && s.seekTo()) || s.next();) {
183 ByteBuffer bb = s.getKey();
184 kv = KeyValue.createKeyValueFromKey(bb);
185 if (first) {
186 assertTrue(Bytes.equals(kv.getRow(), midRow));
187 first = false;
188 }
189 }
190 assertTrue(Bytes.equals(kv.getRow(), finalRow));
191 }
192
193 public void testHFileLink() throws IOException {
194 final HRegionInfo hri = new HRegionInfo(TableName.valueOf("testHFileLinkTb"));
195
196 Configuration testConf = new Configuration(this.conf);
197 FSUtils.setRootDir(testConf, this.testDir);
198 HRegionFileSystem regionFs = HRegionFileSystem.createRegionOnFileSystem(
199 testConf, fs, FSUtils.getTableDir(this.testDir, hri.getTable()), hri);
200 HFileContext meta = new HFileContextBuilder().withBlockSize(8 * 1024).build();
201
202
203 StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
204 .withFilePath(regionFs.createTempName())
205 .withFileContext(meta)
206 .build();
207 writeStoreFile(writer);
208
209 Path storeFilePath = regionFs.commitStoreFile(TEST_FAMILY, writer.getPath());
210 Path dstPath = new Path(regionFs.getTableDir(), new Path("test-region", TEST_FAMILY));
211 HFileLink.create(testConf, this.fs, dstPath, hri, storeFilePath.getName());
212 Path linkFilePath = new Path(dstPath,
213 HFileLink.createHFileLinkName(hri, storeFilePath.getName()));
214
215
216 StoreFileInfo storeFileInfo = new StoreFileInfo(testConf, this.fs, linkFilePath);
217 StoreFile hsf = new StoreFile(this.fs, storeFileInfo, testConf, cacheConf,
218 BloomType.NONE);
219 assertTrue(storeFileInfo.isLink());
220
221
222 int count = 1;
223 HFileScanner s = hsf.createReader().getScanner(false, false);
224 s.seekTo();
225 while (s.next()) {
226 count++;
227 }
228 assertEquals((LAST_CHAR - FIRST_CHAR + 1) * (LAST_CHAR - FIRST_CHAR + 1), count);
229 }
230
231
232
233
234
235 public void testReferenceToHFileLink() throws IOException {
236
237 Configuration testConf = new Configuration(this.conf);
238 FSUtils.setRootDir(testConf, this.testDir);
239
240
241 HRegionInfo hri = new HRegionInfo(TableName.valueOf("_original-evil-name"));
242 HRegionFileSystem regionFs = HRegionFileSystem.createRegionOnFileSystem(
243 testConf, fs, FSUtils.getTableDir(this.testDir, hri.getTable()), hri);
244
245 HFileContext meta = new HFileContextBuilder().withBlockSize(8 * 1024).build();
246
247 StoreFile.Writer writer = new StoreFile.WriterBuilder(testConf, cacheConf, this.fs)
248 .withFilePath(regionFs.createTempName())
249 .withFileContext(meta)
250 .build();
251 writeStoreFile(writer);
252 Path storeFilePath = regionFs.commitStoreFile(TEST_FAMILY, writer.getPath());
253
254
255 HRegionInfo hriClone = new HRegionInfo(TableName.valueOf("clone"));
256 HRegionFileSystem cloneRegionFs = HRegionFileSystem.createRegionOnFileSystem(
257 testConf, fs, FSUtils.getTableDir(this.testDir, hri.getTable()),
258 hriClone);
259 Path dstPath = cloneRegionFs.getStoreDir(TEST_FAMILY);
260 HFileLink.create(testConf, this.fs, dstPath, hri, storeFilePath.getName());
261 Path linkFilePath = new Path(dstPath,
262 HFileLink.createHFileLinkName(hri, storeFilePath.getName()));
263
264
265
266
267 HRegionInfo splitHriA = new HRegionInfo(hri.getTable(), null, SPLITKEY);
268 HRegionInfo splitHriB = new HRegionInfo(hri.getTable(), SPLITKEY, null);
269 StoreFile f = new StoreFile(fs, linkFilePath, testConf, cacheConf, BloomType.NONE);
270 f.createReader();
271 Path pathA = splitStoreFile(cloneRegionFs, splitHriA, TEST_FAMILY, f, SPLITKEY, true);
272 Path pathB = splitStoreFile(cloneRegionFs, splitHriB, TEST_FAMILY, f, SPLITKEY, false);
273 f.closeReader(true);
274
275 FSUtils.logFileSystemState(fs, this.testDir, LOG);
276
277
278
279
280
281 StoreFile hsfA = new StoreFile(this.fs, pathA, testConf, cacheConf,
282 BloomType.NONE);
283
284
285 int count = 1;
286 HFileScanner s = hsfA.createReader().getScanner(false, false);
287 s.seekTo();
288 while (s.next()) {
289 count++;
290 }
291 assertTrue(count > 0);
292
293
294 StoreFile hsfB = new StoreFile(this.fs, pathB, testConf, cacheConf,
295 BloomType.NONE);
296
297
298 HFileScanner sB = hsfB.createReader().getScanner(false, false);
299 sB.seekTo();
300
301
302 count++;
303 while (sB.next()) {
304 count++;
305 }
306
307
308 assertEquals((LAST_CHAR - FIRST_CHAR + 1) * (LAST_CHAR - FIRST_CHAR + 1), count);
309 }
310
311 private void checkHalfHFile(final HRegionFileSystem regionFs, final StoreFile f)
312 throws IOException {
313 byte [] midkey = f.createReader().midkey();
314 KeyValue midKV = KeyValue.createKeyValueFromKey(midkey);
315 byte [] midRow = midKV.getRow();
316
317 HRegionInfo topHri = new HRegionInfo(regionFs.getRegionInfo().getTable(),
318 null, midRow);
319 Path topPath = splitStoreFile(regionFs, topHri, TEST_FAMILY, f, midRow, true);
320
321 HRegionInfo bottomHri = new HRegionInfo(regionFs.getRegionInfo().getTable(),
322 midRow, null);
323 Path bottomPath = splitStoreFile(regionFs, bottomHri, TEST_FAMILY, f, midRow, false);
324
325 StoreFile.Reader top = new StoreFile(
326 this.fs, topPath, conf, cacheConf, BloomType.NONE).createReader();
327 StoreFile.Reader bottom = new StoreFile(
328 this.fs, bottomPath, conf, cacheConf, BloomType.NONE).createReader();
329 ByteBuffer previous = null;
330 LOG.info("Midkey: " + midKV.toString());
331 ByteBuffer bbMidkeyBytes = ByteBuffer.wrap(midkey);
332 try {
333
334
335
336
337 boolean first = true;
338 ByteBuffer key = null;
339 HFileScanner topScanner = top.getScanner(false, false);
340 while ((!topScanner.isSeeked() && topScanner.seekTo()) ||
341 (topScanner.isSeeked() && topScanner.next())) {
342 key = topScanner.getKey();
343
344 if (topScanner.getReader().getComparator().compareFlatKey(key.array(),
345 key.arrayOffset(), key.limit(), midkey, 0, midkey.length) < 0) {
346 fail("key=" + Bytes.toStringBinary(key) + " < midkey=" +
347 Bytes.toStringBinary(midkey));
348 }
349 if (first) {
350 first = false;
351 LOG.info("First in top: " + Bytes.toString(Bytes.toBytes(key)));
352 }
353 }
354 LOG.info("Last in top: " + Bytes.toString(Bytes.toBytes(key)));
355
356 first = true;
357 HFileScanner bottomScanner = bottom.getScanner(false, false);
358 while ((!bottomScanner.isSeeked() && bottomScanner.seekTo()) ||
359 bottomScanner.next()) {
360 previous = bottomScanner.getKey();
361 key = bottomScanner.getKey();
362 if (first) {
363 first = false;
364 LOG.info("First in bottom: " +
365 Bytes.toString(Bytes.toBytes(previous)));
366 }
367 assertTrue(key.compareTo(bbMidkeyBytes) < 0);
368 }
369 if (previous != null) {
370 LOG.info("Last in bottom: " + Bytes.toString(Bytes.toBytes(previous)));
371 }
372
373 regionFs.cleanupDaughterRegion(topHri);
374 regionFs.cleanupDaughterRegion(bottomHri);
375
376
377
378
379 byte [] badmidkey = Bytes.toBytes(" .");
380 assertTrue(fs.exists(f.getPath()));
381 topPath = splitStoreFile(regionFs, topHri, TEST_FAMILY, f, badmidkey, true);
382 bottomPath = splitStoreFile(regionFs, bottomHri, TEST_FAMILY, f, badmidkey, false);
383
384 assertNull(bottomPath);
385
386 top = new StoreFile(this.fs, topPath, conf, cacheConf, BloomType.NONE).createReader();
387
388 first = true;
389 topScanner = top.getScanner(false, false);
390 while ((!topScanner.isSeeked() && topScanner.seekTo()) ||
391 topScanner.next()) {
392 key = topScanner.getKey();
393 assertTrue(topScanner.getReader().getComparator().compareFlatKey(key.array(),
394 key.arrayOffset(), key.limit(), badmidkey, 0, badmidkey.length) >= 0);
395 if (first) {
396 first = false;
397 KeyValue keyKV = KeyValue.createKeyValueFromKey(key);
398 LOG.info("First top when key < bottom: " + keyKV);
399 String tmp = Bytes.toString(keyKV.getRow());
400 for (int i = 0; i < tmp.length(); i++) {
401 assertTrue(tmp.charAt(i) == 'a');
402 }
403 }
404 }
405 KeyValue keyKV = KeyValue.createKeyValueFromKey(key);
406 LOG.info("Last top when key < bottom: " + keyKV);
407 String tmp = Bytes.toString(keyKV.getRow());
408 for (int i = 0; i < tmp.length(); i++) {
409 assertTrue(tmp.charAt(i) == 'z');
410 }
411
412 regionFs.cleanupDaughterRegion(topHri);
413 regionFs.cleanupDaughterRegion(bottomHri);
414
415
416 badmidkey = Bytes.toBytes("|||");
417 topPath = splitStoreFile(regionFs,topHri, TEST_FAMILY, f, badmidkey, true);
418 bottomPath = splitStoreFile(regionFs, bottomHri, TEST_FAMILY, f, badmidkey, false);
419 assertNull(topPath);
420 bottom = new StoreFile(this.fs, bottomPath, conf, cacheConf,
421 BloomType.NONE).createReader();
422 first = true;
423 bottomScanner = bottom.getScanner(false, false);
424 while ((!bottomScanner.isSeeked() && bottomScanner.seekTo()) ||
425 bottomScanner.next()) {
426 key = bottomScanner.getKey();
427 if (first) {
428 first = false;
429 keyKV = KeyValue.createKeyValueFromKey(key);
430 LOG.info("First bottom when key > top: " + keyKV);
431 tmp = Bytes.toString(keyKV.getRow());
432 for (int i = 0; i < tmp.length(); i++) {
433 assertTrue(tmp.charAt(i) == 'a');
434 }
435 }
436 }
437 keyKV = KeyValue.createKeyValueFromKey(key);
438 LOG.info("Last bottom when key > top: " + keyKV);
439 for (int i = 0; i < tmp.length(); i++) {
440 assertTrue(Bytes.toString(keyKV.getRow()).charAt(i) == 'z');
441 }
442 } finally {
443 if (top != null) {
444 top.close(true);
445 }
446 if (bottom != null) {
447 bottom.close(true);
448 }
449 fs.delete(f.getPath(), true);
450 }
451 }
452
453 private static final String localFormatter = "%010d";
454
455 private void bloomWriteRead(StoreFile.Writer writer, FileSystem fs) throws Exception {
456 float err = conf.getFloat(BloomFilterFactory.IO_STOREFILE_BLOOM_ERROR_RATE, 0);
457 Path f = writer.getPath();
458 long now = System.currentTimeMillis();
459 for (int i = 0; i < 2000; i += 2) {
460 String row = String.format(localFormatter, i);
461 KeyValue kv = new KeyValue(row.getBytes(), "family".getBytes(),
462 "col".getBytes(), now, "value".getBytes());
463 writer.append(kv);
464 }
465 writer.close();
466
467 StoreFile.Reader reader = new StoreFile.Reader(fs, f, cacheConf, conf);
468 reader.loadFileInfo();
469 reader.loadBloomfilter();
470 StoreFileScanner scanner = reader.getStoreFileScanner(false, false);
471
472
473 int falsePos = 0;
474 int falseNeg = 0;
475 for (int i = 0; i < 2000; i++) {
476 String row = String.format(localFormatter, i);
477 TreeSet<byte[]> columns = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
478 columns.add("family:col".getBytes());
479
480 Scan scan = new Scan(row.getBytes(),row.getBytes());
481 scan.addColumn("family".getBytes(), "family:col".getBytes());
482 boolean exists = scanner.shouldUseScanner(scan, columns, Long.MIN_VALUE);
483 if (i % 2 == 0) {
484 if (!exists) falseNeg++;
485 } else {
486 if (exists) falsePos++;
487 }
488 }
489 reader.close(true);
490 fs.delete(f, true);
491 assertEquals("False negatives: " + falseNeg, 0, falseNeg);
492 int maxFalsePos = (int) (2 * 2000 * err);
493 assertTrue("Too many false positives: " + falsePos + " (err=" + err
494 + ", expected no more than " + maxFalsePos + ")",
495 falsePos <= maxFalsePos);
496 }
497
498 private static final int BLOCKSIZE_SMALL = 8192;
499
500 public void testBloomFilter() throws Exception {
501 FileSystem fs = FileSystem.getLocal(conf);
502 conf.setFloat(BloomFilterFactory.IO_STOREFILE_BLOOM_ERROR_RATE, (float) 0.01);
503 conf.setBoolean(BloomFilterFactory.IO_STOREFILE_BLOOM_ENABLED, true);
504
505
506 Path f = new Path(ROOT_DIR, getName());
507 HFileContext meta = new HFileContextBuilder().withBlockSize(BLOCKSIZE_SMALL)
508 .withChecksumType(CKTYPE)
509 .withBytesPerCheckSum(CKBYTES).build();
510
511 StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
512 .withFilePath(f)
513 .withBloomType(BloomType.ROW)
514 .withMaxKeyCount(2000)
515 .withFileContext(meta)
516 .build();
517 bloomWriteRead(writer, fs);
518 }
519
520 public void testDeleteFamilyBloomFilter() throws Exception {
521 FileSystem fs = FileSystem.getLocal(conf);
522 conf.setFloat(BloomFilterFactory.IO_STOREFILE_BLOOM_ERROR_RATE, (float) 0.01);
523 conf.setBoolean(BloomFilterFactory.IO_STOREFILE_BLOOM_ENABLED, true);
524 float err = conf.getFloat(BloomFilterFactory.IO_STOREFILE_BLOOM_ERROR_RATE, 0);
525
526
527 Path f = new Path(ROOT_DIR, getName());
528
529 HFileContext meta = new HFileContextBuilder()
530 .withBlockSize(BLOCKSIZE_SMALL)
531 .withChecksumType(CKTYPE)
532 .withBytesPerCheckSum(CKBYTES).build();
533
534 StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
535 .withFilePath(f)
536 .withMaxKeyCount(2000)
537 .withFileContext(meta)
538 .build();
539
540
541 long now = System.currentTimeMillis();
542 for (int i = 0; i < 2000; i += 2) {
543 String row = String.format(localFormatter, i);
544 KeyValue kv = new KeyValue(row.getBytes(), "family".getBytes(),
545 "col".getBytes(), now, KeyValue.Type.DeleteFamily, "value".getBytes());
546 writer.append(kv);
547 }
548 writer.close();
549
550 StoreFile.Reader reader = new StoreFile.Reader(fs, f, cacheConf, conf);
551 reader.loadFileInfo();
552 reader.loadBloomfilter();
553
554
555 int falsePos = 0;
556 int falseNeg = 0;
557 for (int i = 0; i < 2000; i++) {
558 String row = String.format(localFormatter, i);
559 byte[] rowKey = Bytes.toBytes(row);
560 boolean exists = reader.passesDeleteFamilyBloomFilter(rowKey, 0,
561 rowKey.length);
562 if (i % 2 == 0) {
563 if (!exists)
564 falseNeg++;
565 } else {
566 if (exists)
567 falsePos++;
568 }
569 }
570 assertEquals(1000, reader.getDeleteFamilyCnt());
571 reader.close(true);
572 fs.delete(f, true);
573 assertEquals("False negatives: " + falseNeg, 0, falseNeg);
574 int maxFalsePos = (int) (2 * 2000 * err);
575 assertTrue("Too many false positives: " + falsePos + " (err=" + err
576 + ", expected no more than " + maxFalsePos, falsePos <= maxFalsePos);
577 }
578
579
580
581
582 public void testReseek() throws Exception {
583
584 Path f = new Path(ROOT_DIR, getName());
585 HFileContext meta = new HFileContextBuilder().withBlockSize(8 * 1024).build();
586
587 StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
588 .withFilePath(f)
589 .withFileContext(meta)
590 .build();
591
592 writeStoreFile(writer);
593 writer.close();
594
595 StoreFile.Reader reader = new StoreFile.Reader(fs, f, cacheConf, conf);
596
597
598
599 KeyValue k = KeyValueUtil.createFirstOnRow(HConstants.EMPTY_BYTE_ARRAY);
600 StoreFileScanner s = reader.getStoreFileScanner(false, false);
601 s.reseek(k);
602
603 assertNotNull("Intial reseek should position at the beginning of the file", s.peek());
604 }
605
606 public void testBloomTypes() throws Exception {
607 float err = (float) 0.01;
608 FileSystem fs = FileSystem.getLocal(conf);
609 conf.setFloat(BloomFilterFactory.IO_STOREFILE_BLOOM_ERROR_RATE, err);
610 conf.setBoolean(BloomFilterFactory.IO_STOREFILE_BLOOM_ENABLED, true);
611
612 int rowCount = 50;
613 int colCount = 10;
614 int versions = 2;
615
616
617 BloomType[] bt = {BloomType.ROWCOL, BloomType.ROW};
618 int[] expKeys = {rowCount*colCount, rowCount};
619
620
621
622
623 float[] expErr = {2*rowCount*colCount*err, 2*rowCount*2*colCount*err};
624
625 for (int x : new int[]{0,1}) {
626
627 Path f = new Path(ROOT_DIR, getName() + x);
628 HFileContext meta = new HFileContextBuilder().withBlockSize(BLOCKSIZE_SMALL)
629 .withChecksumType(CKTYPE)
630 .withBytesPerCheckSum(CKBYTES).build();
631
632 StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
633 .withFilePath(f)
634 .withBloomType(bt[x])
635 .withMaxKeyCount(expKeys[x])
636 .withFileContext(meta)
637 .build();
638
639 long now = System.currentTimeMillis();
640 for (int i = 0; i < rowCount*2; i += 2) {
641 for (int j = 0; j < colCount*2; j += 2) {
642 String row = String.format(localFormatter, i);
643 String col = String.format(localFormatter, j);
644 for (int k= 0; k < versions; ++k) {
645 KeyValue kv = new KeyValue(row.getBytes(),
646 "family".getBytes(), ("col" + col).getBytes(),
647 now-k, Bytes.toBytes((long)-1));
648 writer.append(kv);
649 }
650 }
651 }
652 writer.close();
653
654 StoreFile.Reader reader = new StoreFile.Reader(fs, f, cacheConf, conf);
655 reader.loadFileInfo();
656 reader.loadBloomfilter();
657 StoreFileScanner scanner = reader.getStoreFileScanner(false, false);
658 assertEquals(expKeys[x], reader.generalBloomFilter.getKeyCount());
659
660
661 int falsePos = 0;
662 int falseNeg = 0;
663 for (int i = 0; i < rowCount*2; ++i) {
664 for (int j = 0; j < colCount*2; ++j) {
665 String row = String.format(localFormatter, i);
666 String col = String.format(localFormatter, j);
667 TreeSet<byte[]> columns = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
668 columns.add(("col" + col).getBytes());
669
670 Scan scan = new Scan(row.getBytes(),row.getBytes());
671 scan.addColumn("family".getBytes(), ("col"+col).getBytes());
672 boolean exists =
673 scanner.shouldUseScanner(scan, columns, Long.MIN_VALUE);
674 boolean shouldRowExist = i % 2 == 0;
675 boolean shouldColExist = j % 2 == 0;
676 shouldColExist = shouldColExist || bt[x] == BloomType.ROW;
677 if (shouldRowExist && shouldColExist) {
678 if (!exists) falseNeg++;
679 } else {
680 if (exists) falsePos++;
681 }
682 }
683 }
684 reader.close(true);
685 fs.delete(f, true);
686 System.out.println(bt[x].toString());
687 System.out.println(" False negatives: " + falseNeg);
688 System.out.println(" False positives: " + falsePos);
689 assertEquals(0, falseNeg);
690 assertTrue(falsePos < 2*expErr[x]);
691 }
692 }
693
694 public void testSeqIdComparator() {
695 assertOrdering(StoreFile.Comparators.SEQ_ID,
696 mockStoreFile(true, 100, 1000, -1, "/foo/123"),
697 mockStoreFile(true, 100, 1000, -1, "/foo/124"),
698 mockStoreFile(true, 99, 1000, -1, "/foo/126"),
699 mockStoreFile(true, 98, 2000, -1, "/foo/126"),
700 mockStoreFile(false, 3453, -1, 1, "/foo/1"),
701 mockStoreFile(false, 2, -1, 3, "/foo/2"),
702 mockStoreFile(false, 1000, -1, 5, "/foo/2"),
703 mockStoreFile(false, 76, -1, 5, "/foo/3"));
704 }
705
706
707
708
709
710 private void assertOrdering(Comparator<StoreFile> comparator, StoreFile ... sfs) {
711 ArrayList<StoreFile> sorted = Lists.newArrayList(sfs);
712 Collections.shuffle(sorted);
713 Collections.sort(sorted, comparator);
714 LOG.debug("sfs: " + Joiner.on(",").join(sfs));
715 LOG.debug("sorted: " + Joiner.on(",").join(sorted));
716 assertTrue(Iterables.elementsEqual(Arrays.asList(sfs), sorted));
717 }
718
719
720
721
722 private StoreFile mockStoreFile(boolean bulkLoad,
723 long size,
724 long bulkTimestamp,
725 long seqId,
726 String path) {
727 StoreFile mock = Mockito.mock(StoreFile.class);
728 StoreFile.Reader reader = Mockito.mock(StoreFile.Reader.class);
729
730 Mockito.doReturn(size).when(reader).length();
731
732 Mockito.doReturn(reader).when(mock).getReader();
733 Mockito.doReturn(bulkLoad).when(mock).isBulkLoadResult();
734 Mockito.doReturn(bulkTimestamp).when(mock).getBulkLoadTimestamp();
735 Mockito.doReturn(seqId).when(mock).getMaxSequenceId();
736 Mockito.doReturn(new Path(path)).when(mock).getPath();
737 String name = "mock storefile, bulkLoad=" + bulkLoad +
738 " bulkTimestamp=" + bulkTimestamp +
739 " seqId=" + seqId +
740 " path=" + path;
741 Mockito.doReturn(name).when(mock).toString();
742 return mock;
743 }
744
745
746
747
748
749
750
751
752
753 List<KeyValue> getKeyValueSet(long[] timestamps, int numRows,
754 byte[] qualifier, byte[] family) {
755 List<KeyValue> kvList = new ArrayList<KeyValue>();
756 for (int i=1;i<=numRows;i++) {
757 byte[] b = Bytes.toBytes(i) ;
758 LOG.info(Bytes.toString(b));
759 LOG.info(Bytes.toString(b));
760 for (long timestamp: timestamps)
761 {
762 kvList.add(new KeyValue(b, family, qualifier, timestamp, b));
763 }
764 }
765 return kvList;
766 }
767
768
769
770
771
772 public void testMultipleTimestamps() throws IOException {
773 byte[] family = Bytes.toBytes("familyname");
774 byte[] qualifier = Bytes.toBytes("qualifier");
775 int numRows = 10;
776 long[] timestamps = new long[] {20,10,5,1};
777 Scan scan = new Scan();
778
779
780 Path storedir = new Path(new Path(this.testDir, "7e0102"), "familyname");
781 Path dir = new Path(storedir, "1234567890");
782 HFileContext meta = new HFileContextBuilder().withBlockSize(8 * 1024).build();
783
784 StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
785 .withOutputDir(dir)
786 .withFileContext(meta)
787 .build();
788
789 List<KeyValue> kvList = getKeyValueSet(timestamps,numRows,
790 family, qualifier);
791
792 for (KeyValue kv : kvList) {
793 writer.append(kv);
794 }
795 writer.appendMetadata(0, false);
796 writer.close();
797
798 StoreFile hsf = new StoreFile(this.fs, writer.getPath(), conf, cacheConf,
799 BloomType.NONE);
800 StoreFile.Reader reader = hsf.createReader();
801 StoreFileScanner scanner = reader.getStoreFileScanner(false, false);
802 TreeSet<byte[]> columns = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
803 columns.add(qualifier);
804
805 scan.setTimeRange(20, 100);
806 assertTrue(scanner.shouldUseScanner(scan, columns, Long.MIN_VALUE));
807
808 scan.setTimeRange(1, 2);
809 assertTrue(scanner.shouldUseScanner(scan, columns, Long.MIN_VALUE));
810
811 scan.setTimeRange(8, 10);
812 assertTrue(scanner.shouldUseScanner(scan, columns, Long.MIN_VALUE));
813
814 scan.setTimeRange(7, 50);
815 assertTrue(scanner.shouldUseScanner(scan, columns, Long.MIN_VALUE));
816
817
818 scan.setTimeRange(27, 50);
819 assertTrue(!scanner.shouldUseScanner(scan, columns, Long.MIN_VALUE));
820 }
821
822 public void testCacheOnWriteEvictOnClose() throws Exception {
823 Configuration conf = this.conf;
824
825
826 Path baseDir = new Path(new Path(this.testDir, "7e0102"),"twoCOWEOC");
827
828
829 BlockCache bc = new CacheConfig(conf).getBlockCache();
830 assertNotNull(bc);
831 CacheStats cs = bc.getStats();
832 long startHit = cs.getHitCount();
833 long startMiss = cs.getMissCount();
834 long startEvicted = cs.getEvictedCount();
835
836
837 conf.setBoolean(CacheConfig.CACHE_BLOCKS_ON_WRITE_KEY, false);
838 CacheConfig cacheConf = new CacheConfig(conf);
839 Path pathCowOff = new Path(baseDir, "123456789");
840 StoreFile.Writer writer = writeStoreFile(conf, cacheConf, pathCowOff, 3);
841 StoreFile hsf = new StoreFile(this.fs, writer.getPath(), conf, cacheConf,
842 BloomType.NONE);
843 LOG.debug(hsf.getPath().toString());
844
845
846 StoreFile.Reader reader = hsf.createReader();
847 reader.loadFileInfo();
848 StoreFileScanner scanner = reader.getStoreFileScanner(true, true);
849 scanner.seek(KeyValue.LOWESTKEY);
850 while (scanner.next() != null);
851 assertEquals(startHit, cs.getHitCount());
852 assertEquals(startMiss + 3, cs.getMissCount());
853 assertEquals(startEvicted, cs.getEvictedCount());
854 startMiss += 3;
855 scanner.close();
856 reader.close(cacheConf.shouldEvictOnClose());
857
858
859 conf.setBoolean(CacheConfig.CACHE_BLOCKS_ON_WRITE_KEY, true);
860 cacheConf = new CacheConfig(conf);
861 Path pathCowOn = new Path(baseDir, "123456788");
862 writer = writeStoreFile(conf, cacheConf, pathCowOn, 3);
863 hsf = new StoreFile(this.fs, writer.getPath(), conf, cacheConf,
864 BloomType.NONE);
865
866
867 reader = hsf.createReader();
868 scanner = reader.getStoreFileScanner(true, true);
869 scanner.seek(KeyValue.LOWESTKEY);
870 while (scanner.next() != null);
871 assertEquals(startHit + 3, cs.getHitCount());
872 assertEquals(startMiss, cs.getMissCount());
873 assertEquals(startEvicted, cs.getEvictedCount());
874 startHit += 3;
875 scanner.close();
876 reader.close(cacheConf.shouldEvictOnClose());
877
878
879 hsf = new StoreFile(this.fs, pathCowOff, conf, cacheConf,
880 BloomType.NONE);
881 StoreFile.Reader readerOne = hsf.createReader();
882 readerOne.loadFileInfo();
883 StoreFileScanner scannerOne = readerOne.getStoreFileScanner(true, true);
884 scannerOne.seek(KeyValue.LOWESTKEY);
885 hsf = new StoreFile(this.fs, pathCowOn, conf, cacheConf,
886 BloomType.NONE);
887 StoreFile.Reader readerTwo = hsf.createReader();
888 readerTwo.loadFileInfo();
889 StoreFileScanner scannerTwo = readerTwo.getStoreFileScanner(true, true);
890 scannerTwo.seek(KeyValue.LOWESTKEY);
891 Cell kv1 = null;
892 Cell kv2 = null;
893 while ((kv1 = scannerOne.next()) != null) {
894 kv2 = scannerTwo.next();
895 assertTrue(kv1.equals(kv2));
896 KeyValue keyv1 = KeyValueUtil.ensureKeyValue(kv1);
897 KeyValue keyv2 = KeyValueUtil.ensureKeyValue(kv2);
898 assertTrue(Bytes.compareTo(
899 keyv1.getBuffer(), keyv1.getKeyOffset(), keyv1.getKeyLength(),
900 keyv2.getBuffer(), keyv2.getKeyOffset(), keyv2.getKeyLength()) == 0);
901 assertTrue(Bytes.compareTo(
902 kv1.getValueArray(), kv1.getValueOffset(), kv1.getValueLength(),
903 kv2.getValueArray(), kv2.getValueOffset(), kv2.getValueLength()) == 0);
904 }
905 assertNull(scannerTwo.next());
906 assertEquals(startHit + 6, cs.getHitCount());
907 assertEquals(startMiss, cs.getMissCount());
908 assertEquals(startEvicted, cs.getEvictedCount());
909 startHit += 6;
910 scannerOne.close();
911 readerOne.close(cacheConf.shouldEvictOnClose());
912 scannerTwo.close();
913 readerTwo.close(cacheConf.shouldEvictOnClose());
914
915
916 conf.setBoolean("hbase.rs.evictblocksonclose", true);
917 cacheConf = new CacheConfig(conf);
918 hsf = new StoreFile(this.fs, pathCowOff, conf, cacheConf,
919 BloomType.NONE);
920 reader = hsf.createReader();
921 reader.close(cacheConf.shouldEvictOnClose());
922
923
924 assertEquals(startHit, cs.getHitCount());
925 assertEquals(startMiss, cs.getMissCount());
926 assertEquals(startEvicted + 3, cs.getEvictedCount());
927 startEvicted += 3;
928
929
930 conf.setBoolean("hbase.rs.evictblocksonclose", false);
931 cacheConf = new CacheConfig(conf);
932 hsf = new StoreFile(this.fs, pathCowOn, conf, cacheConf,
933 BloomType.NONE);
934 reader = hsf.createReader();
935 reader.close(cacheConf.shouldEvictOnClose());
936
937
938 assertEquals(startHit, cs.getHitCount());
939 assertEquals(startMiss, cs.getMissCount());
940 assertEquals(startEvicted, cs.getEvictedCount());
941 }
942
943 private Path splitStoreFile(final HRegionFileSystem regionFs, final HRegionInfo hri,
944 final String family, final StoreFile sf, final byte[] splitKey, boolean isTopRef)
945 throws IOException {
946 FileSystem fs = regionFs.getFileSystem();
947 Path path = regionFs.splitStoreFile(hri, family, sf, splitKey, isTopRef, null);
948 if (null == path) {
949 return null;
950 }
951 Path regionDir = regionFs.commitDaughterRegion(hri);
952 return new Path(new Path(regionDir, family), path.getName());
953 }
954
955 private StoreFile.Writer writeStoreFile(Configuration conf,
956 CacheConfig cacheConf, Path path, int numBlocks)
957 throws IOException {
958
959 int numKVs = 5 * numBlocks;
960 List<KeyValue> kvs = new ArrayList<KeyValue>(numKVs);
961 byte [] b = Bytes.toBytes("x");
962 int totalSize = 0;
963 for (int i=numKVs;i>0;i--) {
964 KeyValue kv = new KeyValue(b, b, b, i, b);
965 kvs.add(kv);
966
967 totalSize += kv.getLength() + 1;
968 }
969 int blockSize = totalSize / numBlocks;
970 HFileContext meta = new HFileContextBuilder().withBlockSize(blockSize)
971 .withChecksumType(CKTYPE)
972 .withBytesPerCheckSum(CKBYTES)
973 .build();
974
975 StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
976 .withFilePath(path)
977 .withMaxKeyCount(2000)
978 .withFileContext(meta)
979 .build();
980
981 kvs.remove(kvs.size()-1);
982 for (KeyValue kv : kvs) {
983 writer.append(kv);
984 }
985 writer.appendMetadata(0, false);
986 writer.close();
987 return writer;
988 }
989
990
991
992
993
994 public void testDataBlockEncodingMetaData() throws IOException {
995
996 Path dir = new Path(new Path(this.testDir, "7e0102"), "familyname");
997 Path path = new Path(dir, "1234567890");
998
999 DataBlockEncoding dataBlockEncoderAlgo =
1000 DataBlockEncoding.FAST_DIFF;
1001 HFileDataBlockEncoder dataBlockEncoder =
1002 new HFileDataBlockEncoderImpl(
1003 dataBlockEncoderAlgo);
1004 cacheConf = new CacheConfig(conf);
1005 HFileContext meta = new HFileContextBuilder().withBlockSize(BLOCKSIZE_SMALL)
1006 .withChecksumType(CKTYPE)
1007 .withBytesPerCheckSum(CKBYTES)
1008 .withDataBlockEncoding(dataBlockEncoderAlgo)
1009 .build();
1010
1011 StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
1012 .withFilePath(path)
1013 .withMaxKeyCount(2000)
1014 .withFileContext(meta)
1015 .build();
1016 writer.close();
1017
1018 StoreFile storeFile = new StoreFile(fs, writer.getPath(), conf,
1019 cacheConf, BloomType.NONE);
1020 StoreFile.Reader reader = storeFile.createReader();
1021
1022 Map<byte[], byte[]> fileInfo = reader.loadFileInfo();
1023 byte[] value = fileInfo.get(HFileDataBlockEncoder.DATA_BLOCK_ENCODING);
1024 assertEquals(dataBlockEncoderAlgo.getNameInBytes(), value);
1025 }
1026 }
1027