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}