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.Assert.assertEquals; 021import static org.junit.Assert.assertTrue; 022 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.List; 026import java.util.Random; 027import java.util.concurrent.ThreadLocalRandom; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HBaseTestingUtility; 030import org.apache.hadoop.hbase.TableName; 031import org.apache.hadoop.hbase.client.Admin; 032import org.apache.hadoop.hbase.client.CompactionState; 033import org.apache.hadoop.hbase.client.Put; 034import org.apache.hadoop.hbase.client.Table; 035import org.apache.hadoop.hbase.master.HMaster; 036import org.apache.hadoop.hbase.testclassification.LargeTests; 037import org.apache.hadoop.hbase.testclassification.VerySlowRegionServerTests; 038import org.apache.hadoop.hbase.util.Bytes; 039import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 040import org.junit.AfterClass; 041import org.junit.BeforeClass; 042import org.junit.ClassRule; 043import org.junit.Rule; 044import org.junit.Test; 045import org.junit.experimental.categories.Category; 046import org.junit.rules.TestName; 047 048/** Unit tests to test retrieving table/region compaction state */ 049@Category({ VerySlowRegionServerTests.class, LargeTests.class }) 050public class TestCompactionState { 051 052 @ClassRule 053 public static final HBaseClassTestRule CLASS_RULE = 054 HBaseClassTestRule.forClass(TestCompactionState.class); 055 056 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 057 058 @Rule 059 public TestName name = new TestName(); 060 061 @BeforeClass 062 public static void setUpBeforeClass() throws Exception { 063 TEST_UTIL.startMiniCluster(); 064 } 065 066 @AfterClass 067 public static void tearDownAfterClass() throws Exception { 068 TEST_UTIL.shutdownMiniCluster(); 069 } 070 071 enum StateSource { 072 ADMIN, 073 MASTER 074 } 075 076 @Test 077 public void testMajorCompactionStateFromAdmin() throws IOException, InterruptedException { 078 compaction(name.getMethodName(), 8, CompactionState.MAJOR, false, StateSource.ADMIN); 079 } 080 081 @Test 082 public void testMinorCompactionStateFromAdmin() throws IOException, InterruptedException { 083 compaction(name.getMethodName(), 15, CompactionState.MINOR, false, StateSource.ADMIN); 084 } 085 086 @Test 087 public void testMajorCompactionOnFamilyStateFromAdmin() throws IOException, InterruptedException { 088 compaction(name.getMethodName(), 8, CompactionState.MAJOR, true, StateSource.ADMIN); 089 } 090 091 @Test 092 public void testMinorCompactionOnFamilyStateFromAdmin() throws IOException, InterruptedException { 093 compaction(name.getMethodName(), 15, CompactionState.MINOR, true, StateSource.ADMIN); 094 } 095 096 @Test 097 public void testMajorCompactionStateFromMaster() throws IOException, InterruptedException { 098 compaction(name.getMethodName(), 8, CompactionState.MAJOR, false, StateSource.MASTER); 099 } 100 101 @Test 102 public void testMinorCompactionStateFromMaster() throws IOException, InterruptedException { 103 compaction(name.getMethodName(), 15, CompactionState.MINOR, false, StateSource.MASTER); 104 } 105 106 @Test 107 public void testMajorCompactionOnFamilyStateFromMaster() 108 throws IOException, InterruptedException { 109 compaction(name.getMethodName(), 8, CompactionState.MAJOR, true, StateSource.MASTER); 110 } 111 112 @Test 113 public void testMinorCompactionOnFamilyStateFromMaster() 114 throws IOException, InterruptedException { 115 compaction(name.getMethodName(), 15, CompactionState.MINOR, true, StateSource.MASTER); 116 } 117 118 @Test 119 public void testInvalidColumnFamily() throws IOException, InterruptedException { 120 final TableName tableName = TableName.valueOf(name.getMethodName()); 121 byte[] family = Bytes.toBytes("family"); 122 byte[] fakecf = Bytes.toBytes("fakecf"); 123 boolean caughtMinorCompact = false; 124 boolean caughtMajorCompact = false; 125 Table ht = null; 126 try { 127 ht = TEST_UTIL.createTable(tableName, family); 128 Admin admin = TEST_UTIL.getAdmin(); 129 try { 130 admin.compact(tableName, fakecf); 131 } catch (IOException ioe) { 132 caughtMinorCompact = true; 133 } 134 try { 135 admin.majorCompact(tableName, fakecf); 136 } catch (IOException ioe) { 137 caughtMajorCompact = true; 138 } 139 } finally { 140 if (ht != null) { 141 TEST_UTIL.deleteTable(tableName); 142 } 143 assertTrue(caughtMinorCompact); 144 assertTrue(caughtMajorCompact); 145 } 146 } 147 148 /** 149 * Load data to a table, flush it to disk, trigger compaction, confirm the compaction state is 150 * right and wait till it is done. nnn * @param singleFamily otherwise, run compaction on all cfs 151 * @param stateSource get the state by Admin or Master nn 152 */ 153 private void compaction(final String tableName, final int flushes, 154 final CompactionState expectedState, boolean singleFamily, StateSource stateSource) 155 throws IOException, InterruptedException { 156 // Create a table with regions 157 TableName table = TableName.valueOf(tableName); 158 byte[] family = Bytes.toBytes("family"); 159 byte[][] families = 160 { family, Bytes.add(family, Bytes.toBytes("2")), Bytes.add(family, Bytes.toBytes("3")) }; 161 Table ht = null; 162 try { 163 ht = TEST_UTIL.createTable(table, families); 164 loadData(ht, families, 3000, flushes); 165 HRegionServer rs = TEST_UTIL.getMiniHBaseCluster().getRegionServer(0); 166 HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster(); 167 List<HRegion> regions = rs.getRegions(table); 168 int countBefore = countStoreFilesInFamilies(regions, families); 169 int countBeforeSingleFamily = countStoreFilesInFamily(regions, family); 170 assertTrue(countBefore > 0); // there should be some data files 171 Admin admin = TEST_UTIL.getAdmin(); 172 if (expectedState == CompactionState.MINOR) { 173 if (singleFamily) { 174 admin.compact(table, family); 175 } else { 176 admin.compact(table); 177 } 178 } else { 179 if (singleFamily) { 180 admin.majorCompact(table, family); 181 } else { 182 admin.majorCompact(table); 183 } 184 } 185 long curt = EnvironmentEdgeManager.currentTime(); 186 long waitTime = 5000; 187 long endt = curt + waitTime; 188 CompactionState state = getCompactionState(stateSource, master, admin, table); 189 while (state == CompactionState.NONE && curt < endt) { 190 Thread.sleep(10); 191 state = getCompactionState(stateSource, master, admin, table); 192 curt = EnvironmentEdgeManager.currentTime(); 193 } 194 // Now, should have the right compaction state, 195 // otherwise, the compaction should have already been done 196 if (expectedState != state) { 197 for (Region region : regions) { 198 state = CompactionState.valueOf(region.getCompactionState().toString()); 199 assertEquals(CompactionState.NONE, state); 200 } 201 } else { 202 // Wait until the compaction is done 203 state = getCompactionState(stateSource, master, admin, table); 204 while (state != CompactionState.NONE && curt < endt) { 205 Thread.sleep(10); 206 state = getCompactionState(stateSource, master, admin, table); 207 } 208 // Now, compaction should be done. 209 assertEquals(CompactionState.NONE, state); 210 } 211 int countAfter = countStoreFilesInFamilies(regions, families); 212 int countAfterSingleFamily = countStoreFilesInFamily(regions, family); 213 assertTrue(countAfter < countBefore); 214 if (!singleFamily) { 215 if (expectedState == CompactionState.MAJOR) assertTrue(families.length == countAfter); 216 else assertTrue(families.length < countAfter); 217 } else { 218 int singleFamDiff = countBeforeSingleFamily - countAfterSingleFamily; 219 // assert only change was to single column family 220 assertTrue(singleFamDiff == (countBefore - countAfter)); 221 if (expectedState == CompactionState.MAJOR) { 222 assertTrue(1 == countAfterSingleFamily); 223 } else { 224 assertTrue(1 < countAfterSingleFamily); 225 } 226 } 227 } finally { 228 if (ht != null) { 229 TEST_UTIL.deleteTable(table); 230 } 231 } 232 } 233 234 private static CompactionState getCompactionState(StateSource stateSource, HMaster master, 235 Admin admin, TableName table) throws IOException { 236 CompactionState state = stateSource == StateSource.ADMIN 237 ? admin.getCompactionState(table) 238 : master.getCompactionState(table); 239 return state; 240 } 241 242 private static int countStoreFilesInFamily(List<HRegion> regions, final byte[] family) { 243 return countStoreFilesInFamilies(regions, new byte[][] { family }); 244 } 245 246 private static int countStoreFilesInFamilies(List<HRegion> regions, final byte[][] families) { 247 int count = 0; 248 for (HRegion region : regions) { 249 count += region.getStoreFileList(families).size(); 250 } 251 return count; 252 } 253 254 private static void loadData(final Table ht, final byte[][] families, final int rows, 255 final int flushes) throws IOException { 256 List<Put> puts = new ArrayList<>(rows); 257 byte[] qualifier = Bytes.toBytes("val"); 258 Random rand = ThreadLocalRandom.current(); 259 for (int i = 0; i < flushes; i++) { 260 for (int k = 0; k < rows; k++) { 261 byte[] row = Bytes.toBytes(rand.nextLong()); 262 Put p = new Put(row); 263 for (int j = 0; j < families.length; ++j) { 264 p.addColumn(families[j], qualifier, row); 265 } 266 puts.add(p); 267 } 268 ht.put(puts); 269 TEST_UTIL.flush(); 270 puts.clear(); 271 } 272 } 273}