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  private static final TableName T4 = TableName.valueOf("t4");
064  private static final TableName T5 = TableName.valueOf("t5");
065
066  @Before
067  public void before() throws Exception {
068    TEST_UTIL.startMiniCluster();
069    TEST_UTIL.createMultiRegionTable(T1, new byte [][] {HConstants.CATALOG_FAMILY});
070    TEST_UTIL.createMultiRegionTable(T2, new byte [][] {HConstants.CATALOG_FAMILY});
071    TEST_UTIL.createMultiRegionTable(T3, new byte [][] {HConstants.CATALOG_FAMILY});
072
073    final byte[][] keysForT4 = {
074      Bytes.toBytes("aa"),
075      Bytes.toBytes("bb"),
076      Bytes.toBytes("cc"),
077      Bytes.toBytes("dd")
078    };
079
080    TEST_UTIL.createTable(T4, HConstants.CATALOG_FAMILY, keysForT4);
081
082    final byte[][] keysForT5 = {
083      Bytes.toBytes("bb"),
084      Bytes.toBytes("cc"),
085      Bytes.toBytes("dd")
086    };
087
088    TEST_UTIL.createTable(T5, HConstants.CATALOG_FAMILY, keysForT5);
089  }
090
091  @After
092  public void after() throws Exception {
093    TEST_UTIL.shutdownMiniCluster();
094  }
095
096  /**
097   * Fat method where we start with a fat hbase:meta and then gradually intro
098   * problems running catalogjanitor for each to ensure it triggers complaint.
099   * Do one big method because takes a while to build up the context we need.
100   * We create three tables and then make holes, overlaps, add unknown servers
101   * and empty out regioninfo columns. Each should up counts in the
102   * CatalogJanitor.Report produced.
103   */
104  @Test
105  public void testConsistency() throws IOException {
106    CatalogJanitor janitor = TEST_UTIL.getHBaseCluster().getMaster().getCatalogJanitor();
107    int gc = janitor.scan();
108    CatalogJanitor.Report report = janitor.getLastReport();
109    // Assert no problems.
110    assertTrue(report.isEmpty());
111    // Now remove first region in table t2 to see if catalogjanitor scan notices.
112    List<RegionInfo> t2Ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), T2);
113    MetaTableAccessor.deleteRegionInfo(TEST_UTIL.getConnection(), t2Ris.get(0));
114    gc = janitor.scan();
115    report = janitor.getLastReport();
116    assertFalse(report.isEmpty());
117    assertEquals(1, report.getHoles().size());
118    assertTrue(report.getHoles().get(0).getFirst().getTable().equals(T1));
119    assertTrue(report.getHoles().get(0).getFirst().isLast());
120    assertTrue(report.getHoles().get(0).getSecond().getTable().equals(T2));
121    assertEquals(0, report.getOverlaps().size());
122    // Next, add overlaps to first row in t3
123    List<RegionInfo> t3Ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), T3);
124    RegionInfo ri = t3Ris.get(0);
125    RegionInfo newRi1 = RegionInfoBuilder.newBuilder(ri.getTable()).
126        setStartKey(incrementRow(ri.getStartKey())).
127        setEndKey(incrementRow(ri.getEndKey())).build();
128    Put p1 = MetaTableAccessor.makePutFromRegionInfo(newRi1, System.currentTimeMillis());
129    RegionInfo newRi2 = RegionInfoBuilder.newBuilder(newRi1.getTable()).
130        setStartKey(incrementRow(newRi1.getStartKey())).
131        setEndKey(incrementRow(newRi1.getEndKey())).build();
132    Put p2 = MetaTableAccessor.makePutFromRegionInfo(newRi2, System.currentTimeMillis());
133    MetaTableAccessor.putsToMetaTable(TEST_UTIL.getConnection(), Arrays.asList(p1, p2));
134    gc = janitor.scan();
135    report = janitor.getLastReport();
136    assertFalse(report.isEmpty());
137    // We added two overlaps so total three.
138    assertEquals(3, report.getOverlaps().size());
139    // Assert hole is still there.
140    assertEquals(1, report.getHoles().size());
141    // Assert other attributes are empty still.
142    assertTrue(report.getEmptyRegionInfo().isEmpty());
143    assertTrue(report.getUnknownServers().isEmpty());
144    // Now make bad server in t1.
145    List<RegionInfo> t1Ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), T1);
146    RegionInfo t1Ri1 = t1Ris.get(1);
147    Put pServer = new Put(t1Ri1.getRegionName());
148    pServer.addColumn(MetaTableAccessor.getCatalogFamily(),
149        MetaTableAccessor.getServerColumn(0), Bytes.toBytes("bad.server.example.org:1234"));
150    MetaTableAccessor.putsToMetaTable(TEST_UTIL.getConnection(), Arrays.asList(pServer));
151    gc = janitor.scan();
152    report = janitor.getLastReport();
153    assertFalse(report.isEmpty());
154    assertEquals(1, report.getUnknownServers().size());
155    // Test what happens if we blow away an info:server row, if it is null. Should not kill CJ
156    // and we should log the row that had the problem. HBASE-23192. Just make sure we don't
157    // break if this happens.
158    LOG.info("Make null info:server");
159    Put emptyInfoServerPut = new Put(t1Ri1.getRegionName());
160    emptyInfoServerPut.addColumn(MetaTableAccessor.getCatalogFamily(),
161        MetaTableAccessor.getServerColumn(0), Bytes.toBytes(""));
162    MetaTableAccessor.putsToMetaTable(TEST_UTIL.getConnection(), Arrays.asList(emptyInfoServerPut));
163    janitor.scan();
164    report = janitor.getLastReport();
165    assertEquals(0, report.getUnknownServers().size());
166    // Mke an empty regioninfo in t1.
167    RegionInfo t1Ri2 = t1Ris.get(2);
168    Put pEmptyRI = new Put(t1Ri2.getRegionName());
169    pEmptyRI.addColumn(MetaTableAccessor.getCatalogFamily(),
170        MetaTableAccessor.getRegionInfoColumn(), HConstants.EMPTY_BYTE_ARRAY);
171    MetaTableAccessor.putsToMetaTable(TEST_UTIL.getConnection(), Arrays.asList(pEmptyRI));
172    janitor.scan();
173    report = janitor.getLastReport();
174    assertEquals(1, report.getEmptyRegionInfo().size());
175
176    int holesReported = report.getHoles().size();
177    int overlapsReported = report.getOverlaps().size();
178
179    // Test the case for T4
180    //    r1: [aa, bb), r2: [cc, dd), r3: [a, cc)
181    // Make sure only overlaps and no holes are reported.
182    List<RegionInfo> t4Ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), T4);
183    // delete the region [bb, cc)
184    MetaTableAccessor.deleteRegionInfo(TEST_UTIL.getConnection(), t4Ris.get(2));
185
186    // add a new region [a, cc)
187    RegionInfo newRiT4 = RegionInfoBuilder.newBuilder(T4).
188      setStartKey("a".getBytes()).
189      setEndKey("cc".getBytes()).build();
190    Put putForT4 = MetaTableAccessor.makePutFromRegionInfo(newRiT4, System.currentTimeMillis());
191    MetaTableAccessor.putsToMetaTable(TEST_UTIL.getConnection(), Arrays.asList(putForT4));
192
193    janitor.scan();
194    report = janitor.getLastReport();
195    // there is no new hole reported, 2 more overLaps added.
196    assertEquals(holesReported, report.getHoles().size());
197    assertEquals(overlapsReported + 2, report.getOverlaps().size());
198
199    holesReported = report.getHoles().size();
200    overlapsReported = report.getOverlaps().size();
201
202    // Test the case for T5
203    //    r0: [, bb), r1: [a, g), r2: [bb, cc), r3: [dd, )
204    // Make sure only overlaps and no holes are reported.
205    List<RegionInfo> t5Ris = MetaTableAccessor.getTableRegions(TEST_UTIL.getConnection(), T5);
206    // delete the region [cc, dd)
207    MetaTableAccessor.deleteRegionInfo(TEST_UTIL.getConnection(), t5Ris.get(2));
208
209    // add a new region [a, g)
210    RegionInfo newRiT5 = RegionInfoBuilder.newBuilder(T5).
211      setStartKey("a".getBytes()).
212      setEndKey("g".getBytes()).build();
213    Put putForT5 = MetaTableAccessor.makePutFromRegionInfo(newRiT5, System.currentTimeMillis());
214    MetaTableAccessor.putsToMetaTable(TEST_UTIL.getConnection(), Arrays.asList(putForT5));
215
216    janitor.scan();
217    report = janitor.getLastReport();
218    // there is no new hole reported, 3 more overLaps added.
219    // ([a, g), [, bb)), ([a, g), [bb, cc)), ([a, g), [dd, ))
220    assertEquals(holesReported, report.getHoles().size());
221    assertEquals(overlapsReported + 3, report.getOverlaps().size());
222  }
223
224  /**
225   * Take last byte and add one to it.
226   */
227  private static byte [] incrementRow(byte [] row) {
228    if (row.length == 0) {
229      return new byte []{'0'};
230    }
231    row[row.length - 1] = (byte)(((int)row[row.length - 1]) + 1);
232    return row;
233  }
234}