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