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