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.janitor;
019
020import static org.junit.Assert.assertFalse;
021import static org.junit.Assert.assertNotNull;
022import static org.junit.Assert.assertTrue;
023
024import java.io.IOException;
025import java.util.ArrayList;
026import java.util.List;
027import java.util.concurrent.ExecutionException;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.HBaseTestingUtility;
030import org.apache.hadoop.hbase.HRegionLocation;
031import org.apache.hadoop.hbase.MetaMockingUtil;
032import org.apache.hadoop.hbase.MetaTableAccessor;
033import org.apache.hadoop.hbase.TableName;
034import org.apache.hadoop.hbase.TableNameTestRule;
035import org.apache.hadoop.hbase.client.Admin;
036import org.apache.hadoop.hbase.client.Connection;
037import org.apache.hadoop.hbase.client.ConnectionFactory;
038import org.apache.hadoop.hbase.client.Get;
039import org.apache.hadoop.hbase.client.RegionInfo;
040import org.apache.hadoop.hbase.client.RegionLocator;
041import org.apache.hadoop.hbase.client.Result;
042import org.apache.hadoop.hbase.client.Table;
043import org.apache.hadoop.hbase.master.HMaster;
044import org.apache.hadoop.hbase.master.ServerManager;
045import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
046import org.apache.hadoop.hbase.testclassification.MasterTests;
047import org.apache.hadoop.hbase.testclassification.MediumTests;
048import org.apache.hadoop.hbase.util.Bytes;
049import org.apache.hadoop.hbase.util.PairOfSameType;
050import org.apache.hadoop.hbase.util.Threads;
051import org.junit.AfterClass;
052import org.junit.BeforeClass;
053import org.junit.ClassRule;
054import org.junit.Rule;
055import org.junit.Test;
056import org.junit.experimental.categories.Category;
057import org.slf4j.Logger;
058import org.slf4j.LoggerFactory;
059
060@Category({ MasterTests.class, MediumTests.class })
061public class TestCatalogJanitorInMemoryStates {
062
063  @ClassRule
064  public static final HBaseClassTestRule CLASS_RULE =
065    HBaseClassTestRule.forClass(TestCatalogJanitorInMemoryStates.class);
066
067  private static final Logger LOG = LoggerFactory.getLogger(TestCatalogJanitorInMemoryStates.class);
068
069  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
070
071  private static byte[] FAMILY = Bytes.toBytes("testFamily");
072
073  @Rule
074  public final TableNameTestRule name = new TableNameTestRule();
075
076  @BeforeClass
077  public static void setUpBeforeClass() throws Exception {
078    TEST_UTIL.startMiniCluster(1);
079  }
080
081  @AfterClass
082  public static void tearDownAfterClass() throws Exception {
083    TEST_UTIL.shutdownMiniCluster();
084  }
085
086  /**
087   * Test clearing a split parent from memory.
088   */
089  @Test
090  public void testInMemoryParentCleanup()
091    throws IOException, InterruptedException, ExecutionException {
092    HMaster master = TEST_UTIL.getHBaseCluster().getMaster();
093    final AssignmentManager am = master.getAssignmentManager();
094    final ServerManager sm = master.getServerManager();
095
096    Admin admin = TEST_UTIL.getAdmin();
097    admin.enableCatalogJanitor(false);
098
099    final TableName tableName = name.getTableName();
100    Table t = TEST_UTIL.createTable(tableName, FAMILY);
101    TEST_UTIL.loadTable(t, FAMILY, false);
102
103    RegionLocator locator = TEST_UTIL.getConnection().getRegionLocator(tableName);
104    List<HRegionLocation> allRegionLocations = locator.getAllRegionLocations();
105
106    // We need to create a valid split with daughter regions
107    HRegionLocation parent = allRegionLocations.get(0);
108    List<HRegionLocation> daughters = splitRegion(parent.getRegionInfo());
109    LOG.info("Parent region: " + parent);
110    LOG.info("Daughter regions: " + daughters);
111    assertNotNull("Should have found daughter regions for " + parent, daughters);
112
113    assertTrue("Parent region should exist in RegionStates",
114      am.getRegionStates().isRegionInRegionStates(parent.getRegion()));
115    assertTrue("Parent region should exist in ServerManager",
116      sm.isRegionInServerManagerStates(parent.getRegion()));
117
118    // clean the parent
119    Result r = MetaMockingUtil.getMetaTableRowResult(parent.getRegion(), null,
120      daughters.get(0).getRegion(), daughters.get(1).getRegion());
121    CatalogJanitor.cleanParent(master, parent.getRegion(), r);
122    assertFalse("Parent region should have been removed from RegionStates",
123      am.getRegionStates().isRegionInRegionStates(parent.getRegion()));
124    assertFalse("Parent region should have been removed from ServerManager",
125      sm.isRegionInServerManagerStates(parent.getRegion()));
126
127  }
128
129  /**
130   * Splits a region
131   * @param r Region to split.
132   * @return List of region locations
133   */
134  private List<HRegionLocation> splitRegion(final RegionInfo r)
135    throws IOException, InterruptedException, ExecutionException {
136    List<HRegionLocation> locations = new ArrayList<>();
137    // Split this table in two.
138    Admin admin = TEST_UTIL.getAdmin();
139    Connection connection = TEST_UTIL.getConnection();
140    admin.splitRegionAsync(r.getEncodedNameAsBytes()).get();
141    admin.close();
142    PairOfSameType<RegionInfo> regions = waitOnDaughters(r);
143    if (regions != null) {
144      try (RegionLocator rl = connection.getRegionLocator(r.getTable())) {
145        locations.add(rl.getRegionLocation(regions.getFirst().getEncodedNameAsBytes()));
146        locations.add(rl.getRegionLocation(regions.getSecond().getEncodedNameAsBytes()));
147      }
148      return locations;
149    }
150    return locations;
151  }
152
153  /*
154   * Wait on region split. May return because we waited long enough on the split and it didn't
155   * happen. Caller should check.
156   * @param r
157   * @return Daughter regions; caller needs to check table actually split.
158   */
159  private PairOfSameType<RegionInfo> waitOnDaughters(final RegionInfo r) throws IOException {
160    long start = System.currentTimeMillis();
161    PairOfSameType<RegionInfo> pair = null;
162    try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
163      Table metaTable = conn.getTable(TableName.META_TABLE_NAME)) {
164      Result result = null;
165      RegionInfo region = null;
166      while ((System.currentTimeMillis() - start) < 60000) {
167        result = metaTable.get(new Get(r.getRegionName()));
168        if (result == null) {
169          break;
170        }
171        region = MetaTableAccessor.getRegionInfo(result);
172        if (region.isSplitParent()) {
173          LOG.debug(region.toString() + " IS a parent!");
174          pair = MetaTableAccessor.getDaughterRegions(result);
175          break;
176        }
177        Threads.sleep(100);
178      }
179
180      if (pair.getFirst() == null || pair.getSecond() == null) {
181        throw new IOException("Failed to get daughters, for parent region: " + r);
182      }
183      return pair;
184    }
185  }
186}