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.regionserver; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertNotNull; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023import static org.mockito.Mockito.mock; 024 025import java.io.File; 026import java.io.FileOutputStream; 027import java.io.IOException; 028import java.util.ArrayList; 029import java.util.Arrays; 030import java.util.List; 031import java.util.UUID; 032import java.util.stream.Stream; 033import org.apache.hadoop.conf.Configuration; 034import org.apache.hadoop.fs.FSDataOutputStream; 035import org.apache.hadoop.fs.Path; 036import org.apache.hadoop.hbase.CellBuilderType; 037import org.apache.hadoop.hbase.CellUtil; 038import org.apache.hadoop.hbase.ExtendedCellBuilderFactory; 039import org.apache.hadoop.hbase.HBaseConfiguration; 040import org.apache.hadoop.hbase.HBaseTestingUtil; 041import org.apache.hadoop.hbase.KeyValue; 042import org.apache.hadoop.hbase.TableName; 043import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 044import org.apache.hadoop.hbase.client.RegionInfo; 045import org.apache.hadoop.hbase.client.RegionInfoBuilder; 046import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 047import org.apache.hadoop.hbase.io.hfile.HFile; 048import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder; 049import org.apache.hadoop.hbase.regionserver.storefiletracker.StoreFileTrackerFactory; 050import org.apache.hadoop.hbase.util.Bytes; 051import org.apache.hadoop.hbase.util.Pair; 052import org.apache.hadoop.hbase.wal.WAL; 053import org.apache.hadoop.hbase.wal.WALEdit; 054import org.hamcrest.Description; 055import org.hamcrest.Matcher; 056import org.hamcrest.TypeSafeMatcher; 057import org.junit.jupiter.api.BeforeEach; 058import org.junit.jupiter.api.TestInfo; 059import org.junit.jupiter.api.io.TempDir; 060import org.junit.jupiter.params.provider.Arguments; 061 062import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 063import org.apache.hadoop.hbase.shaded.protobuf.generated.WALProtos; 064 065public class TestBulkloadBase { 066 067 @TempDir 068 public static File testFolder; 069 private static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 070 protected final WAL log = mock(WAL.class); 071 protected final Configuration conf = HBaseConfiguration.create(); 072 private final byte[] randomBytes = new byte[100]; 073 protected final byte[] family1 = Bytes.toBytes("family1"); 074 protected final byte[] family2 = Bytes.toBytes("family2"); 075 protected final byte[] family3 = Bytes.toBytes("family3"); 076 077 protected Boolean useFileBasedSFT; 078 private String name; 079 080 public TestBulkloadBase(boolean useFileBasedSFT) { 081 this.useFileBasedSFT = useFileBasedSFT; 082 } 083 084 public static Stream<Arguments> parameters() { 085 return Stream.of(Arguments.of(false), Arguments.of(true)); 086 } 087 088 @BeforeEach 089 public void before(TestInfo testInfo) throws IOException { 090 this.name = testInfo.getTestMethod().get().getName(); 091 Bytes.random(randomBytes); 092 if (useFileBasedSFT) { 093 conf.set(StoreFileTrackerFactory.TRACKER_IMPL, 094 "org.apache.hadoop.hbase.regionserver.storefiletracker.FileBasedStoreFileTracker"); 095 } else { 096 conf.unset(StoreFileTrackerFactory.TRACKER_IMPL); 097 } 098 } 099 100 protected Pair<byte[], String> withMissingHFileForFamily(byte[] family) { 101 return new Pair<>(family, getNotExistFilePath()); 102 } 103 104 private String getNotExistFilePath() { 105 Path path = new Path(TEST_UTIL.getDataTestDir(), "does_not_exist"); 106 return path.toUri().getPath(); 107 } 108 109 protected Pair<byte[], String> withInvalidColumnFamilyButProperHFileLocation(byte[] family) 110 throws IOException { 111 createHFileForFamilies(family); 112 return new Pair<>(new byte[] { 0x00, 0x01, 0x02 }, getNotExistFilePath()); 113 } 114 115 protected HRegion testRegionWithFamiliesAndSpecifiedTableName(TableName tableName, 116 byte[]... families) throws IOException { 117 RegionInfo hRegionInfo = RegionInfoBuilder.newBuilder(tableName).build(); 118 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName); 119 120 for (byte[] family : families) { 121 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)); 122 } 123 ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null, 124 MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 125 // TODO We need a way to do this without creating files 126 return HRegion.createHRegion(hRegionInfo, 127 new Path(new File(testFolder, generateUniqueName(null)).toURI()), conf, builder.build(), log); 128 129 } 130 131 protected HRegion testRegionWithFamilies(byte[]... families) throws IOException { 132 TableName tableName = TableName.valueOf(name); 133 return testRegionWithFamiliesAndSpecifiedTableName(tableName, families); 134 } 135 136 private List<Pair<byte[], String>> getBlankFamilyPaths() { 137 return new ArrayList<>(); 138 } 139 140 protected List<Pair<byte[], String>> withFamilyPathsFor(byte[]... families) throws IOException { 141 List<Pair<byte[], String>> familyPaths = getBlankFamilyPaths(); 142 for (byte[] family : families) { 143 familyPaths.add(new Pair<>(family, createHFileForFamilies(family))); 144 } 145 return familyPaths; 146 } 147 148 private String createHFileForFamilies(byte[] family) throws IOException { 149 HFile.WriterFactory hFileFactory = HFile.getWriterFactoryNoCache(conf); 150 // TODO We need a way to do this without creating files 151 File hFileLocation = new File(testFolder, generateUniqueName(null)); 152 FSDataOutputStream out = new FSDataOutputStream(new FileOutputStream(hFileLocation), null); 153 try { 154 hFileFactory.withOutputStream(out); 155 hFileFactory.withFileContext(new HFileContextBuilder().build()); 156 HFile.Writer writer = hFileFactory.create(); 157 try { 158 writer.append(new KeyValue(ExtendedCellBuilderFactory.create(CellBuilderType.DEEP_COPY) 159 .setRow(randomBytes).setFamily(family).setQualifier(randomBytes).setTimestamp(0L) 160 .setType(KeyValue.Type.Put.getCode()).setValue(randomBytes).build())); 161 } finally { 162 writer.close(); 163 } 164 } finally { 165 out.close(); 166 } 167 return hFileLocation.getAbsoluteFile().getAbsolutePath(); 168 } 169 170 protected static String generateUniqueName(final String suffix) { 171 String name = UUID.randomUUID().toString().replaceAll("-", ""); 172 if (suffix != null) name += suffix; 173 return name; 174 } 175 176 protected static Matcher<WALEdit> bulkLogWalEditType(byte[] typeBytes) { 177 return new WalMatcher(typeBytes); 178 } 179 180 protected static Matcher<WALEdit> bulkLogWalEdit(byte[] typeBytes, byte[] tableName, 181 byte[] familyName, List<String> storeFileNames) { 182 return new WalMatcher(typeBytes, tableName, familyName, storeFileNames); 183 } 184 185 private static class WalMatcher extends TypeSafeMatcher<WALEdit> { 186 private final byte[] typeBytes; 187 private final byte[] tableName; 188 private final byte[] familyName; 189 private final List<String> storeFileNames; 190 191 public WalMatcher(byte[] typeBytes) { 192 this(typeBytes, null, null, null); 193 } 194 195 public WalMatcher(byte[] typeBytes, byte[] tableName, byte[] familyName, 196 List<String> storeFileNames) { 197 this.typeBytes = typeBytes; 198 this.tableName = tableName; 199 this.familyName = familyName; 200 this.storeFileNames = storeFileNames; 201 } 202 203 @Override 204 protected boolean matchesSafely(WALEdit item) { 205 assertTrue(Arrays.equals(CellUtil.cloneQualifier(item.getCells().get(0)), typeBytes)); 206 WALProtos.BulkLoadDescriptor desc; 207 try { 208 desc = WALEdit.getBulkLoadDescriptor(item.getCells().get(0)); 209 } catch (IOException e) { 210 return false; 211 } 212 assertNotNull(desc); 213 214 if (tableName != null) { 215 assertTrue( 216 Bytes.equals(ProtobufUtil.toTableName(desc.getTableName()).getName(), tableName)); 217 } 218 219 if (storeFileNames != null) { 220 int index = 0; 221 WALProtos.StoreDescriptor store = desc.getStores(0); 222 assertTrue(Bytes.equals(store.getFamilyName().toByteArray(), familyName)); 223 assertTrue(Bytes.equals(Bytes.toBytes(store.getStoreHomeDir()), familyName)); 224 assertEquals(storeFileNames.size(), store.getStoreFileCount()); 225 } 226 227 return true; 228 } 229 230 @Override 231 public void describeTo(Description description) { 232 233 } 234 } 235}