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