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