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;
019
020import static org.junit.Assert.assertArrayEquals;
021import static org.junit.Assert.assertEquals;
022import static org.junit.Assert.assertFalse;
023import static org.junit.Assert.assertTrue;
024import static org.junit.Assert.fail;
025
026import java.io.IOException;
027import java.util.List;
028
029import org.apache.hadoop.fs.FileSystem;
030import org.apache.hadoop.fs.Path;
031import org.apache.hadoop.hbase.HBaseClassTestRule;
032import org.apache.hadoop.hbase.HBaseTestingUtility;
033import org.apache.hadoop.hbase.HColumnDescriptor;
034import org.apache.hadoop.hbase.HConstants;
035import org.apache.hadoop.hbase.HTableDescriptor;
036import org.apache.hadoop.hbase.MetaTableAccessor;
037import org.apache.hadoop.hbase.MiniHBaseCluster;
038import org.apache.hadoop.hbase.PleaseHoldException;
039import org.apache.hadoop.hbase.ServerName;
040import org.apache.hadoop.hbase.TableName;
041import org.apache.hadoop.hbase.UnknownRegionException;
042import org.apache.hadoop.hbase.client.Admin;
043import org.apache.hadoop.hbase.client.RegionInfo;
044import org.apache.hadoop.hbase.client.RegionInfoBuilder;
045import org.apache.hadoop.hbase.client.Table;
046import org.apache.hadoop.hbase.client.TableState;
047import org.apache.hadoop.hbase.testclassification.MasterTests;
048import org.apache.hadoop.hbase.testclassification.MediumTests;
049import org.apache.hadoop.hbase.util.Bytes;
050import org.apache.hadoop.hbase.util.HBaseFsck;
051import org.apache.hadoop.hbase.util.Pair;
052import org.apache.hadoop.util.StringUtils;
053import org.junit.AfterClass;
054import org.junit.BeforeClass;
055import org.junit.ClassRule;
056import org.junit.Rule;
057import org.junit.Test;
058import org.junit.experimental.categories.Category;
059import org.junit.rules.TestName;
060import org.slf4j.Logger;
061import org.slf4j.LoggerFactory;
062
063import org.apache.hbase.thirdparty.com.google.common.base.Joiner;
064
065@Category({MasterTests.class, MediumTests.class})
066public class TestMaster {
067
068  @ClassRule
069  public static final HBaseClassTestRule CLASS_RULE =
070      HBaseClassTestRule.forClass(TestMaster.class);
071
072  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
073  private static final Logger LOG = LoggerFactory.getLogger(TestMaster.class);
074  private static final TableName TABLENAME =
075      TableName.valueOf("TestMaster");
076  private static final byte[] FAMILYNAME = Bytes.toBytes("fam");
077  private static Admin admin;
078
079  @Rule
080  public TestName name = new TestName();
081
082  @BeforeClass
083  public static void beforeAllTests() throws Exception {
084    // we will retry operations when PleaseHoldException is thrown
085    TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 3);
086    // Start a cluster of two regionservers.
087    TEST_UTIL.startMiniCluster(2);
088    admin = TEST_UTIL.getAdmin();
089  }
090
091  @AfterClass
092  public static void afterAllTests() throws Exception {
093    TEST_UTIL.shutdownMiniCluster();
094  }
095
096  @Test
097  @SuppressWarnings("deprecation")
098  public void testMasterOpsWhileSplitting() throws Exception {
099    MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
100    HMaster m = cluster.getMaster();
101
102    try (Table ht = TEST_UTIL.createTable(TABLENAME, FAMILYNAME)) {
103      assertTrue(m.getTableStateManager().isTableState(TABLENAME, TableState.State.ENABLED));
104      TEST_UTIL.loadTable(ht, FAMILYNAME, false);
105    }
106
107    List<Pair<RegionInfo, ServerName>> tableRegions = MetaTableAccessor.getTableRegionsAndLocations(
108        m.getConnection(), TABLENAME);
109    LOG.info("Regions after load: " + Joiner.on(',').join(tableRegions));
110    assertEquals(1, tableRegions.size());
111    assertArrayEquals(HConstants.EMPTY_START_ROW,
112        tableRegions.get(0).getFirst().getStartKey());
113    assertArrayEquals(HConstants.EMPTY_END_ROW,
114        tableRegions.get(0).getFirst().getEndKey());
115
116    // Now trigger a split and stop when the split is in progress
117    LOG.info("Splitting table");
118    TEST_UTIL.getAdmin().split(TABLENAME);
119
120    LOG.info("Making sure we can call getTableRegions while opening");
121    while (tableRegions.size() < 3) {
122      tableRegions = MetaTableAccessor.getTableRegionsAndLocations(m.getConnection(),
123          TABLENAME, false);
124      Thread.sleep(100);
125    }
126    LOG.info("Regions: " + Joiner.on(',').join(tableRegions));
127    // We have three regions because one is split-in-progress
128    assertEquals(3, tableRegions.size());
129    LOG.info("Making sure we can call getTableRegionClosest while opening");
130    Pair<RegionInfo, ServerName> pair =
131        m.getTableRegionForRow(TABLENAME, Bytes.toBytes("cde"));
132    LOG.info("Result is: " + pair);
133    Pair<RegionInfo, ServerName> tableRegionFromName =
134        MetaTableAccessor.getRegion(m.getConnection(),
135          pair.getFirst().getRegionName());
136    assertTrue(RegionInfo.COMPARATOR.compare(tableRegionFromName.getFirst(), pair.getFirst()) == 0);
137  }
138
139  @Test
140  public void testMoveRegionWhenNotInitialized() {
141    MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster();
142    HMaster m = cluster.getMaster();
143    try {
144      m.setInitialized(false); // fake it, set back later
145      RegionInfo meta = RegionInfoBuilder.FIRST_META_REGIONINFO;
146      m.move(meta.getEncodedNameAsBytes(), null);
147      fail("Region should not be moved since master is not initialized");
148    } catch (IOException ioe) {
149      assertTrue(ioe instanceof PleaseHoldException);
150    } finally {
151      m.setInitialized(true);
152    }
153  }
154
155  @Test
156  public void testMoveThrowsUnknownRegionException() throws IOException {
157    final TableName tableName = TableName.valueOf(name.getMethodName());
158    HTableDescriptor htd = new HTableDescriptor(tableName);
159    HColumnDescriptor hcd = new HColumnDescriptor("value");
160    htd.addFamily(hcd);
161
162    admin.createTable(htd, null);
163    try {
164      RegionInfo hri = RegionInfoBuilder.newBuilder(tableName)
165          .setStartKey(Bytes.toBytes("A"))
166          .setEndKey(Bytes.toBytes("Z"))
167          .build();
168      admin.move(hri.getEncodedNameAsBytes(), null);
169      fail("Region should not be moved since it is fake");
170    } catch (IOException ioe) {
171      assertTrue(ioe instanceof UnknownRegionException);
172    } finally {
173      TEST_UTIL.deleteTable(tableName);
174    }
175  }
176
177  @Test
178  public void testMoveThrowsPleaseHoldException() throws IOException {
179    final TableName tableName = TableName.valueOf(name.getMethodName());
180    HMaster master = TEST_UTIL.getMiniHBaseCluster().getMaster();
181    HTableDescriptor htd = new HTableDescriptor(tableName);
182    HColumnDescriptor hcd = new HColumnDescriptor("value");
183    htd.addFamily(hcd);
184
185    admin.createTable(htd, null);
186    try {
187      List<RegionInfo> tableRegions = admin.getRegions(tableName);
188
189      master.setInitialized(false); // fake it, set back later
190      admin.move(tableRegions.get(0).getEncodedNameAsBytes(), null);
191      fail("Region should not be moved since master is not initialized");
192    } catch (IOException ioe) {
193      assertTrue(StringUtils.stringifyException(ioe).contains("PleaseHoldException"));
194    } finally {
195      master.setInitialized(true);
196      TEST_UTIL.deleteTable(tableName);
197    }
198  }
199
200  @Test
201  public void testBlockingHbkc1WithLockFile() throws IOException {
202    // This is how the patch to the lock file is created inside in HBaseFsck. Too hard to use its
203    // actual method without disturbing HBaseFsck... Do the below mimic instead.
204    Path hbckLockPath = new Path(HBaseFsck.getTmpDir(TEST_UTIL.getConfiguration()),
205        HBaseFsck.HBCK_LOCK_FILE);
206    FileSystem fs = TEST_UTIL.getTestFileSystem();
207    assertTrue(fs.exists(hbckLockPath));
208    TEST_UTIL.getMiniHBaseCluster().
209        killMaster(TEST_UTIL.getMiniHBaseCluster().getMaster().getServerName());
210    assertTrue(fs.exists(hbckLockPath));
211    TEST_UTIL.getMiniHBaseCluster().startMaster();
212    TEST_UTIL.waitFor(30000, () -> TEST_UTIL.getMiniHBaseCluster().getMaster() != null &&
213        TEST_UTIL.getMiniHBaseCluster().getMaster().isInitialized());
214    assertTrue(fs.exists(hbckLockPath));
215    // Start a second Master. Should be fine.
216    TEST_UTIL.getMiniHBaseCluster().startMaster();
217    assertTrue(fs.exists(hbckLockPath));
218    fs.delete(hbckLockPath, true);
219    assertFalse(fs.exists(hbckLockPath));
220    // Kill all Masters.
221    TEST_UTIL.getMiniHBaseCluster().getLiveMasterThreads().stream().
222        map(sn -> sn.getMaster().getServerName()).forEach(sn -> {
223          try {
224            TEST_UTIL.getMiniHBaseCluster().killMaster(sn);
225          } catch (IOException e) {
226            e.printStackTrace();
227          }
228        });
229    // Start a new one.
230    TEST_UTIL.getMiniHBaseCluster().startMaster();
231    TEST_UTIL.waitFor(30000, () -> TEST_UTIL.getMiniHBaseCluster().getMaster() != null &&
232        TEST_UTIL.getMiniHBaseCluster().getMaster().isInitialized());
233    // Assert lock gets put in place again.
234    assertTrue(fs.exists(hbckLockPath));
235  }
236}
237