View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
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   * Test HStoreFile
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     * Write a file and then assert that we can read from top and bottom halves
97     * using two HalfMapFiles.
98     * @throws Exception
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   // pick an split point (roughly halfway)
124   byte[] SPLITKEY = new byte[] { (LAST_CHAR + FIRST_CHAR)/2, FIRST_CHAR};
125 
126   /*
127    * Writes HStoreKey and ImmutableBytes data to passed writer and
128    * then closes it.
129    * @param writer
130    * @throws IOException
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    * Test that our mechanism of writing store files in one region to reference
149    * store files in other regions works.
150    * @throws IOException
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     // Make a store file and write data to it.
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     // Split on a row, not in middle of row.  Midkey returned by reader
170     // may be in middle of row.  Create new one with empty column and
171     // timestamp.
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     // Make a reference
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     // Now confirm that I can read from the reference and that it only gets
185     // keys from top half of the file.
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     // force temp data in hbase/target/test-data instead of /tmp/hbase-xxxx/
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     // Make a store file and write data to it.
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     // Try to open store file from link
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     // Now confirm that I can read from the link
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     // Make a store file and write data to it.
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     // Creates a reader for StoreFile
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     // Verify after instantiating scanner refCount is increased
252     assertTrue(scanner.getReader().isReferencedInReads());
253     scanner.close();
254     // Verify after closing scanner refCount is decreased
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    * This test creates an hfile and then the dir structures and files to verify that references
274    * to hfilelinks (created by snapshot clones) can be properly interpreted.
275    */
276   public void testReferenceToHFileLink() throws IOException {
277     // force temp data in hbase/target/test-data instead of /tmp/hbase-xxxx/
278     Configuration testConf = new Configuration(this.conf);
279     FSUtils.setRootDir(testConf, this.testDir);
280 
281     // adding legal table name chars to verify regex handles it.
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     // Make a store file and write data to it. <root>/<tablename>/<rgn>/<cf>/<file>
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     // create link to store file. <root>/clone/region/<cf>/<hfile>-<region>-<table>
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     // create splits of the link.
306     // <root>/clone/splitA/<cf>/<reftohfilelink>,
307     // <root>/clone/splitB/<cf>/<reftohfilelink>
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); // top
313     Path pathB = splitStoreFile(cloneRegionFs, splitHriB, TEST_FAMILY, f, SPLITKEY, false);// bottom
314     f.closeReader(true);
315     // OK test the thing
316     FSUtils.logFileSystemState(fs, this.testDir, LOG);
317 
318     // There is a case where a file with the hfilelink pattern is actually a daughter
319     // reference to a hfile link.  This code in StoreFile that handles this case.
320 
321     // Try to open store file from link
322     StoreFile hsfA = new StoreFile(this.fs, pathA, testConf, cacheConf,
323       BloomType.NONE);
324 
325     // Now confirm that I can read from the ref to link
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); // read some rows here
333 
334     // Try to open store file from link
335     StoreFile hsfB = new StoreFile(this.fs, pathB, testConf, cacheConf,
336       BloomType.NONE);
337 
338     // Now confirm that I can read from the ref to link
339     HFileScanner sB = hsfB.createReader().getScanner(false, false);
340     sB.seekTo();
341     
342     //count++ as seekTo() will advance the scanner
343     count++;
344     while (sB.next()) {
345       count++;
346     }
347 
348     // read the rest of the rows
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     // Create top split.
358     HRegionInfo topHri = new HRegionInfo(regionFs.getRegionInfo().getTable(),
359         null, midRow);
360     Path topPath = splitStoreFile(regionFs, topHri, TEST_FAMILY, f, midRow, true);
361     // Create bottom split.
362     HRegionInfo bottomHri = new HRegionInfo(regionFs.getRegionInfo().getTable(),
363         midRow, null);
364     Path bottomPath = splitStoreFile(regionFs, bottomHri, TEST_FAMILY, f, midRow, false);
365     // Make readers on top and bottom.
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       // Now make two HalfMapFiles and assert they can read the full backing
375       // file, one from the top and the other from the bottom.
376       // Test bottom half first.
377       // Now test reading from the top.
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       // Remove references.
414       regionFs.cleanupDaughterRegion(topHri);
415       regionFs.cleanupDaughterRegion(bottomHri);
416 
417       // Next test using a midkey that does not exist in the file.
418       // First, do a key that is < than first key. Ensure splits behave
419       // properly.
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       // Now read from the top.
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       // Remove references.
453       regionFs.cleanupDaughterRegion(topHri);
454       regionFs.cleanupDaughterRegion(bottomHri);
455 
456       // Test when badkey is > than last key in file ('||' > 'zz').
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); // evict since we are about to delete the file
486       }
487       if (bottom != null) {
488         bottom.close(true); // evict since we are about to delete the file
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     // check false positives rate
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); // evict because we are about to delete the file
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     // write the file
551     Path f = new Path(ROOT_DIR, getName());
552     HFileContext meta = new HFileContextBuilder().withBlockSize(BLOCKSIZE_SMALL)
553                         .withChecksumType(CKTYPE)
554                         .withBytesPerCheckSum(CKBYTES).build();
555     // Make a store file and write data to it.
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     // write the file
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     // Make a store file and write data to it.
579     StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
580             .withFilePath(f)
581             .withMaxKeyCount(2000)
582             .withFileContext(meta)
583             .build();
584 
585     // add delete family
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     // check false positives rate
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); // evict because we are about to delete the file
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    * Test for HBASE-8012
626    */
627   public void testReseek() throws Exception {
628     // write the file
629     Path f = new Path(ROOT_DIR, getName());
630     HFileContext meta = new HFileContextBuilder().withBlockSize(8 * 1024).build();
631     // Make a store file and write data to it.
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     // Now do reseek with empty KV to position to the beginning of the file
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     // run once using columns and once using rows
662     BloomType[] bt = {BloomType.ROWCOL, BloomType.ROW};
663     int[] expKeys  = {rowCount*colCount, rowCount};
664     // below line deserves commentary.  it is expected bloom false positives
665     //  column = rowCount*2*colCount inserts
666     //  row-level = only rowCount*2 inserts, but failures will be magnified by
667     //              2nd for loop for every column (2*colCount)
668     float[] expErr   = {2*rowCount*colCount*err, 2*rowCount*2*colCount*err};
669 
670     for (int x : new int[]{0,1}) {
671       // write the file
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       // Make a store file and write data to it.
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) { // rows
686         for (int j = 0; j < colCount*2; j += 2) {   // column qualifiers
687           String row = String.format(localFormatter, i);
688           String col = String.format(localFormatter, j);
689           for (int k= 0; k < versions; ++k) { // versions
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       // check false positives rate
710       int falsePos = 0;
711       int falseNeg = 0;
712       for (int i = 0; i < rowCount*2; ++i) { // rows
713         for (int j = 0; j < colCount*2; ++j) {   // column qualifiers
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); // evict because we are about to delete the file
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    * Assert that the given comparator orders the given storefiles in the
754    * same way that they're passed.
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    * Create a mock StoreFile with the given attributes.
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    * Generate a list of KeyValues for testing based on given parameters
793    * @param timestamps
794    * @param numRows
795    * @param qualifier
796    * @param family
797    * @return
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    * Test to ensure correctness when using StoreFile with multiple timestamps
816    * @throws IOException
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     // Make up a directory hierarchy that has a regiondir ("7e0102") and familyname.
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     // Make a store file and write data to it.
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     // lets make sure it still works with column family time ranges
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     // This test relies on the timestamp range optimization
870     scan = new Scan();
871     scan.setTimeRange(27, 50);
872     assertTrue(!scanner.shouldUseScanner(scan, store, Long.MIN_VALUE));
873 
874     // should still use the scanner because we override the family time range
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     // Find a home for our files (regiondir ("7e0102") and familyname).
886     Path baseDir = new Path(new Path(this.testDir, "7e0102"),"twoCOWEOC");
887 
888     // Grab the block cache and get the initial hit/miss counts
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     // Let's write a StoreFile with three blocks, with cache on write off
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     // Read this file, we should see 3 misses
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     // Now write a StoreFile with three blocks, with cache on write on
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     // Read this file, we should see 3 hits
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     // Let's read back the two files to ensure the blocks exactly match
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     // Let's close the first file with evict on close turned on
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     // We should have 3 new evictions
984     assertEquals(startHit, cs.getHitCount());
985     assertEquals(startMiss, cs.getMissCount());
986     assertEquals(startEvicted + 3, cs.getEvictedCount());
987     startEvicted += 3;
988 
989     // Let's close the second file with evict on close turned off
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     // We expect no changes
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     // Let's put ~5 small KVs in each block, so let's make 5*numBlocks KVs
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       // kv has memstoreTS 0, which takes 1 byte to store.
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     // Make a store file and write data to it.
1035     StoreFile.Writer writer = new StoreFile.WriterBuilder(conf, cacheConf, this.fs)
1036             .withFilePath(path)
1037             .withMaxKeyCount(2000)
1038             .withFileContext(meta)
1039             .build();
1040     // We'll write N-1 KVs to ensure we don't write an extra block
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    * Check if data block encoding information is saved correctly in HFile's
1052    * file info.
1053    */
1054   public void testDataBlockEncodingMetaData() throws IOException {
1055     // Make up a directory hierarchy that has a regiondir ("7e0102") and familyname.
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     // Make a store file and write data to it.
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