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