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.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertTrue;
023
024import java.io.IOException;
025import java.util.Arrays;
026import java.util.List;
027
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.HBaseTestingUtility;
030import org.apache.hadoop.hbase.HConstants;
031import org.apache.hadoop.hbase.MetaTableAccessor;
032import org.apache.hadoop.hbase.TableName;
033import org.apache.hadoop.hbase.client.Put;
034import org.apache.hadoop.hbase.client.RegionInfo;
035import org.apache.hadoop.hbase.client.RegionInfoBuilder;
036import org.apache.hadoop.hbase.testclassification.LargeTests;
037import org.apache.hadoop.hbase.testclassification.MasterTests;
038import org.apache.hadoop.hbase.util.Bytes;
039import org.junit.After;
040import org.junit.Before;
041import org.junit.ClassRule;
042import org.junit.Rule;
043import org.junit.Test;
044import org.junit.experimental.categories.Category;
045import org.junit.rules.TestName;
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048
049@Category({MasterTests.class, LargeTests.class})
050public class TestCatalogJanitorCluster {
051  private static final Logger LOG = LoggerFactory.getLogger(TestCatalogJanitorCluster.class);
052  @ClassRule
053  public static final HBaseClassTestRule CLASS_RULE =
054      HBaseClassTestRule.forClass(TestCatalogJanitorCluster.class);
055
056  @Rule
057  public final TestName name = new TestName();
058
059  private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
060  private static final TableName T1 = TableName.valueOf("t1");
061  private static final TableName T2 = TableName.valueOf("t2");
062  private static final TableName T3 = TableName.valueOf("t3");
063
064  @Before
065  public void before() throws Exception {
066    TEST_UTIL.startMiniCluster();
067    TEST_UTIL.createMultiRegionTable(T1, new byte [][] {HConstants.CATALOG_FAMILY});
068    TEST_UTIL.createMultiRegionTable(T2, new byte [][] {HConstants.CATALOG_FAMILY});
069    TEST_UTIL.createMultiRegionTable(T3, new byte [][] {HConstants.CATALOG_FAMILY});
070  }
071
072  @After
073  public void after() throws Exception {
074    TEST_UTIL.shutdownMiniCluster();
075  }
076
077  /**
078   * Fat method where we start with a fat hbase:meta and then gradually intro
079   * problems running catalogjanitor for each to ensure it triggers complaint.
080   * Do one big method because takes a while to build up the context we need.
081   * We create three tables and then make holes, overlaps, add unknown servers
082   * and empty out regioninfo columns. Each should up counts in the
083   * CatalogJanitor.Report produced.
084   */
085  @Test
086  public void testConsistency() throws IOException {
087    CatalogJanitor janitor = TEST_UTIL.getHBaseCluster().getMaster().getCatalogJanitor();
088    int gc = janitor.scan();
089    CatalogJanitor.Report report = janitor.getLastReport();
090    // Assert no problems.
091    assertTrue(report.isEmpty());
092    // Now remove first region in table t2 to see if catalogjanitor scan notices.
093    List<RegionInfo> t2Ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), T2);
094    MetaTableAccessor.deleteRegionInfo(TEST_UTIL.getConnection(), t2Ris.get(0));
095    gc = janitor.scan();
096    report = janitor.getLastReport();
097    assertFalse(report.isEmpty());
098    assertEquals(1, report.getHoles().size());
099    assertTrue(report.getHoles().get(0).getFirst().getTable().equals(T1));
100    assertTrue(report.getHoles().get(0).getFirst().isLast());
101    assertTrue(report.getHoles().get(0).getSecond().getTable().equals(T2));
102    assertEquals(0, report.getOverlaps().size());
103    // Next, add overlaps to first row in t3
104    List<RegionInfo> t3Ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), T3);
105    RegionInfo ri = t3Ris.get(0);
106    RegionInfo newRi1 = RegionInfoBuilder.newBuilder(ri.getTable()).
107        setStartKey(incrementRow(ri.getStartKey())).
108        setEndKey(incrementRow(ri.getEndKey())).build();
109    Put p1 = MetaTableAccessor.makePutFromRegionInfo(newRi1, System.currentTimeMillis());
110    RegionInfo newRi2 = RegionInfoBuilder.newBuilder(newRi1.getTable()).
111        setStartKey(incrementRow(newRi1.getStartKey())).
112        setEndKey(incrementRow(newRi1.getEndKey())).build();
113    Put p2 = MetaTableAccessor.makePutFromRegionInfo(newRi2, System.currentTimeMillis());
114    MetaTableAccessor.putsToMetaTable(TEST_UTIL.getConnection(), Arrays.asList(p1, p2));
115    gc = janitor.scan();
116    report = janitor.getLastReport();
117    assertFalse(report.isEmpty());
118    // We added two overlaps so total three.
119    assertEquals(3, report.getOverlaps().size());
120    // Assert hole is still there.
121    assertEquals(1, report.getHoles().size());
122    // Assert other attributes are empty still.
123    assertTrue(report.getEmptyRegionInfo().isEmpty());
124    assertTrue(report.getUnknownServers().isEmpty());
125    // Now make bad server in t1.
126    List<RegionInfo> t1Ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), T1);
127    RegionInfo t1Ri1 = t1Ris.get(1);
128    Put pServer = new Put(t1Ri1.getRegionName());
129    pServer.addColumn(MetaTableAccessor.getCatalogFamily(),
130        MetaTableAccessor.getServerColumn(0), Bytes.toBytes("bad.server.example.org:1234"));
131    MetaTableAccessor.putsToMetaTable(TEST_UTIL.getConnection(), Arrays.asList(pServer));
132    gc = janitor.scan();
133    report = janitor.getLastReport();
134    assertFalse(report.isEmpty());
135    assertEquals(0, report.getUnknownServers().size());
136    // Test what happens if we blow away an info:server row, if it is null. Should not kill CJ
137    // and we should log the row that had the problem. HBASE-23192. Just make sure we don't
138    // break if this happens.
139    LOG.info("Make null info:server");
140    Put emptyInfoServerPut = new Put(t1Ri1.getRegionName());
141    emptyInfoServerPut.addColumn(MetaTableAccessor.getCatalogFamily(),
142        MetaTableAccessor.getServerColumn(0), Bytes.toBytes(""));
143    MetaTableAccessor.putsToMetaTable(TEST_UTIL.getConnection(), Arrays.asList(emptyInfoServerPut));
144    gc = janitor.scan();
145    report = janitor.getLastReport();
146    assertEquals(0, report.getUnknownServers().size());
147    // Finally, make an empty regioninfo in t1.
148    RegionInfo t1Ri2 = t1Ris.get(2);
149    Put pEmptyRI = new Put(t1Ri2.getRegionName());
150    pEmptyRI.addColumn(MetaTableAccessor.getCatalogFamily(),
151        MetaTableAccessor.getRegionInfoColumn(), HConstants.EMPTY_BYTE_ARRAY);
152    MetaTableAccessor.putsToMetaTable(TEST_UTIL.getConnection(), Arrays.asList(pEmptyRI));
153    gc = janitor.scan();
154    report = janitor.getLastReport();
155    assertEquals(1, report.getEmptyRegionInfo().size());
156  }
157
158  /**
159   * Take last byte and add one to it.
160   */
161  private static byte [] incrementRow(byte [] row) {
162    if (row.length == 0) {
163      return new byte []{'0'};
164    }
165    row[row.length - 1] = (byte)(((int)row[row.length - 1]) + 1);
166    return row;
167  }
168}