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.assignment;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023
024import java.io.IOException;
025import org.apache.hadoop.hbase.HBaseClassTestRule;
026import org.apache.hadoop.hbase.HBaseTestingUtility;
027import org.apache.hadoop.hbase.TableName;
028import org.apache.hadoop.hbase.client.RegionInfo;
029import org.apache.hadoop.hbase.master.HMaster;
030import org.apache.hadoop.hbase.master.procedure.MasterProcedureConstants;
031import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
032import org.apache.hadoop.hbase.master.procedure.MasterProcedureTestingUtility;
033import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
034import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
035import org.apache.hadoop.hbase.regionserver.HRegion;
036import org.apache.hadoop.hbase.regionserver.HRegionServer;
037import org.apache.hadoop.hbase.testclassification.MasterTests;
038import org.apache.hadoop.hbase.testclassification.MediumTests;
039import org.apache.hadoop.hbase.util.Bytes;
040import org.junit.After;
041import org.junit.AfterClass;
042import org.junit.Before;
043import org.junit.BeforeClass;
044import org.junit.ClassRule;
045import org.junit.Rule;
046import org.junit.Test;
047import org.junit.experimental.categories.Category;
048import org.junit.rules.TestName;
049
050@Category({ MasterTests.class, MediumTests.class })
051public class TestTransitRegionStateProcedure {
052
053  @ClassRule
054  public static final HBaseClassTestRule CLASS_RULE =
055    HBaseClassTestRule.forClass(TestTransitRegionStateProcedure.class);
056
057  private static HBaseTestingUtility UTIL = new HBaseTestingUtility();
058
059  private static byte[] CF = Bytes.toBytes("cf");
060
061  @Rule
062  public TestName name = new TestName();
063
064  private TableName tableName;
065
066  @BeforeClass
067  public static void setUpBeforeClass() throws Exception {
068    UTIL.getConfiguration().setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
069    UTIL.startMiniCluster(3);
070    UTIL.getAdmin().balancerSwitch(false, true);
071  }
072
073  @AfterClass
074  public static void tearDownAfterClass() throws Exception {
075    UTIL.shutdownMiniCluster();
076  }
077
078  @Before
079  public void setUp() throws IOException, InterruptedException {
080    tableName = TableName.valueOf(name.getMethodName());
081    UTIL.createTable(tableName, CF);
082    UTIL.waitTableAvailable(tableName);
083  }
084
085  private void resetProcExecutorTestingKillFlag() {
086    ProcedureExecutor<MasterProcedureEnv> procExec =
087      UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
088    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
089    assertTrue("expected executor to be running", procExec.isRunning());
090  }
091
092  @After
093  public void tearDown() throws IOException {
094    resetProcExecutorTestingKillFlag();
095    UTIL.deleteTable(tableName);
096  }
097
098  private void testRecoveryAndDoubleExcution(TransitRegionStateProcedure proc) throws Exception {
099    HMaster master = UTIL.getHBaseCluster().getMaster();
100    AssignmentManager am = master.getAssignmentManager();
101    RegionStateNode regionNode = am.getRegionStates().getRegionStateNode(proc.getRegion());
102    assertFalse(regionNode.isInTransition());
103    regionNode.setProcedure(proc);
104    assertTrue(regionNode.isInTransition());
105    ProcedureExecutor<MasterProcedureEnv> procExec = master.getMasterProcedureExecutor();
106    ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
107    long procId = procExec.submitProcedure(proc);
108    MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId);
109    regionNode = am.getRegionStates().getRegionStateNode(proc.getRegion());
110    assertFalse(regionNode.isInTransition());
111  }
112
113  @Test
114  public void testRecoveryAndDoubleExecutionMove() throws Exception {
115    MasterProcedureEnv env =
116      UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor().getEnvironment();
117    HRegion region = UTIL.getMiniHBaseCluster().getRegions(tableName).get(0);
118    long openSeqNum = region.getOpenSeqNum();
119    TransitRegionStateProcedure proc =
120      TransitRegionStateProcedure.move(env, region.getRegionInfo(), null);
121    testRecoveryAndDoubleExcution(proc);
122    HRegion region2 = UTIL.getMiniHBaseCluster().getRegions(tableName).get(0);
123    long openSeqNum2 = region2.getOpenSeqNum();
124    // confirm that the region is successfully opened
125    assertTrue(openSeqNum2 > openSeqNum);
126  }
127
128  @Test
129  public void testRecoveryAndDoubleExecutionReopen() throws Exception {
130    MasterProcedureEnv env =
131      UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor().getEnvironment();
132    HRegionServer rs = UTIL.getRSForFirstRegionInTable(tableName);
133    HRegion region = rs.getRegions(tableName).get(0);
134    region.addReadRequestsCount(1);
135    region.addWriteRequestsCount(2);
136    long openSeqNum = region.getOpenSeqNum();
137    TransitRegionStateProcedure proc =
138      TransitRegionStateProcedure.reopen(env, region.getRegionInfo());
139    testRecoveryAndDoubleExcution(proc);
140    // should still be on the same RS
141    HRegion region2 = rs.getRegions(tableName).get(0);
142    long openSeqNum2 = region2.getOpenSeqNum();
143    // confirm that the region is successfully opened
144    assertTrue(openSeqNum2 > openSeqNum);
145    // we check the available by scan after table created,
146    // so the readRequestsCount should be 2 here
147    assertEquals(2, region2.getReadRequestsCount());
148    assertEquals(2, region2.getWriteRequestsCount());
149  }
150
151  @Test
152  public void testRecoveryAndDoubleExecutionUnassignAndAssign() throws Exception {
153    HMaster master = UTIL.getMiniHBaseCluster().getMaster();
154    MasterProcedureEnv env = master.getMasterProcedureExecutor().getEnvironment();
155    HRegion region = UTIL.getMiniHBaseCluster().getRegions(tableName).get(0);
156    RegionInfo regionInfo = region.getRegionInfo();
157    long openSeqNum = region.getOpenSeqNum();
158    TransitRegionStateProcedure unassign = TransitRegionStateProcedure.unassign(env, regionInfo);
159    testRecoveryAndDoubleExcution(unassign);
160    AssignmentManager am = master.getAssignmentManager();
161    assertTrue(am.getRegionStates().getRegionState(regionInfo).isClosed());
162
163    TransitRegionStateProcedure assign = TransitRegionStateProcedure.assign(env, regionInfo, null);
164    testRecoveryAndDoubleExcution(assign);
165
166    HRegion region2 = UTIL.getMiniHBaseCluster().getRegions(tableName).get(0);
167    long openSeqNum2 = region2.getOpenSeqNum();
168    // confirm that the region is successfully opened
169    assertTrue(openSeqNum2 > openSeqNum);
170  }
171}