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