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; 021 022import java.io.FileNotFoundException; 023import java.io.IOException; 024import java.util.List; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.hbase.HBaseClassTestRule; 027import org.apache.hadoop.hbase.HBaseTestingUtility; 028import org.apache.hadoop.hbase.TableName; 029import org.apache.hadoop.hbase.Waiter; 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.MediumTests; 035import org.apache.hadoop.hbase.util.Bytes; 036import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread; 037import org.junit.After; 038import org.junit.AfterClass; 039import org.junit.Assert; 040import org.junit.BeforeClass; 041import org.junit.ClassRule; 042import org.junit.Test; 043import org.junit.experimental.categories.Category; 044import org.slf4j.Logger; 045import org.slf4j.LoggerFactory; 046 047/** 048 * This class tests the scenario where a store refresh happens due to a file not found during scan, 049 * after a compaction but before the compacted files are archived. At this state we test for a split 050 * and compaction 051 */ 052@Category(MediumTests.class) 053public class TestCompactionFileNotFound { 054 055 @ClassRule 056 public static final HBaseClassTestRule CLASS_RULE = 057 HBaseClassTestRule.forClass(TestCompactionFileNotFound.class); 058 059 private static final Logger LOG = LoggerFactory.getLogger(TestCompactionFileNotFound.class); 060 private static final HBaseTestingUtility util = new HBaseTestingUtility(); 061 062 private static final TableName TEST_TABLE = TableName.valueOf("test"); 063 private static final byte[] TEST_FAMILY = Bytes.toBytes("f1"); 064 065 private static final byte[] ROW_A = Bytes.toBytes("aaa"); 066 private static final byte[] ROW_B = Bytes.toBytes("bbb"); 067 private static final byte[] ROW_C = Bytes.toBytes("ccc"); 068 069 private static final byte[] qualifierCol1 = Bytes.toBytes("col1"); 070 071 private static final byte[] bytes1 = Bytes.toBytes(1); 072 private static final byte[] bytes2 = Bytes.toBytes(2); 073 private static final byte[] bytes3 = Bytes.toBytes(3); 074 075 private Table table; 076 077 @BeforeClass 078 public static void setupBeforeClass() throws Exception { 079 Configuration conf = util.getConfiguration(); 080 conf.setInt("hbase.hfile.compaction.discharger.interval", Integer.MAX_VALUE); 081 util.startMiniCluster(3); 082 } 083 084 @AfterClass 085 public static void tearDownAfterClass() throws Exception { 086 util.shutdownMiniCluster(); 087 } 088 089 @After 090 public void after() throws Exception { 091 try { 092 if (table != null) { 093 table.close(); 094 } 095 } finally { 096 util.deleteTable(TEST_TABLE); 097 } 098 } 099 100 @Test 101 public void testSplitAfterRefresh() throws Exception { 102 Admin admin = util.getAdmin(); 103 table = util.createTable(TEST_TABLE, TEST_FAMILY); 104 105 try { 106 // Create Multiple store files 107 Put puta = new Put(ROW_A); 108 puta.addColumn(TEST_FAMILY, qualifierCol1, bytes1); 109 table.put(puta); 110 admin.flush(TEST_TABLE); 111 112 Put putb = new Put(ROW_B); 113 putb.addColumn(TEST_FAMILY, qualifierCol1, bytes2); 114 table.put(putb); 115 admin.flush(TEST_TABLE); 116 117 Put putc = new Put(ROW_C); 118 putc.addColumn(TEST_FAMILY, qualifierCol1, bytes3); 119 table.put(putc); 120 admin.flush(TEST_TABLE); 121 122 admin.compact(TEST_TABLE); 123 while (admin.getCompactionState(TEST_TABLE) != CompactionState.NONE) { 124 Thread.sleep(1000); 125 } 126 table.put(putb); 127 HRegion hr1 = (HRegion) util.getRSForFirstRegionInTable(TEST_TABLE) 128 .getRegionByEncodedName(admin.getTableRegions(TEST_TABLE).get(0).getEncodedName()); 129 // Refresh store files post compaction, this should not open already compacted files 130 hr1.refreshStoreFiles(true); 131 int numRegionsBeforeSplit = admin.getTableRegions(TEST_TABLE).size(); 132 // Check if we can successfully split after compaction 133 admin.splitRegion(admin.getTableRegions(TEST_TABLE).get(0).getEncodedNameAsBytes(), ROW_C); 134 util.waitFor(20000, new Waiter.Predicate<Exception>() { 135 @Override 136 public boolean evaluate() throws Exception { 137 int numRegionsAfterSplit = 0; 138 List<RegionServerThread> rst = util.getMiniHBaseCluster().getLiveRegionServerThreads(); 139 for (RegionServerThread t : rst) { 140 numRegionsAfterSplit += t.getRegionServer().getRegions(TEST_TABLE).size(); 141 } 142 // Make sure that the split went through and all the regions are assigned 143 return (numRegionsAfterSplit == numRegionsBeforeSplit + 1 144 && admin.isTableAvailable(TEST_TABLE)); 145 } 146 }); 147 // Split at this point should not result in the RS being aborted 148 assertEquals(3, util.getMiniHBaseCluster().getLiveRegionServerThreads().size()); 149 } finally { 150 if (admin != null) { 151 admin.close(); 152 } 153 } 154 } 155 156 @Test 157 public void testCompactionAfterRefresh() throws Exception { 158 Admin admin = util.getAdmin(); 159 table = util.createTable(TEST_TABLE, TEST_FAMILY); 160 try { 161 // Create Multiple store files 162 Put puta = new Put(ROW_A); 163 puta.addColumn(TEST_FAMILY, qualifierCol1, bytes1); 164 table.put(puta); 165 admin.flush(TEST_TABLE); 166 167 Put putb = new Put(ROW_B); 168 putb.addColumn(TEST_FAMILY, qualifierCol1, bytes2); 169 table.put(putb); 170 admin.flush(TEST_TABLE); 171 172 Put putc = new Put(ROW_C); 173 putc.addColumn(TEST_FAMILY, qualifierCol1, bytes3); 174 table.put(putc); 175 admin.flush(TEST_TABLE); 176 177 admin.compact(TEST_TABLE); 178 while (admin.getCompactionState(TEST_TABLE) != CompactionState.NONE) { 179 Thread.sleep(1000); 180 } 181 table.put(putb); 182 HRegion hr1 = (HRegion) util.getRSForFirstRegionInTable(TEST_TABLE) 183 .getRegionByEncodedName(admin.getTableRegions(TEST_TABLE).get(0).getEncodedName()); 184 // Refresh store files post compaction, this should not open already compacted files 185 hr1.refreshStoreFiles(true); 186 // Archive the store files and try another compaction to see if all is good 187 for (HStore store : hr1.getStores()) { 188 store.closeAndArchiveCompactedFiles(); 189 } 190 try { 191 hr1.compact(false); 192 } catch (IOException e) { 193 LOG.error("Got an exception during compaction", e); 194 if (e instanceof FileNotFoundException) { 195 Assert.fail("Got a FNFE during compaction"); 196 } else { 197 Assert.fail(); 198 } 199 } 200 } finally { 201 if (admin != null) { 202 admin.close(); 203 } 204 } 205 } 206}