001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.util; 019 020import static org.apache.hadoop.hbase.regionserver.HStoreFile.BULKLOAD_TIME_KEY; 021import static org.junit.Assert.assertArrayEquals; 022import static org.junit.Assert.fail; 023 024import java.io.IOException; 025import java.util.Arrays; 026import java.util.Locale; 027import java.util.Optional; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.fs.FileSystem; 030import org.apache.hadoop.fs.Path; 031import org.apache.hadoop.hbase.ArrayBackedTag; 032import org.apache.hadoop.hbase.Cell; 033import org.apache.hadoop.hbase.HColumnDescriptor; 034import org.apache.hadoop.hbase.KeyValue; 035import org.apache.hadoop.hbase.PrivateCellUtil; 036import org.apache.hadoop.hbase.Tag; 037import org.apache.hadoop.hbase.TagType; 038import org.apache.hadoop.hbase.client.Result; 039import org.apache.hadoop.hbase.client.ResultScanner; 040import org.apache.hadoop.hbase.client.Scan; 041import org.apache.hadoop.hbase.client.Table; 042import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding; 043import org.apache.hadoop.hbase.io.hfile.CacheConfig; 044import org.apache.hadoop.hbase.io.hfile.HFile; 045import org.apache.hadoop.hbase.io.hfile.HFileContext; 046import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder; 047import org.apache.hadoop.hbase.mob.MobUtils; 048 049/** 050 * Utility class for HFile-related testing. 051 */ 052public class HFileTestUtil { 053 054 public static final String OPT_DATA_BLOCK_ENCODING_USAGE = "Encoding algorithm (e.g. prefix " 055 + "compression) to use for data blocks in the test column family, " + "one of " 056 + Arrays.toString(DataBlockEncoding.values()) + "."; 057 public static final String OPT_DATA_BLOCK_ENCODING = 058 HColumnDescriptor.DATA_BLOCK_ENCODING.toLowerCase(Locale.ROOT); 059 /** Column family used by the test */ 060 public static byte[] DEFAULT_COLUMN_FAMILY = Bytes.toBytes("test_cf"); 061 /** Column families used by the test */ 062 public static final byte[][] DEFAULT_COLUMN_FAMILIES = { DEFAULT_COLUMN_FAMILY }; 063 064 /** 065 * Create an HFile with the given number of rows between a given start key and end key @ 066 * family:qualifier. The value will be the key value. This file will not have tags. 067 */ 068 public static void createHFile(Configuration configuration, FileSystem fs, Path path, 069 byte[] family, byte[] qualifier, byte[] startKey, byte[] endKey, int numRows) 070 throws IOException { 071 createHFile(configuration, fs, path, DataBlockEncoding.NONE, family, qualifier, startKey, 072 endKey, numRows, false); 073 } 074 075 /** 076 * Create an HFile with the given number of rows between a given start key and end key @ 077 * family:qualifier. The value will be the key value. This file will use certain data block 078 * encoding algorithm. 079 */ 080 public static void createHFileWithDataBlockEncoding(Configuration configuration, FileSystem fs, 081 Path path, DataBlockEncoding encoding, byte[] family, byte[] qualifier, byte[] startKey, 082 byte[] endKey, int numRows) throws IOException { 083 createHFile(configuration, fs, path, encoding, family, qualifier, startKey, endKey, numRows, 084 false); 085 } 086 087 /** 088 * Create an HFile with the given number of rows between a given start key and end key @ 089 * family:qualifier. The value will be the key value. This cells will also have a tag whose value 090 * is the key. 091 */ 092 public static void createHFileWithTags(Configuration configuration, FileSystem fs, Path path, 093 byte[] family, byte[] qualifier, byte[] startKey, byte[] endKey, int numRows) 094 throws IOException { 095 createHFile(configuration, fs, path, DataBlockEncoding.NONE, family, qualifier, startKey, 096 endKey, numRows, true); 097 } 098 099 /** 100 * Create an HFile with the given number of rows between a given start key and end key @ 101 * family:qualifier. If withTag is true, we add the rowKey as the tag value for tagtype 102 * MOB_TABLE_NAME_TAG_TYPE 103 */ 104 public static void createHFile(Configuration configuration, FileSystem fs, Path path, 105 DataBlockEncoding encoding, byte[] family, byte[] qualifier, byte[] startKey, byte[] endKey, 106 int numRows, boolean withTag) throws IOException { 107 HFileContext meta = new HFileContextBuilder().withIncludesTags(withTag) 108 .withDataBlockEncoding(encoding).withColumnFamily(family).build(); 109 HFile.Writer writer = HFile.getWriterFactory(configuration, new CacheConfig(configuration)) 110 .withPath(fs, path).withFileContext(meta).create(); 111 long now = EnvironmentEdgeManager.currentTime(); 112 try { 113 // subtract 2 since iterateOnSplits doesn't include boundary keys 114 for (byte[] key : Bytes.iterateOnSplits(startKey, endKey, numRows - 2)) { 115 Cell kv = new KeyValue(key, family, qualifier, now, key); 116 if (withTag) { 117 // add a tag. Arbitrarily chose mob tag since we have a helper already. 118 Tag tableNameTag = new ArrayBackedTag(TagType.MOB_TABLE_NAME_TAG_TYPE, key); 119 kv = MobUtils.createMobRefCell(kv, key, tableNameTag); 120 121 // verify that the kv has the tag. 122 Optional<Tag> tag = PrivateCellUtil.getTag(kv, TagType.MOB_TABLE_NAME_TAG_TYPE); 123 if (!tag.isPresent()) { 124 throw new IllegalStateException("Tag didn't stick to KV " + kv.toString()); 125 } 126 } 127 writer.append(kv); 128 } 129 } finally { 130 writer.appendFileInfo(BULKLOAD_TIME_KEY, Bytes.toBytes(EnvironmentEdgeManager.currentTime())); 131 writer.close(); 132 } 133 } 134 135 /** 136 * This verifies that each cell has a tag that is equal to its rowkey name. For this to work the 137 * hbase instance must have HConstants.RPC_CODEC_CONF_KEY set to 138 * KeyValueCodecWithTags.class.getCanonicalName()); 139 * @param table table containing tagged cells 140 * @throws IOException if problems reading table 141 */ 142 public static void verifyTags(Table table) throws IOException { 143 ResultScanner s = table.getScanner(new Scan()); 144 for (Result r : s) { 145 for (Cell c : r.listCells()) { 146 Optional<Tag> tag = PrivateCellUtil.getTag(c, TagType.MOB_TABLE_NAME_TAG_TYPE); 147 if (!tag.isPresent()) { 148 fail(c.toString() + " has null tag"); 149 continue; 150 } 151 Tag t = tag.get(); 152 byte[] tval = Tag.cloneValue(t); 153 assertArrayEquals(c.toString() + " has tag" + Bytes.toString(tval), r.getRow(), tval); 154 } 155 } 156 } 157}