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