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.jupiter.api.Assertions.assertFalse; 021import static org.junit.jupiter.api.Assertions.assertTrue; 022 023import java.util.List; 024import java.util.concurrent.ThreadLocalRandom; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.hbase.HBaseTestingUtil; 027import org.apache.hadoop.hbase.TableName; 028import org.apache.hadoop.hbase.client.RegionInfo; 029import org.apache.hadoop.hbase.client.TableDescriptor; 030import org.apache.hadoop.hbase.procedure2.Procedure; 031import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 032import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 033import org.apache.hadoop.hbase.testclassification.MasterTests; 034import org.apache.hadoop.hbase.testclassification.MediumTests; 035import org.junit.jupiter.api.AfterAll; 036import org.junit.jupiter.api.AfterEach; 037import org.junit.jupiter.api.BeforeAll; 038import org.junit.jupiter.api.BeforeEach; 039import org.junit.jupiter.api.Tag; 040import org.junit.jupiter.api.Test; 041import org.junit.jupiter.api.TestInfo; 042import org.slf4j.Logger; 043import org.slf4j.LoggerFactory; 044 045@Tag(MasterTests.TAG) 046@Tag(MediumTests.TAG) 047public class TestProcedureAdmin { 048 049 private static final Logger LOG = LoggerFactory.getLogger(TestProcedureAdmin.class); 050 private String testMethodName; 051 052 @BeforeEach 053 public void setTestMethod(TestInfo testInfo) { 054 testMethodName = testInfo.getTestMethod().get().getName(); 055 } 056 057 protected static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 058 059 private static void setupConf(Configuration conf) { 060 conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1); 061 } 062 063 @BeforeAll 064 public static void setupCluster() throws Exception { 065 setupConf(UTIL.getConfiguration()); 066 UTIL.startMiniCluster(1); 067 } 068 069 @AfterAll 070 public static void cleanupTest() throws Exception { 071 try { 072 UTIL.shutdownMiniCluster(); 073 } catch (Exception e) { 074 LOG.warn("failure shutting down cluster", e); 075 } 076 } 077 078 @BeforeEach 079 public void setup() throws Exception { 080 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 081 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false); 082 assertTrue(procExec.isRunning(), "expected executor to be running"); 083 } 084 085 @AfterEach 086 public void tearDown() throws Exception { 087 assertTrue(getMasterProcedureExecutor().isRunning(), "expected executor to be running"); 088 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false); 089 for (TableDescriptor htd : UTIL.getAdmin().listTableDescriptors()) { 090 LOG.info("Tear down, remove table=" + htd.getTableName()); 091 UTIL.deleteTable(htd.getTableName()); 092 } 093 } 094 095 @Test 096 public void testAbortProcedureSuccess() throws Exception { 097 final TableName tableName = TableName.valueOf(testMethodName); 098 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 099 100 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); 101 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 102 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 103 // Submit an abortable procedure 104 long procId = procExec 105 .submitProcedure(new DisableTableProcedure(procExec.getEnvironment(), tableName, false)); 106 // Wait for one step to complete 107 ProcedureTestingUtility.waitProcedure(procExec, procId); 108 109 boolean abortResult = procExec.abort(procId, true); 110 assertTrue(abortResult); 111 112 MasterProcedureTestingUtility.testRestartWithAbort(procExec, procId); 113 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 114 // Validate the disable table procedure was aborted successfully 115 MasterProcedureTestingUtility.validateTableIsEnabled(UTIL.getHBaseCluster().getMaster(), 116 tableName); 117 } 118 119 @Test 120 public void testAbortProcedureFailure() throws Exception { 121 final TableName tableName = TableName.valueOf(testMethodName); 122 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 123 124 RegionInfo[] regions = 125 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); 126 UTIL.getAdmin().disableTable(tableName); 127 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 128 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 129 // Submit an un-abortable procedure 130 long procId = 131 procExec.submitProcedure(new DeleteTableProcedure(procExec.getEnvironment(), tableName)); 132 // Wait for a couple of steps to complete (first step "prepare" is abortable) 133 ProcedureTestingUtility.waitProcedure(procExec, procId); 134 for (int i = 0; i < 2; ++i) { 135 ProcedureTestingUtility.assertProcNotYetCompleted(procExec, procId); 136 ProcedureTestingUtility.restart(procExec); 137 ProcedureTestingUtility.waitProcedure(procExec, procId); 138 } 139 140 boolean abortResult = procExec.abort(procId, true); 141 assertFalse(abortResult); 142 143 MasterProcedureTestingUtility.testRestartWithAbort(procExec, procId); 144 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 145 ProcedureTestingUtility.assertProcNotFailed(procExec, procId); 146 // Validate the delete table procedure was not aborted 147 MasterProcedureTestingUtility.validateTableDeletion(UTIL.getHBaseCluster().getMaster(), 148 tableName); 149 } 150 151 @Test 152 public void testAbortProcedureInterruptedNotAllowed() throws Exception { 153 final TableName tableName = TableName.valueOf(testMethodName); 154 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 155 156 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f"); 157 ProcedureTestingUtility.waitNoProcedureRunning(procExec); 158 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true); 159 // Submit a procedure 160 long procId = procExec 161 .submitProcedure(new DisableTableProcedure(procExec.getEnvironment(), tableName, true)); 162 // Wait for one step to complete 163 ProcedureTestingUtility.waitProcedure(procExec, procId); 164 // After HBASE-28210, the injection of kill before update is moved before we add rollback 165 // step, so here we need to run two steps, otherwise we will not consider the procedure as 166 // executed 167 MasterProcedureTestingUtility.restartMasterProcedureExecutor(procExec); 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(UTIL.getHBaseCluster().getMaster(), 180 tableName); 181 } 182 183 @Test 184 public void testAbortNonExistProcedure() throws Exception { 185 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 186 long procId; 187 // Generate a non-existing procedure 188 do { 189 procId = ThreadLocalRandom.current().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(testMethodName); 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 206 .submitProcedure(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}