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.assertNotEquals;
022import static org.junit.jupiter.api.Assertions.assertTrue;
023
024import java.io.IOException;
025import org.apache.hadoop.conf.Configuration;
026import org.apache.hadoop.hbase.HBaseTestingUtil;
027import org.apache.hadoop.hbase.ServerName;
028import org.apache.hadoop.hbase.TableName;
029import org.apache.hadoop.hbase.client.Get;
030import org.apache.hadoop.hbase.client.Put;
031import org.apache.hadoop.hbase.client.RegionInfo;
032import org.apache.hadoop.hbase.client.Result;
033import org.apache.hadoop.hbase.client.Table;
034import org.apache.hadoop.hbase.master.procedure.MasterProcedureConstants;
035import org.apache.hadoop.hbase.testclassification.LargeTests;
036import org.apache.hadoop.hbase.testclassification.MasterTests;
037import org.apache.hadoop.hbase.util.Bytes;
038import org.junit.jupiter.api.AfterEach;
039import org.junit.jupiter.api.BeforeEach;
040import org.junit.jupiter.api.Tag;
041import org.junit.jupiter.api.Test;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045@Tag(MasterTests.TAG)
046@Tag(LargeTests.TAG)
047public class TestAssignmentOnRSCrash {
048
049  private static final Logger LOG = LoggerFactory.getLogger(TestAssignmentOnRSCrash.class);
050
051  private static final TableName TEST_TABLE = TableName.valueOf("testb");
052  private static final String FAMILY_STR = "f";
053  private static final byte[] FAMILY = Bytes.toBytes(FAMILY_STR);
054  private static final int NUM_RS = 3;
055
056  private HBaseTestingUtil UTIL;
057
058  private static void setupConf(Configuration conf) {
059    conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
060    conf.set("hbase.balancer.tablesOnMaster", "none");
061  }
062
063  @BeforeEach
064  public void setup() throws Exception {
065    UTIL = new HBaseTestingUtil();
066
067    setupConf(UTIL.getConfiguration());
068    UTIL.startMiniCluster(NUM_RS);
069
070    UTIL.createTable(TEST_TABLE, new byte[][] { FAMILY }, new byte[][] { Bytes.toBytes("B"),
071      Bytes.toBytes("D"), Bytes.toBytes("F"), Bytes.toBytes("L") });
072  }
073
074  @AfterEach
075  public void tearDown() throws Exception {
076    UTIL.shutdownMiniCluster();
077  }
078
079  @Test
080  public void testKillRsWithUserRegionWithData() throws Exception {
081    testCrashRsWithUserRegion(true, true);
082  }
083
084  @Test
085  public void testKillRsWithUserRegionWithoutData() throws Exception {
086    testCrashRsWithUserRegion(true, false);
087  }
088
089  @Test
090  public void testStopRsWithUserRegionWithData() throws Exception {
091    testCrashRsWithUserRegion(false, true);
092  }
093
094  @Test
095  public void testStopRsWithUserRegionWithoutData() throws Exception {
096    testCrashRsWithUserRegion(false, false);
097  }
098
099  private void testCrashRsWithUserRegion(final boolean kill, final boolean withData)
100    throws Exception {
101    final int NROWS = 100;
102    int nkilled = 0;
103    for (RegionInfo hri : UTIL.getAdmin().getRegions(TEST_TABLE)) {
104      ServerName serverName = AssignmentTestingUtil.getServerHoldingRegion(UTIL, hri);
105      if (AssignmentTestingUtil.isServerHoldingMeta(UTIL, serverName)) continue;
106
107      if (withData) {
108        testInsert(hri, NROWS);
109      }
110
111      // wait for regions to enter in transition and then to get out of transition
112      AssignmentTestingUtil.crashRs(UTIL, serverName, kill);
113      AssignmentTestingUtil.waitForRegionToBeInTransition(UTIL, hri);
114      UTIL.waitUntilNoRegionsInTransition();
115
116      if (withData) {
117        assertEquals(NROWS, testGet(hri, NROWS));
118      }
119
120      // region should be moved to another RS
121      assertNotEquals(serverName, AssignmentTestingUtil.getServerHoldingRegion(UTIL, hri));
122
123      if (++nkilled == (NUM_RS - 1)) {
124        break;
125      }
126    }
127    assertTrue(nkilled > 0, "expected RSs to be killed");
128  }
129
130  @Test
131  public void testKillRsWithMetaRegion() throws Exception {
132    testCrashRsWithMetaRegion(true);
133  }
134
135  @Test
136  public void testStopRsWithMetaRegion() throws Exception {
137    testCrashRsWithMetaRegion(false);
138  }
139
140  private void testCrashRsWithMetaRegion(final boolean kill) throws Exception {
141    int nkilled = 0;
142    for (RegionInfo hri : AssignmentTestingUtil.getMetaRegions(UTIL)) {
143      ServerName serverName = AssignmentTestingUtil.crashRsWithRegion(UTIL, hri, kill);
144
145      // wait for region to enter in transition and then to get out of transition
146      AssignmentTestingUtil.waitForRegionToBeInTransition(UTIL, hri);
147      UTIL.waitUntilNoRegionsInTransition();
148      testGet(hri, 10);
149
150      // region should be moved to another RS
151      assertNotEquals(serverName, AssignmentTestingUtil.getServerHoldingRegion(UTIL, hri));
152
153      if (++nkilled == (NUM_RS - 1)) {
154        break;
155      }
156    }
157    assertTrue(nkilled > 0, "expected RSs to be killed");
158  }
159
160  private void testInsert(final RegionInfo hri, final int nrows) throws IOException {
161    final Table table = UTIL.getConnection().getTable(hri.getTable());
162    for (int i = 0; i < nrows; ++i) {
163      final byte[] row = Bytes.add(hri.getStartKey(), Bytes.toBytes(i));
164      final Put put = new Put(row);
165      put.addColumn(FAMILY, null, row);
166      table.put(put);
167    }
168  }
169
170  public int testGet(final RegionInfo hri, final int nrows) throws IOException {
171    int nresults = 0;
172    final Table table = UTIL.getConnection().getTable(hri.getTable());
173    for (int i = 0; i < nrows; ++i) {
174      final byte[] row = Bytes.add(hri.getStartKey(), Bytes.toBytes(i));
175      final Result result = table.get(new Get(row));
176      if (result != null && !result.isEmpty() && Bytes.equals(row, result.getValue(FAMILY, null))) {
177        nresults++;
178      }
179    }
180    return nresults;
181  }
182}