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}