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 */
018
019package org.apache.hadoop.hbase.client;
020
021import static junit.framework.TestCase.assertTrue;
022import static org.junit.Assert.assertEquals;
023
024import java.util.Arrays;
025import java.util.List;
026import java.util.stream.Collectors;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseTestingUtility;
029import org.apache.hadoop.hbase.ServerName;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.master.RegionState;
032import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
033import org.apache.hadoop.hbase.master.procedure.TableProcedureInterface;
034import org.apache.hadoop.hbase.procedure2.Procedure;
035import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
036import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException;
037import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
038import org.apache.hadoop.hbase.regionserver.HRegionServer;
039import org.apache.hadoop.hbase.testclassification.ClientTests;
040import org.apache.hadoop.hbase.testclassification.LargeTests;
041import org.apache.hadoop.hbase.util.Bytes;
042import org.apache.hadoop.hbase.util.Threads;
043import org.junit.AfterClass;
044import org.junit.BeforeClass;
045import org.junit.ClassRule;
046import org.junit.Rule;
047import org.junit.Test;
048import org.junit.experimental.categories.Category;
049import org.junit.rules.TestName;
050import org.junit.runner.RunWith;
051import org.junit.runners.Parameterized;
052import org.junit.runners.Parameterized.Parameter;
053import org.junit.runners.Parameterized.Parameters;
054import org.slf4j.Logger;
055import org.slf4j.LoggerFactory;
056
057import org.apache.hbase.thirdparty.com.google.common.io.Closeables;
058
059import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
060
061/**
062 * Class to test HBaseHbck.
063 * Spins up the minicluster once at test start and then takes it down afterward.
064 * Add any testing of HBaseHbck functionality here.
065 */
066@RunWith(Parameterized.class)
067@Category({LargeTests.class, ClientTests.class})
068public class TestHbck {
069  @ClassRule
070  public static final HBaseClassTestRule CLASS_RULE =
071      HBaseClassTestRule.forClass(TestHbck.class);
072
073  private static final Logger LOG = LoggerFactory.getLogger(TestHbck.class);
074  private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
075
076  @Rule
077  public TestName name = new TestName();
078
079  @Parameter
080  public boolean async;
081
082  private static final TableName TABLE_NAME = TableName.valueOf(TestHbck.class.getSimpleName());
083
084  private static ProcedureExecutor<MasterProcedureEnv> procExec;
085
086  private static AsyncConnection ASYNC_CONN;
087
088  @Parameters(name = "{index}: async={0}")
089  public static List<Object[]> params() {
090    return Arrays.asList(new Object[] { false }, new Object[] { true });
091  }
092
093  private Hbck getHbck() throws Exception {
094    if (async) {
095      return ASYNC_CONN.getHbck().get();
096    } else {
097      return TEST_UTIL.getHbck();
098    }
099  }
100
101  @BeforeClass
102  public static void setUpBeforeClass() throws Exception {
103    TEST_UTIL.startMiniCluster(3);
104    TEST_UTIL.createMultiRegionTable(TABLE_NAME, Bytes.toBytes("family1"), 5);
105    procExec = TEST_UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();
106    ASYNC_CONN = ConnectionFactory.createAsyncConnection(TEST_UTIL.getConfiguration()).get();
107  }
108
109  @AfterClass
110  public static void tearDownAfterClass() throws Exception {
111    Closeables.close(ASYNC_CONN, true);
112    TEST_UTIL.shutdownMiniCluster();
113  }
114
115  public static class SuspendProcedure extends
116      ProcedureTestingUtility.NoopProcedure<MasterProcedureEnv> implements TableProcedureInterface {
117    public SuspendProcedure() {
118      super();
119    }
120
121    @Override
122    protected Procedure[] execute(final MasterProcedureEnv env)
123        throws ProcedureSuspendedException {
124      // Always suspend the procedure
125      throw new ProcedureSuspendedException();
126    }
127
128    @Override
129    public TableName getTableName() {
130      return TABLE_NAME;
131    }
132
133    @Override
134    public TableOperationType getTableOperationType() {
135      return TableOperationType.READ;
136    }
137  }
138
139  @Test
140  public void testBypassProcedure() throws Exception {
141    // SuspendProcedure
142    final SuspendProcedure proc = new SuspendProcedure();
143    long procId = procExec.submitProcedure(proc);
144    Thread.sleep(500);
145
146    //bypass the procedure
147    List<Long> pids = Arrays.<Long>asList(procId);
148    List<Boolean> results = getHbck().bypassProcedure(pids, 30000, false, false);
149    assertTrue("Failed to by pass procedure!", results.get(0));
150    TEST_UTIL.waitFor(5000, () -> proc.isSuccess() && proc.isBypass());
151    LOG.info("{} finished", proc);
152  }
153
154  @Test
155  public void testSetTableStateInMeta() throws Exception {
156    Hbck hbck = getHbck();
157    // set table state to DISABLED
158    hbck.setTableStateInMeta(new TableState(TABLE_NAME, TableState.State.DISABLED));
159    // Method {@link Hbck#setTableStateInMeta()} returns previous state, which in this case
160    // will be DISABLED
161    TableState prevState =
162        hbck.setTableStateInMeta(new TableState(TABLE_NAME, TableState.State.ENABLED));
163    assertTrue("Incorrect previous state! expeced=DISABLED, found=" + prevState.getState(),
164        prevState.isDisabled());
165  }
166
167  @Test
168  public void testAssigns() throws Exception {
169    Hbck hbck = getHbck();
170    try (Admin admin = TEST_UTIL.getConnection().getAdmin()) {
171      List<RegionInfo> regions = admin.getRegions(TABLE_NAME);
172      for (RegionInfo ri: regions) {
173        RegionState rs = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().
174            getRegionStates().getRegionState(ri.getEncodedName());
175        LOG.info("RS: {}", rs.toString());
176      }
177      List<Long> pids = hbck.unassigns(regions.stream().map(r -> r.getEncodedName()).
178          collect(Collectors.toList()));
179      waitOnPids(pids);
180      for (RegionInfo ri: regions) {
181        RegionState rs = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().
182            getRegionStates().getRegionState(ri.getEncodedName());
183        LOG.info("RS: {}", rs.toString());
184        assertTrue(rs.toString(), rs.isClosed());
185      }
186      pids = hbck.assigns(regions.stream().map(r -> r.getEncodedName()).
187          collect(Collectors.toList()));
188      waitOnPids(pids);
189      for (RegionInfo ri: regions) {
190        RegionState rs = TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().
191            getRegionStates().getRegionState(ri.getEncodedName());
192        LOG.info("RS: {}", rs.toString());
193        assertTrue(rs.toString(), rs.isOpened());
194      }
195      // What happens if crappy region list passed?
196      pids = hbck.assigns(Arrays.stream(new String [] {"a", "some rubbish name"}).
197          collect(Collectors.toList()));
198      for (long pid: pids) {
199        assertEquals(org.apache.hadoop.hbase.procedure2.Procedure.NO_PROC_ID, pid);
200      }
201    }
202  }
203
204  @Test
205  public void testScheduleSCP() throws Exception {
206    HRegionServer testRs = TEST_UTIL.getRSForFirstRegionInTable(TABLE_NAME);
207    TEST_UTIL.loadTable(TEST_UTIL.getConnection().getTable(TABLE_NAME), Bytes.toBytes("family1"),
208      true);
209    ServerName serverName = testRs.getServerName();
210    Hbck hbck = getHbck();
211    List<Long> pids =
212        hbck.scheduleServerCrashProcedure(Arrays.asList(ProtobufUtil.toServerName(serverName)));
213    assertTrue(pids.get(0) > 0);
214    LOG.info("pid is {}", pids.get(0));
215
216    pids = hbck.scheduleServerCrashProcedure(Arrays.asList(ProtobufUtil.toServerName(serverName)));
217    assertTrue(pids.get(0) == -1);
218    LOG.info("pid is {}", pids.get(0));
219  }
220
221  private void waitOnPids(List<Long> pids) {
222    for (Long pid: pids) {
223      while (!TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().
224          isFinished(pid)) {
225        Threads.sleep(100);
226      }
227    }
228  }
229}