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