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.assignment;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertFalse;
022import static org.junit.Assert.assertNotEquals;
023import static org.junit.Assert.assertNotNull;
024import static org.junit.Assert.assertNull;
025import static org.junit.Assert.assertTrue;
026
027import java.time.Instant;
028import java.util.Collections;
029import java.util.List;
030import java.util.Map;
031import java.util.Optional;
032import java.util.concurrent.Future;
033import org.apache.hadoop.conf.Configuration;
034import org.apache.hadoop.hbase.HBaseClassTestRule;
035import org.apache.hadoop.hbase.ServerName;
036import org.apache.hadoop.hbase.TableName;
037import org.apache.hadoop.hbase.client.RegionInfo;
038import org.apache.hadoop.hbase.client.RegionInfoBuilder;
039import org.apache.hadoop.hbase.client.TableState;
040import org.apache.hadoop.hbase.master.TableStateManager;
041import org.apache.hadoop.hbase.master.hbck.HbckChore;
042import org.apache.hadoop.hbase.master.hbck.HbckReport;
043import org.apache.hadoop.hbase.regionserver.HRegion;
044import org.apache.hadoop.hbase.testclassification.MasterTests;
045import org.apache.hadoop.hbase.testclassification.MediumTests;
046import org.apache.hadoop.hbase.util.Bytes;
047import org.apache.hadoop.hbase.util.CommonFSUtils;
048import org.apache.hadoop.hbase.util.FSUtils;
049import org.apache.hadoop.hbase.util.Pair;
050import org.junit.Before;
051import org.junit.ClassRule;
052import org.junit.Test;
053import org.junit.experimental.categories.Category;
054import org.mockito.Mockito;
055
056@Category({ MasterTests.class, MediumTests.class })
057public class TestHbckChore extends TestAssignmentManagerBase {
058
059  @ClassRule
060  public static final HBaseClassTestRule CLASS_RULE =
061    HBaseClassTestRule.forClass(TestHbckChore.class);
062
063  private HbckChore hbckChore;
064
065  @Before
066  public void setUp() throws Exception {
067    super.setUp();
068    hbckChore = new HbckChore(master);
069  }
070
071  @Test
072  public void testForMeta() {
073    byte[] metaRegionNameAsBytes = RegionInfoBuilder.FIRST_META_REGIONINFO.getRegionName();
074    String metaRegionName = RegionInfoBuilder.FIRST_META_REGIONINFO.getRegionNameAsString();
075    List<ServerName> serverNames = master.getServerManager().getOnlineServersList();
076    assertEquals(NSERVERS, serverNames.size());
077
078    hbckChore.choreForTesting();
079    Map<String, Pair<ServerName, List<ServerName>>> inconsistentRegions =
080      hbckChore.getLastReport().getInconsistentRegions();
081
082    // Test for case1: Master thought this region opened, but no regionserver reported it.
083    assertTrue(inconsistentRegions.containsKey(metaRegionName));
084    Pair<ServerName, List<ServerName>> pair = inconsistentRegions.get(metaRegionName);
085    ServerName locationInMeta = pair.getFirst();
086    List<ServerName> reportedRegionServers = pair.getSecond();
087    assertTrue(serverNames.contains(locationInMeta));
088    assertEquals(0, reportedRegionServers.size());
089
090    // Reported right region location. Then not in problematic regions.
091    am.reportOnlineRegions(locationInMeta, Collections.singleton(metaRegionNameAsBytes));
092    hbckChore.choreForTesting();
093    inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions();
094    assertFalse(inconsistentRegions.containsKey(metaRegionName));
095  }
096
097  @Test
098  public void testForUserTable() throws Exception {
099    TableName tableName = TableName.valueOf("testForUserTable");
100    RegionInfo hri = createRegionInfo(tableName, 1);
101    String regionName = hri.getRegionNameAsString();
102    rsDispatcher.setMockRsExecutor(new GoodRsExecutor());
103    Future<byte[]> future = submitProcedure(createAssignProcedure(hri));
104    waitOnFuture(future);
105
106    List<ServerName> serverNames = master.getServerManager().getOnlineServersList();
107    assertEquals(NSERVERS, serverNames.size());
108
109    // Test for case1: Master thought this region opened, but no regionserver reported it.
110    hbckChore.choreForTesting();
111    Map<String, Pair<ServerName, List<ServerName>>> inconsistentRegions =
112      hbckChore.getLastReport().getInconsistentRegions();
113    assertTrue(inconsistentRegions.containsKey(regionName));
114    Pair<ServerName, List<ServerName>> pair = inconsistentRegions.get(regionName);
115    ServerName locationInMeta = pair.getFirst();
116    List<ServerName> reportedRegionServers = pair.getSecond();
117    assertTrue(serverNames.contains(locationInMeta));
118    assertEquals(0, reportedRegionServers.size());
119
120    // Test for case2: Master thought this region opened on Server1, but regionserver reported
121    // Server2
122    final ServerName tempLocationInMeta = locationInMeta;
123    final ServerName anotherServer =
124      serverNames.stream().filter(s -> !s.equals(tempLocationInMeta)).findFirst().get();
125    am.reportOnlineRegions(anotherServer, Collections.singleton(hri.getRegionName()));
126    hbckChore.choreForTesting();
127    inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions();
128    assertTrue(inconsistentRegions.containsKey(regionName));
129    pair = inconsistentRegions.get(regionName);
130    locationInMeta = pair.getFirst();
131    reportedRegionServers = pair.getSecond();
132    assertEquals(1, reportedRegionServers.size());
133    assertFalse(reportedRegionServers.contains(locationInMeta));
134    assertTrue(reportedRegionServers.contains(anotherServer));
135
136    // Test for case3: More than one regionservers reported opened this region.
137    am.reportOnlineRegions(locationInMeta, Collections.singleton(hri.getRegionName()));
138    hbckChore.choreForTesting();
139    inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions();
140    assertTrue(inconsistentRegions.containsKey(regionName));
141    pair = inconsistentRegions.get(regionName);
142    locationInMeta = pair.getFirst();
143    reportedRegionServers = pair.getSecond();
144    assertEquals(2, reportedRegionServers.size());
145    assertTrue(reportedRegionServers.contains(locationInMeta));
146    assertTrue(reportedRegionServers.contains(anotherServer));
147
148    // Reported right region location, then not in inconsistent regions.
149    am.reportOnlineRegions(anotherServer, Collections.emptySet());
150    hbckChore.choreForTesting();
151    inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions();
152    assertFalse(inconsistentRegions.containsKey(regionName));
153
154    // Test for case4: No region location for a previously reported region. Probably due to
155    // TRSP bug or bypass.
156    am.offlineRegion(hri);
157    hbckChore.choreForTesting();
158    inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions();
159    assertTrue(inconsistentRegions.containsKey(regionName));
160  }
161
162  @Test
163  public void testForDisabledTable() throws Exception {
164    TableName tableName = TableName.valueOf("testForDisabledTable");
165    RegionInfo hri = createRegionInfo(tableName, 1);
166    String regionName = hri.getRegionNameAsString();
167    rsDispatcher.setMockRsExecutor(new GoodRsExecutor());
168    Future<byte[]> future = submitProcedure(createAssignProcedure(hri));
169    waitOnFuture(future);
170
171    List<ServerName> serverNames = master.getServerManager().getOnlineServersList();
172    assertEquals(NSERVERS, serverNames.size());
173
174    hbckChore.choreForTesting();
175    Map<String, Pair<ServerName, List<ServerName>>> inconsistentRegions =
176      hbckChore.getLastReport().getInconsistentRegions();
177    assertTrue(inconsistentRegions.containsKey(regionName));
178    Pair<ServerName, List<ServerName>> pair = inconsistentRegions.get(regionName);
179    ServerName locationInMeta = pair.getFirst();
180    List<ServerName> reportedRegionServers = pair.getSecond();
181    assertTrue(serverNames.contains(locationInMeta));
182    assertEquals(0, reportedRegionServers.size());
183
184    // Set table state to disabled, then not in inconsistent regions.
185    TableStateManager tableStateManager = master.getTableStateManager();
186    Mockito.when(tableStateManager.isTableState(tableName, TableState.State.DISABLED))
187      .thenReturn(true);
188    hbckChore.choreForTesting();
189    inconsistentRegions = hbckChore.getLastReport().getInconsistentRegions();
190    assertFalse(inconsistentRegions.containsKey(regionName));
191  }
192
193  @Test
194  public void testForSplitParent() throws Exception {
195    TableName tableName = TableName.valueOf("testForSplitParent");
196    RegionInfo hri = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes(0))
197      .setEndKey(Bytes.toBytes(1)).setSplit(true).setOffline(true).setRegionId(0).build();
198    String regionName = hri.getEncodedName();
199    rsDispatcher.setMockRsExecutor(new GoodRsExecutor());
200    Future<byte[]> future = submitProcedure(createAssignProcedure(hri));
201    waitOnFuture(future);
202
203    List<ServerName> serverNames = master.getServerManager().getOnlineServersList();
204    assertEquals(NSERVERS, serverNames.size());
205
206    hbckChore.choreForTesting();
207    Map<String, Pair<ServerName, List<ServerName>>> inconsistentRegions =
208      hbckChore.getLastReport().getInconsistentRegions();
209    assertFalse(inconsistentRegions.containsKey(regionName));
210  }
211
212  @Test
213  public void testOrphanRegionsOnFS() throws Exception {
214    TableName tableName = TableName.valueOf("testOrphanRegionsOnFS");
215    RegionInfo regionInfo = RegionInfoBuilder.newBuilder(tableName).build();
216    Configuration conf = util.getConfiguration();
217
218    hbckChore.choreForTesting();
219    assertEquals(0, hbckChore.getLastReport().getOrphanRegionsOnFS().size());
220
221    HRegion.createRegionDir(conf, regionInfo, CommonFSUtils.getRootDir(conf));
222    hbckChore.choreForTesting();
223    assertEquals(1, hbckChore.getLastReport().getOrphanRegionsOnFS().size());
224    assertTrue(
225      hbckChore.getLastReport().getOrphanRegionsOnFS().containsKey(regionInfo.getEncodedName()));
226
227    FSUtils.deleteRegionDir(conf, regionInfo);
228    hbckChore.choreForTesting();
229    assertEquals(0, hbckChore.getLastReport().getOrphanRegionsOnFS().size());
230  }
231
232  @Test
233  public void testChoreDisable() {
234    // The way to disable to chore is to set hbase.master.hbck.chore.interval <= 0
235    // When the interval is > 0, the chore should run.
236    Instant lastRunTime = Optional.ofNullable(hbckChore.getLastReport())
237      .map(HbckReport::getCheckingEndTimestamp).orElse(null);
238    hbckChore.choreForTesting();
239    Instant thisRunTime = Optional.ofNullable(hbckChore.getLastReport())
240      .map(HbckReport::getCheckingEndTimestamp).orElse(null);
241    assertNotNull(thisRunTime);
242    assertNotEquals(lastRunTime, thisRunTime);
243
244    // When the interval <= 0, the chore shouldn't run
245    master.getConfiguration().setInt("hbase.master.hbck.chore.interval", 0);
246    HbckChore hbckChoreWithChangedConf = new HbckChore(master);
247    hbckChoreWithChangedConf.choreForTesting();
248    assertNull(hbckChoreWithChangedConf.getLastReport());
249  }
250}