1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package org.apache.hadoop.hbase.master.procedure; 20 21 import org.apache.commons.logging.Log; 22 import org.apache.commons.logging.LogFactory; 23 import org.apache.hadoop.conf.Configuration; 24 import org.apache.hadoop.hbase.HBaseTestingUtility; 25 import org.apache.hadoop.hbase.HConstants; 26 import org.apache.hadoop.hbase.HTableDescriptor; 27 import org.apache.hadoop.hbase.HRegionInfo; 28 import org.apache.hadoop.hbase.ProcedureInfo; 29 import org.apache.hadoop.hbase.TableName; 30 import org.apache.hadoop.hbase.TableNotDisabledException; 31 import org.apache.hadoop.hbase.TableNotFoundException; 32 import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 33 import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 34 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.TruncateTableState; 35 import org.apache.hadoop.hbase.testclassification.MediumTests; 36 import org.apache.hadoop.hbase.util.Bytes; 37 import org.junit.After; 38 import org.junit.AfterClass; 39 import org.junit.Before; 40 import org.junit.BeforeClass; 41 import org.junit.Test; 42 import org.junit.experimental.categories.Category; 43 44 import static org.junit.Assert.assertEquals; 45 import static org.junit.Assert.assertTrue; 46 47 @Category(MediumTests.class) 48 public class TestTruncateTableProcedure { 49 private static final Log LOG = LogFactory.getLog(TestTruncateTableProcedure.class); 50 51 protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 52 53 private static long nonceGroup = HConstants.NO_NONCE; 54 private static long nonce = HConstants.NO_NONCE; 55 setupConf(Configuration conf)56 private static void setupConf(Configuration conf) { 57 conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1); 58 } 59 60 @BeforeClass setupCluster()61 public static void setupCluster() throws Exception { 62 setupConf(UTIL.getConfiguration()); 63 UTIL.startMiniCluster(1); 64 } 65 66 @AfterClass cleanupTest()67 public static void cleanupTest() throws Exception { 68 try { 69 UTIL.shutdownMiniCluster(); 70 } catch (Exception e) { 71 LOG.warn("failure shutting down cluster", e); 72 } 73 } 74 75 @Before setup()76 public void setup() throws Exception { 77 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 78 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false); 79 assertTrue("expected executor to be running", procExec.isRunning()); 80 81 nonceGroup = 82 MasterProcedureTestingUtility.generateNonceGroup(UTIL.getHBaseCluster().getMaster()); 83 nonce = MasterProcedureTestingUtility.generateNonce(UTIL.getHBaseCluster().getMaster()); 84 } 85 86 @After tearDown()87 public void tearDown() throws Exception { 88 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false); 89 for (HTableDescriptor htd: UTIL.getHBaseAdmin().listTables()) { 90 LOG.info("Tear down, remove table=" + htd.getTableName()); 91 UTIL.deleteTable(htd.getTableName()); 92 } 93 } 94 95 @Test(timeout=60000) testTruncateNotExistentTable()96 public void testTruncateNotExistentTable() throws Exception { 97 final TableName tableName = TableName.valueOf("testTruncateNotExistentTable"); 98 99 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 100 long procId = ProcedureTestingUtility.submitAndWait(procExec, 101 new TruncateTableProcedure(procExec.getEnvironment(), tableName, true)); 102 103 // Second delete should fail with TableNotFound 104 ProcedureInfo result = procExec.getResult(procId); 105 assertTrue(result.isFailed()); 106 LOG.debug("Truncate failed with exception: " + result.getExceptionFullMessage()); 107 assertTrue(ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotFoundException); 108 } 109 110 @Test(timeout=60000) testTruncateNotDisabledTable()111 public void testTruncateNotDisabledTable() throws Exception { 112 final TableName tableName = TableName.valueOf("testTruncateNotDisabledTable"); 113 114 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 115 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); 116 117 long procId = ProcedureTestingUtility.submitAndWait(procExec, 118 new TruncateTableProcedure(procExec.getEnvironment(), tableName, false)); 119 120 // Second delete should fail with TableNotDisabled 121 ProcedureInfo result = procExec.getResult(procId); 122 assertTrue(result.isFailed()); 123 LOG.debug("Truncate failed with exception: " + result.getExceptionFullMessage()); 124 assertTrue( 125 ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotDisabledException); 126 } 127 128 @Test(timeout=60000) testSimpleTruncatePreserveSplits()129 public void testSimpleTruncatePreserveSplits() throws Exception { 130 final TableName tableName = TableName.valueOf("testSimpleTruncatePreserveSplits"); 131 testSimpleTruncate(tableName, true); 132 } 133 134 @Test(timeout=60000) testSimpleTruncateNoPreserveSplits()135 public void testSimpleTruncateNoPreserveSplits() throws Exception { 136 final TableName tableName = TableName.valueOf("testSimpleTruncateNoPreserveSplits"); 137 testSimpleTruncate(tableName, false); 138 } 139 testSimpleTruncate(final TableName tableName, final boolean preserveSplits)140 private void testSimpleTruncate(final TableName tableName, final boolean preserveSplits) 141 throws Exception { 142 final String[] families = new String[] { "f1", "f2" }; 143 final byte[][] splitKeys = new byte[][] { 144 Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c") 145 }; 146 147 HRegionInfo[] regions = MasterProcedureTestingUtility.createTable( 148 getMasterProcedureExecutor(), tableName, splitKeys, families); 149 // load and verify that there are rows in the table 150 MasterProcedureTestingUtility.loadData( 151 UTIL.getConnection(), tableName, 100, splitKeys, families); 152 assertEquals(100, UTIL.countRows(tableName)); 153 // disable the table 154 UTIL.getHBaseAdmin().disableTable(tableName); 155 156 // truncate the table 157 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 158 long procId = ProcedureTestingUtility.submitAndWait(procExec, 159 new TruncateTableProcedure(procExec.getEnvironment(), tableName, preserveSplits)); 160 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 161 162 UTIL.waitUntilAllRegionsAssigned(tableName); 163 164 // validate the table regions and layout 165 if (preserveSplits) { 166 assertEquals(1 + splitKeys.length, UTIL.getHBaseAdmin().getTableRegions(tableName).size()); 167 } else { 168 regions = UTIL.getHBaseAdmin().getTableRegions(tableName).toArray(new HRegionInfo[1]); 169 assertEquals(1, regions.length); 170 } 171 MasterProcedureTestingUtility.validateTableCreation( 172 UTIL.getHBaseCluster().getMaster(), tableName, regions, families); 173 174 // verify that there are no rows in the table 175 assertEquals(0, UTIL.countRows(tableName)); 176 177 // verify that the table is read/writable 178 MasterProcedureTestingUtility.loadData( 179 UTIL.getConnection(), tableName, 50, splitKeys, families); 180 assertEquals(50, UTIL.countRows(tableName)); 181 } 182 183 @Test(timeout=60000) testRecoveryAndDoubleExecutionPreserveSplits()184 public void testRecoveryAndDoubleExecutionPreserveSplits() throws Exception { 185 final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecutionPreserveSplits"); 186 testRecoveryAndDoubleExecution(tableName, true); 187 } 188 189 @Test(timeout=60000) testRecoveryAndDoubleExecutionNoPreserveSplits()190 public void testRecoveryAndDoubleExecutionNoPreserveSplits() throws Exception { 191 final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecutionNoPreserveSplits"); 192 testRecoveryAndDoubleExecution(tableName, false); 193 } 194 testRecoveryAndDoubleExecution(final TableName tableName, final boolean preserveSplits)195 private void testRecoveryAndDoubleExecution(final TableName tableName, 196 final boolean preserveSplits) throws Exception { 197 final String[] families = new String[] { "f1", "f2" }; 198 199 // create the table 200 final byte[][] splitKeys = new byte[][] { 201 Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c") 202 }; 203 HRegionInfo[] regions = MasterProcedureTestingUtility.createTable( 204 getMasterProcedureExecutor(), tableName, splitKeys, families); 205 // load and verify that there are rows in the table 206 MasterProcedureTestingUtility.loadData( 207 UTIL.getConnection(), tableName, 100, splitKeys, families); 208 assertEquals(100, UTIL.countRows(tableName)); 209 // disable the table 210 UTIL.getHBaseAdmin().disableTable(tableName); 211 212 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 213 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 214 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 215 216 // Start the Truncate procedure && kill the executor 217 long procId = procExec.submitProcedure( 218 new TruncateTableProcedure(procExec.getEnvironment(), tableName, preserveSplits), 219 nonceGroup, 220 nonce); 221 222 // Restart the executor and execute the step twice 223 // NOTE: the 7 (number of TruncateTableState steps) is hardcoded, 224 // so you have to look at this test at least once when you add a new step. 225 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution( 226 procExec, procId, 7, TruncateTableState.values()); 227 228 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false); 229 UTIL.waitUntilAllRegionsAssigned(tableName); 230 231 // validate the table regions and layout 232 if (preserveSplits) { 233 assertEquals(1 + splitKeys.length, UTIL.getHBaseAdmin().getTableRegions(tableName).size()); 234 } else { 235 regions = UTIL.getHBaseAdmin().getTableRegions(tableName).toArray(new HRegionInfo[1]); 236 assertEquals(1, regions.length); 237 } 238 MasterProcedureTestingUtility.validateTableCreation( 239 UTIL.getHBaseCluster().getMaster(), tableName, regions, families); 240 241 // verify that there are no rows in the table 242 assertEquals(0, UTIL.countRows(tableName)); 243 244 // verify that the table is read/writable 245 MasterProcedureTestingUtility.loadData( 246 UTIL.getConnection(), tableName, 50, splitKeys, families); 247 assertEquals(50, UTIL.countRows(tableName)); 248 } 249 getMasterProcedureExecutor()250 private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() { 251 return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor(); 252 } 253 } 254