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