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.master.procedure; 019 020import static org.junit.Assert.assertFalse; 021import static org.junit.Assert.assertTrue; 022 023import java.util.List; 024import java.util.Random; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.hbase.HBaseClassTestRule; 027import org.apache.hadoop.hbase.HBaseTestingUtility; 028import org.apache.hadoop.hbase.HTableDescriptor; 029import org.apache.hadoop.hbase.TableName; 030import org.apache.hadoop.hbase.client.RegionInfo; 031import org.apache.hadoop.hbase.procedure2.Procedure; 032import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 033import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 034import org.apache.hadoop.hbase.testclassification.MasterTests; 035import org.apache.hadoop.hbase.testclassification.MediumTests; 036import org.junit.After; 037import org.junit.AfterClass; 038import org.junit.Before; 039import org.junit.BeforeClass; 040import org.junit.ClassRule; 041import org.junit.Rule; 042import org.junit.Test; 043import org.junit.experimental.categories.Category; 044import org.junit.rules.TestName; 045import org.slf4j.Logger; 046import org.slf4j.LoggerFactory; 047 048@Category({MasterTests.class, MediumTests.class}) 049public class TestProcedureAdmin { 050 051 @ClassRule 052 public static final HBaseClassTestRule CLASS_RULE = 053 HBaseClassTestRule.forClass(TestProcedureAdmin.class); 054 055 private static final Logger LOG = LoggerFactory.getLogger(TestProcedureAdmin.class); 056 @Rule public TestName name = new TestName(); 057 058 protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 059 060 061 private static void setupConf(Configuration conf) { 062 conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1); 063 conf.setInt(MasterProcedureConstants.MASTER_URGENT_PROCEDURE_THREADS, 0); 064 } 065 066 @BeforeClass 067 public static void setupCluster() throws Exception { 068 setupConf(UTIL.getConfiguration()); 069 UTIL.startMiniCluster(1); 070 } 071 072 @AfterClass 073 public static void cleanupTest() throws Exception { 074 try { 075 UTIL.shutdownMiniCluster(); 076 } catch (Exception e) { 077 LOG.warn("failure shutting down cluster", e); 078 } 079 } 080 081 @Before 082 public void setup() throws Exception { 083 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 084 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false); 085 assertTrue("expected executor to be running", procExec.isRunning()); 086 } 087 088 @After 089 public void tearDown() throws Exception { 090 assertTrue("expected executor to be running", getMasterProcedureExecutor().isRunning()); 091 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false); 092 for (HTableDescriptor htd: UTIL.getAdmin().listTables()) { 093 LOG.info("Tear down, remove table=" + htd.getTableName()); 094 UTIL.deleteTable(htd.getTableName()); 095 } 096 } 097 098 @Test 099 public void testAbortProcedureSuccess() throws Exception { 100 final TableName tableName = TableName.valueOf(name.getMethodName()); 101 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 102 103 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); 104 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 105 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 106 // Submit an abortable procedure 107 long procId = procExec.submitProcedure( 108 new DisableTableProcedure(procExec.getEnvironment(), tableName, false)); 109 // Wait for one step to complete 110 ProcedureTestingUtility.waitProcedure(procExec, procId); 111 112 boolean abortResult = procExec.abort(procId, true); 113 assertTrue(abortResult); 114 115 MasterProcedureTestingUtility.testRestartWithAbort(procExec, procId); 116 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 117 // Validate the disable table procedure was aborted successfully 118 MasterProcedureTestingUtility.validateTableIsEnabled( 119 UTIL.getHBaseCluster().getMaster(), 120 tableName); 121 } 122 123 @Test 124 public void testAbortProcedureFailure() throws Exception { 125 final TableName tableName = TableName.valueOf(name.getMethodName()); 126 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 127 128 RegionInfo[] regions = 129 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); 130 UTIL.getAdmin().disableTable(tableName); 131 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 132 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 133 // Submit an un-abortable procedure 134 long procId = procExec.submitProcedure( 135 new DeleteTableProcedure(procExec.getEnvironment(), tableName)); 136 // Wait for a couple of steps to complete (first step "prepare" is abortable) 137 ProcedureTestingUtility.waitProcedure(procExec, procId); 138 for (int i = 0; i < 2; ++i) { 139 ProcedureTestingUtility.assertProcNotYetCompleted(procExec, procId); 140 ProcedureTestingUtility.restart(procExec); 141 ProcedureTestingUtility.waitProcedure(procExec, procId); 142 } 143 144 boolean abortResult = procExec.abort(procId, true); 145 assertFalse(abortResult); 146 147 MasterProcedureTestingUtility.testRestartWithAbort(procExec, procId); 148 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 149 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 150 // Validate the delete table procedure was not aborted 151 MasterProcedureTestingUtility.validateTableDeletion( 152 UTIL.getHBaseCluster().getMaster(), tableName); 153 } 154 155 @Test 156 public void testAbortProcedureInterruptedNotAllowed() throws Exception { 157 final TableName tableName = TableName.valueOf(name.getMethodName()); 158 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 159 160 RegionInfo[] regions = 161 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); 162 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 163 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 164 // Submit a procedure 165 long procId = procExec.submitProcedure( 166 new DisableTableProcedure(procExec.getEnvironment(), tableName, true)); 167 // Wait for one step to complete 168 ProcedureTestingUtility.waitProcedure(procExec, procId); 169 170 // Set the mayInterruptIfRunning flag to false 171 boolean abortResult = procExec.abort(procId, false); 172 assertFalse(abortResult); 173 174 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false); 175 ProcedureTestingUtility.restart(procExec); 176 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 177 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 178 // Validate the delete table procedure was not aborted 179 MasterProcedureTestingUtility.validateTableIsDisabled( 180 UTIL.getHBaseCluster().getMaster(), tableName); 181 } 182 183 @Test 184 public void testAbortNonExistProcedure() throws Exception { 185 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 186 Random randomGenerator = new Random(); 187 long procId; 188 // Generate a non-existing procedure 189 do { 190 procId = randomGenerator.nextLong(); 191 } while (procExec.getResult(procId) != null); 192 193 boolean abortResult = procExec.abort(procId, true); 194 assertFalse(abortResult); 195 } 196 197 @Test 198 public void testGetProcedure() throws Exception { 199 final TableName tableName = TableName.valueOf(name.getMethodName()); 200 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 201 202 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); 203 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 204 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 205 206 long procId = procExec.submitProcedure( 207 new DisableTableProcedure(procExec.getEnvironment(), tableName, false)); 208 // Wait for one step to complete 209 ProcedureTestingUtility.waitProcedure(procExec, procId); 210 211 List<Procedure<MasterProcedureEnv>> procedures = procExec.getProcedures(); 212 assertTrue(procedures.size() >= 1); 213 boolean found = false; 214 for (Procedure<?> proc: procedures) { 215 if (proc.getProcId() == procId) { 216 assertTrue(proc.isRunnable()); 217 found = true; 218 } else { 219 assertTrue(proc.isSuccess()); 220 } 221 } 222 assertTrue(found); 223 224 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false); 225 ProcedureTestingUtility.restart(procExec); 226 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 227 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 228 procedures = procExec.getProcedures(); 229 for (Procedure proc: procedures) { 230 assertTrue(proc.isSuccess()); 231 } 232 } 233 234 private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() { 235 return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor(); 236 } 237}