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.apache.hadoop.hbase.regionserver.HRegion.COMPACTION_AFTER_BULKLOAD_ENABLE; 021import static org.junit.jupiter.api.Assertions.assertEquals; 022import static org.mockito.ArgumentMatchers.any; 023import static org.mockito.Mockito.mock; 024import static org.mockito.Mockito.when; 025import static org.mockito.hamcrest.MockitoHamcrest.argThat; 026 027import java.io.File; 028import java.io.IOException; 029import java.util.ArrayList; 030import java.util.List; 031import java.util.concurrent.atomic.AtomicInteger; 032import org.apache.hadoop.conf.Configuration; 033import org.apache.hadoop.fs.Path; 034import org.apache.hadoop.hbase.HBaseConfiguration; 035import org.apache.hadoop.hbase.HBaseParameterizedTestTemplate; 036import org.apache.hadoop.hbase.TableName; 037import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 038import org.apache.hadoop.hbase.client.RegionInfo; 039import org.apache.hadoop.hbase.client.RegionInfoBuilder; 040import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 041import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker; 042import org.apache.hadoop.hbase.security.User; 043import org.apache.hadoop.hbase.testclassification.SmallTests; 044import org.apache.hadoop.hbase.util.Bytes; 045import org.apache.hadoop.hbase.util.Pair; 046import org.apache.hadoop.hbase.wal.WALEdit; 047import org.apache.hadoop.hbase.wal.WALKeyImpl; 048import org.junit.jupiter.api.Tag; 049import org.junit.jupiter.api.TestTemplate; 050import org.mockito.invocation.InvocationOnMock; 051import org.mockito.stubbing.Answer; 052 053@Tag(SmallTests.TAG) 054@HBaseParameterizedTestTemplate(name = "{index}: useFileBasedSFT={0}") 055public class TestCompactionAfterBulkLoad extends TestBulkloadBase { 056 057 private final RegionServerServices regionServerServices = mock(RegionServerServices.class); 058 public static AtomicInteger called = new AtomicInteger(0); 059 060 public TestCompactionAfterBulkLoad(boolean useFileBasedSFT) { 061 super(useFileBasedSFT); 062 } 063 064 @Override 065 protected HRegion testRegionWithFamiliesAndSpecifiedTableName(TableName tableName, 066 byte[]... families) throws IOException { 067 RegionInfo hRegionInfo = RegionInfoBuilder.newBuilder(tableName).build(); 068 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName); 069 070 for (byte[] family : families) { 071 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(family)); 072 } 073 ChunkCreator.initialize(MemStoreLAB.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null, 074 MemStoreLAB.INDEX_CHUNK_SIZE_PERCENTAGE_DEFAULT); 075 // TODO We need a way to do this without creating files 076 return HRegion.createHRegion(hRegionInfo, 077 new Path(new File(testFolder, generateUniqueName(null)).toURI()), conf, builder.build(), log, 078 true, regionServerServices); 079 080 } 081 082 @TestTemplate 083 public void shouldRequestCompactAllStoresAfterBulkLoad() throws IOException { 084 final CompactSplit compactSplit = new TestCompactSplit(HBaseConfiguration.create()); 085 called.set(0); 086 List<Pair<byte[], String>> familyPaths = new ArrayList<>(); 087 // enough hfile to request compaction 088 for (int i = 0; i < 5; i++) { 089 familyPaths.addAll(withFamilyPathsFor(family1, family2, family3)); 090 } 091 try { 092 conf.setBoolean(COMPACTION_AFTER_BULKLOAD_ENABLE, true); 093 when(regionServerServices.getConfiguration()).thenReturn(conf); 094 when(regionServerServices.getCompactionRequestor()).thenReturn(compactSplit); 095 when(log.appendMarker(any(), any(), argThat(bulkLogWalEditType(WALEdit.BULK_LOAD)))) 096 .thenAnswer(new Answer() { 097 @Override 098 public Object answer(InvocationOnMock invocation) { 099 WALKeyImpl walKey = invocation.getArgument(1); 100 MultiVersionConcurrencyControl mvcc = walKey.getMvcc(); 101 if (mvcc != null) { 102 MultiVersionConcurrencyControl.WriteEntry we = mvcc.begin(); 103 walKey.setWriteEntry(we); 104 } 105 return 01L; 106 } 107 }); 108 109 HRegion region = testRegionWithFamilies(family1, family2, family3); 110 region.bulkLoadHFiles(familyPaths, false, null); 111 assertEquals(3, called.get()); 112 } finally { 113 conf.setBoolean(COMPACTION_AFTER_BULKLOAD_ENABLE, false); 114 } 115 } 116 117 @TestTemplate 118 public void testAvoidRepeatedlyRequestCompactAfterBulkLoad() throws IOException { 119 final CompactSplit compactSplit = new TestFamily1UnderCompact(HBaseConfiguration.create()); 120 called.set(0); 121 List<Pair<byte[], String>> familyPaths = new ArrayList<>(); 122 // enough hfile to request compaction 123 for (int i = 0; i < 5; i++) { 124 familyPaths.addAll(withFamilyPathsFor(family1, family2, family3)); 125 } 126 try { 127 conf.setBoolean(COMPACTION_AFTER_BULKLOAD_ENABLE, true); 128 when(regionServerServices.getConfiguration()).thenReturn(conf); 129 when(regionServerServices.getCompactionRequestor()).thenReturn(compactSplit); 130 when(log.appendMarker(any(), any(), argThat(bulkLogWalEditType(WALEdit.BULK_LOAD)))) 131 .thenAnswer(new Answer() { 132 @Override 133 public Object answer(InvocationOnMock invocation) { 134 WALKeyImpl walKey = invocation.getArgument(1); 135 MultiVersionConcurrencyControl mvcc = walKey.getMvcc(); 136 if (mvcc != null) { 137 MultiVersionConcurrencyControl.WriteEntry we = mvcc.begin(); 138 walKey.setWriteEntry(we); 139 } 140 return 01L; 141 } 142 }); 143 144 HRegion region = testRegionWithFamilies(family1, family2, family3); 145 region.bulkLoadHFiles(familyPaths, false, null); 146 // invoke three times for 2 families 147 assertEquals(2, called.get()); 148 } finally { 149 conf.setBoolean(COMPACTION_AFTER_BULKLOAD_ENABLE, false); 150 } 151 } 152 153 private class TestCompactSplit extends CompactSplit { 154 155 TestCompactSplit(Configuration conf) { 156 super(conf); 157 } 158 159 @Override 160 protected void requestCompactionInternal(HRegion region, HStore store, String why, int priority, 161 boolean selectNow, CompactionLifeCycleTracker tracker, 162 CompactionCompleteTracker completeTracker, User user) throws IOException { 163 called.addAndGet(1); 164 } 165 } 166 167 private class TestFamily1UnderCompact extends TestCompactSplit { 168 169 TestFamily1UnderCompact(Configuration conf) { 170 super(conf); 171 } 172 173 @Override 174 public boolean isUnderCompaction(final HStore s) { 175 if (s.getColumnFamilyName().equals(Bytes.toString(family1))) { 176 return true; 177 } 178 return super.isUnderCompaction(s); 179 } 180 } 181 182}