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 */
018
019package org.apache.hadoop.hbase.master;
020
021import edu.umd.cs.findbugs.annotations.Nullable;
022import java.io.UnsupportedEncodingException;
023import java.nio.charset.StandardCharsets;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028
029import org.apache.hadoop.conf.Configuration;
030import org.apache.hadoop.hbase.ClusterMetrics;
031import org.apache.hadoop.hbase.HBaseClassTestRule;
032import org.apache.hadoop.hbase.HBaseTestingUtility;
033import org.apache.hadoop.hbase.RegionMetrics;
034import org.apache.hadoop.hbase.ServerMetrics;
035import org.apache.hadoop.hbase.ServerName;
036import org.apache.hadoop.hbase.Size;
037import org.apache.hadoop.hbase.Stoppable;
038import org.apache.hadoop.hbase.TableName;
039import org.apache.hadoop.hbase.UserMetrics;
040import org.apache.hadoop.hbase.client.CompactionState;
041import org.apache.hadoop.hbase.client.RegionInfo;
042import org.apache.hadoop.hbase.client.RegionStatesCount;
043import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
044import org.apache.hadoop.hbase.replication.ReplicationLoadSink;
045import org.apache.hadoop.hbase.replication.ReplicationLoadSource;
046import org.apache.hadoop.hbase.testclassification.MasterTests;
047import org.apache.hadoop.hbase.testclassification.SmallTests;
048import org.apache.hadoop.hbase.util.Bytes;
049import org.junit.After;
050import org.junit.Before;
051import org.junit.ClassRule;
052import org.junit.Test;
053import org.junit.experimental.categories.Category;
054import org.mockito.Mockito;
055import org.slf4j.Logger;
056import org.slf4j.LoggerFactory;
057
058/**
059 * Test for RegionsRecoveryChore
060 */
061@Category({MasterTests.class, SmallTests.class})
062public class TestRegionsRecoveryChore {
063
064  @ClassRule
065  public static final HBaseClassTestRule CLASS_RULE =
066    HBaseClassTestRule.forClass(TestRegionsRecoveryChore.class);
067
068  private static final Logger LOG = LoggerFactory.getLogger(TestRegionsRecoveryChore.class);
069
070  private static final HBaseTestingUtility HBASE_TESTING_UTILITY = new HBaseTestingUtility();
071
072  private static final String UTF_8_CHARSET = StandardCharsets.UTF_8.name();
073
074  private HMaster hMaster;
075
076  private AssignmentManager assignmentManager;
077
078  private RegionsRecoveryChore regionsRecoveryChore;
079
080  private static int regionNo;
081  public static final byte[][] REGION_NAME_LIST = new byte[][]{
082    new byte[]{114, 101, 103, 105, 111, 110, 50, 49, 95, 51},
083    new byte[]{114, 101, 103, 105, 111, 110, 50, 53, 95, 51},
084    new byte[]{114, 101, 103, 105, 111, 110, 50, 54, 95, 52},
085    new byte[]{114, 101, 103, 105, 111, 110, 51, 50, 95, 53},
086    new byte[]{114, 101, 103, 105, 111, 110, 51, 49, 95, 52},
087    new byte[]{114, 101, 103, 105, 111, 110, 51, 48, 95, 51},
088    new byte[]{114, 101, 103, 105, 111, 110, 50, 48, 95, 50},
089    new byte[]{114, 101, 103, 105, 111, 110, 50, 52, 95, 50},
090    new byte[]{114, 101, 103, 105, 111, 110, 50, 57, 95, 50},
091    new byte[]{114, 101, 103, 105, 111, 110, 51, 53, 95, 50},
092    new byte[]{114, 101, 103, 105, 111, 110, 49, 48, 56, 95, 49, 49}
093  };
094
095  private Configuration getCustomConf() {
096    Configuration conf = HBASE_TESTING_UTILITY.getConfiguration();
097    conf.setInt("hbase.master.regions.recovery.check.interval", 100);
098    return conf;
099  }
100
101  @Before
102  public void setUp() throws Exception {
103    this.hMaster = Mockito.mock(HMaster.class);
104    this.assignmentManager = Mockito.mock(AssignmentManager.class);
105  }
106
107  @After
108  public void tearDown() throws Exception {
109    Mockito.verifyNoMoreInteractions(this.hMaster);
110    Mockito.verifyNoMoreInteractions(this.assignmentManager);
111  }
112
113  @Test
114  public void testRegionReopensWithStoreRefConfig() throws Exception {
115    regionNo = 0;
116    ClusterMetrics clusterMetrics = TestRegionsRecoveryChore.getClusterMetrics(4);
117    final Map<ServerName, ServerMetrics> serverMetricsMap =
118      clusterMetrics.getLiveServerMetrics();
119    LOG.debug("All Region Names with refCount....");
120    for (ServerMetrics serverMetrics : serverMetricsMap.values()) {
121      Map<byte[], RegionMetrics> regionMetricsMap = serverMetrics.getRegionMetrics();
122      for (RegionMetrics regionMetrics : regionMetricsMap.values()) {
123        LOG.debug("name: " + new String(regionMetrics.getRegionName()) + " refCount: " +
124          regionMetrics.getStoreRefCount());
125      }
126    }
127    Mockito.when(hMaster.getClusterMetrics()).thenReturn(clusterMetrics);
128    Mockito.when(hMaster.getAssignmentManager()).thenReturn(assignmentManager);
129    for (byte[] regionName : REGION_NAME_LIST) {
130      Mockito.when(assignmentManager.getRegionInfo(regionName))
131        .thenReturn(TestRegionsRecoveryChore.getRegionInfo(regionName));
132    }
133    Stoppable stoppable = new StoppableImplementation();
134    Configuration configuration = getCustomConf();
135    configuration.setInt("hbase.regions.recovery.store.file.ref.count", 300);
136    regionsRecoveryChore = new RegionsRecoveryChore(stoppable, configuration, hMaster);
137    regionsRecoveryChore.chore();
138
139    // Verify that we need to reopen regions of 2 tables
140    Mockito.verify(hMaster, Mockito.times(2)).reopenRegions(Mockito.any(), Mockito.anyList(),
141      Mockito.anyLong(), Mockito.anyLong());
142    Mockito.verify(hMaster, Mockito.times(1)).getClusterMetrics();
143
144    // Verify that we need to reopen total 3 regions that have refCount > 300
145    Mockito.verify(hMaster, Mockito.times(3)).getAssignmentManager();
146    Mockito.verify(assignmentManager, Mockito.times(3))
147      .getRegionInfo(Mockito.any());
148  }
149
150  @Test
151  public void testRegionReopensWithLessThreshold() throws Exception {
152    regionNo = 0;
153    ClusterMetrics clusterMetrics = TestRegionsRecoveryChore.getClusterMetrics(4);
154    final Map<ServerName, ServerMetrics> serverMetricsMap =
155      clusterMetrics.getLiveServerMetrics();
156    LOG.debug("All Region Names with refCount....");
157    for (ServerMetrics serverMetrics : serverMetricsMap.values()) {
158      Map<byte[], RegionMetrics> regionMetricsMap = serverMetrics.getRegionMetrics();
159      for (RegionMetrics regionMetrics : regionMetricsMap.values()) {
160        LOG.debug("name: " + new String(regionMetrics.getRegionName()) + " refCount: " +
161          regionMetrics.getStoreRefCount());
162      }
163    }
164    Mockito.when(hMaster.getClusterMetrics()).thenReturn(clusterMetrics);
165    Mockito.when(hMaster.getAssignmentManager()).thenReturn(assignmentManager);
166    for (byte[] regionName : REGION_NAME_LIST) {
167      Mockito.when(assignmentManager.getRegionInfo(regionName))
168        .thenReturn(TestRegionsRecoveryChore.getRegionInfo(regionName));
169    }
170    Stoppable stoppable = new StoppableImplementation();
171    Configuration configuration = getCustomConf();
172    configuration.setInt("hbase.regions.recovery.store.file.ref.count", 400);
173    regionsRecoveryChore = new RegionsRecoveryChore(stoppable, configuration, hMaster);
174    regionsRecoveryChore.chore();
175
176    // Verify that we need to reopen regions of only 1 table
177    Mockito.verify(hMaster, Mockito.times(1)).reopenRegions(Mockito.any(), Mockito.anyList(),
178      Mockito.anyLong(), Mockito.anyLong());
179    Mockito.verify(hMaster, Mockito.times(1)).getClusterMetrics();
180
181    // Verify that we need to reopen only 1 region with refCount > 400
182    Mockito.verify(hMaster, Mockito.times(1)).getAssignmentManager();
183    Mockito.verify(assignmentManager, Mockito.times(1))
184      .getRegionInfo(Mockito.any());
185  }
186
187  @Test
188  public void testRegionReopensWithoutStoreRefConfig() throws Exception {
189    regionNo = 0;
190    ClusterMetrics clusterMetrics = TestRegionsRecoveryChore.getClusterMetrics(10);
191    final Map<ServerName, ServerMetrics> serverMetricsMap =
192      clusterMetrics.getLiveServerMetrics();
193    LOG.debug("All Region Names with refCount....");
194    for (ServerMetrics serverMetrics : serverMetricsMap.values()) {
195      Map<byte[], RegionMetrics> regionMetricsMap = serverMetrics.getRegionMetrics();
196      for (RegionMetrics regionMetrics : regionMetricsMap.values()) {
197        LOG.debug("name: " + new String(regionMetrics.getRegionName()) + " refCount: " +
198          regionMetrics.getStoreRefCount());
199      }
200    }
201    Mockito.when(hMaster.getClusterMetrics()).thenReturn(clusterMetrics);
202    Mockito.when(hMaster.getAssignmentManager()).thenReturn(assignmentManager);
203    for (byte[] regionName : REGION_NAME_LIST) {
204      Mockito.when(assignmentManager.getRegionInfo(regionName))
205        .thenReturn(TestRegionsRecoveryChore.getRegionInfo(regionName));
206    }
207    Stoppable stoppable = new StoppableImplementation();
208    Configuration configuration = getCustomConf();
209    configuration.unset("hbase.regions.recovery.store.file.ref.count");
210    regionsRecoveryChore = new RegionsRecoveryChore(stoppable, configuration, hMaster);
211    regionsRecoveryChore.chore();
212
213    // Verify that by default the feature is turned off so no regions
214    // should be reopened
215    Mockito.verify(hMaster, Mockito.times(0)).reopenRegions(Mockito.any(), Mockito.anyList(),
216      Mockito.anyLong(), Mockito.anyLong());
217
218    // default maxCompactedStoreFileRefCount is -1 (no regions to be reopened using AM)
219    Mockito.verify(hMaster, Mockito.times(0)).getAssignmentManager();
220    Mockito.verify(assignmentManager, Mockito.times(0))
221      .getRegionInfo(Mockito.any());
222  }
223
224  private static ClusterMetrics getClusterMetrics(int noOfLiveServer) {
225    ClusterMetrics clusterMetrics = new ClusterMetrics() {
226
227      @Nullable
228      @Override
229      public String getHBaseVersion() {
230        return null;
231      }
232
233      @Override
234      public List<ServerName> getDeadServerNames() {
235        return null;
236      }
237
238      @Override
239      public Map<ServerName, ServerMetrics> getLiveServerMetrics() {
240        Map<ServerName, ServerMetrics> liveServerMetrics = new HashMap<>();
241        for (int i = 0; i < noOfLiveServer; i++) {
242          ServerName serverName = ServerName.valueOf("rs_" + i, 16010, 12345);
243          liveServerMetrics.put(serverName, TestRegionsRecoveryChore.getServerMetrics(i + 3));
244        }
245        return liveServerMetrics;
246      }
247
248      @Nullable
249      @Override
250      public ServerName getMasterName() {
251        return null;
252      }
253
254      @Override
255      public List<ServerName> getBackupMasterNames() {
256        return null;
257      }
258
259      @Override
260      public List<RegionState> getRegionStatesInTransition() {
261        return null;
262      }
263
264      @Nullable
265      @Override
266      public String getClusterId() {
267        return null;
268      }
269
270      @Override
271      public List<String> getMasterCoprocessorNames() {
272        return null;
273      }
274
275      @Nullable
276      @Override
277      public Boolean getBalancerOn() {
278        return null;
279      }
280
281      @Override
282      public int getMasterInfoPort() {
283        return 0;
284      }
285
286      @Override
287      public List<ServerName> getServersName() {
288        return null;
289      }
290
291      @Override
292      public Map<TableName, RegionStatesCount> getTableRegionStatesCount() {
293        return null;
294      }
295
296    };
297    return clusterMetrics;
298  }
299
300  private static ServerMetrics getServerMetrics(int noOfRegions) {
301    ServerMetrics serverMetrics = new ServerMetrics() {
302
303      @Override
304      public ServerName getServerName() {
305        return null;
306      }
307
308      @Override
309      public long getRequestCountPerSecond() {
310        return 0;
311      }
312
313      @Override
314      public long getRequestCount() {
315        return 0;
316      }
317
318      @Override
319      public Size getUsedHeapSize() {
320        return null;
321      }
322
323      @Override
324      public Size getMaxHeapSize() {
325        return null;
326      }
327
328      @Override
329      public int getInfoServerPort() {
330        return 0;
331      }
332
333      @Override
334      public List<ReplicationLoadSource> getReplicationLoadSourceList() {
335        return null;
336      }
337
338      @Override
339      public Map<String, List<ReplicationLoadSource>> getReplicationLoadSourceMap() {
340        return null;
341      }
342
343      @Nullable
344      @Override
345      public ReplicationLoadSink getReplicationLoadSink() {
346        return null;
347      }
348
349      @Override
350      public Map<byte[], RegionMetrics> getRegionMetrics() {
351        Map<byte[], RegionMetrics> regionMetricsMap = new HashMap<>();
352        for (int i = 0; i < noOfRegions; i++) {
353          byte[] regionName = Bytes.toBytes("region" + regionNo + "_" + i);
354          regionMetricsMap.put(regionName,
355            TestRegionsRecoveryChore.getRegionMetrics(regionName, 100 * i));
356          ++regionNo;
357        }
358        return regionMetricsMap;
359      }
360
361      @Override public Map<byte[], UserMetrics> getUserMetrics() {
362        return new HashMap<>();
363      }
364
365      @Override
366      public Set<String> getCoprocessorNames() {
367        return null;
368      }
369
370      @Override
371      public long getReportTimestamp() {
372        return 0;
373      }
374
375      @Override
376      public long getLastReportTimestamp() {
377        return 0;
378      }
379
380    };
381    return serverMetrics;
382  }
383
384  private static RegionMetrics getRegionMetrics(byte[] regionName, int compactedStoreRefCount) {
385    RegionMetrics regionMetrics = new RegionMetrics() {
386
387      @Override
388      public byte[] getRegionName() {
389        return regionName;
390      }
391
392      @Override
393      public int getStoreCount() {
394        return 0;
395      }
396
397      @Override
398      public int getStoreFileCount() {
399        return 0;
400      }
401
402      @Override
403      public Size getStoreFileSize() {
404        return null;
405      }
406
407      @Override
408      public Size getMemStoreSize() {
409        return null;
410      }
411
412      @Override
413      public long getReadRequestCount() {
414        return 0;
415      }
416
417      @Override
418      public long getWriteRequestCount() {
419        return 0;
420      }
421
422      @Override
423      public long getFilteredReadRequestCount() {
424        return 0;
425      }
426
427      @Override
428      public Size getStoreFileIndexSize() {
429        return null;
430      }
431
432      @Override
433      public Size getStoreFileRootLevelIndexSize() {
434        return null;
435      }
436
437      @Override
438      public Size getStoreFileUncompressedDataIndexSize() {
439        return null;
440      }
441
442      @Override
443      public Size getBloomFilterSize() {
444        return null;
445      }
446
447      @Override
448      public long getCompactingCellCount() {
449        return 0;
450      }
451
452      @Override
453      public long getCompactedCellCount() {
454        return 0;
455      }
456
457      @Override
458      public long getCompletedSequenceId() {
459        return 0;
460      }
461
462      @Override
463      public Map<byte[], Long> getStoreSequenceId() {
464        return null;
465      }
466
467      @Override
468      public Size getUncompressedStoreFileSize() {
469        return null;
470      }
471
472      @Override
473      public float getDataLocality() {
474        return 0;
475      }
476
477      @Override
478      public long getLastMajorCompactionTimestamp() {
479        return 0;
480      }
481
482      @Override
483      public int getStoreRefCount() {
484        return compactedStoreRefCount;
485      }
486
487      @Override
488      public int getMaxCompactedStoreFileRefCount() {
489        return compactedStoreRefCount;
490      }
491
492      @Override
493      public float getDataLocalityForSsd() {
494        return 0;
495      }
496
497      @Override
498      public long getBlocksLocalWeight() {
499        return 0;
500      }
501
502      @Override
503      public long getBlocksLocalWithSsdWeight() {
504        return 0;
505      }
506
507      @Override
508      public long getBlocksTotalWeight() {
509        return 0;
510      }
511
512      @Override
513      public CompactionState getCompactionState() {
514        return null;
515      }
516    };
517    return regionMetrics;
518  }
519
520  private static RegionInfo getRegionInfo(byte[] regionNameBytes) {
521    RegionInfo regionInfo = new RegionInfo() {
522
523      @Override
524      public String getShortNameToLog() {
525        return null;
526      }
527
528      @Override
529      public long getRegionId() {
530        return 0;
531      }
532
533      @Override
534      public byte[] getRegionName() {
535        return new byte[0];
536      }
537
538      @Override
539      public String getRegionNameAsString() {
540        try {
541          return new String(regionNameBytes, UTF_8_CHARSET);
542        } catch (UnsupportedEncodingException e) {
543          return "";
544        }
545      }
546
547      @Override
548      public String getEncodedName() {
549        return null;
550      }
551
552      @Override
553      public byte[] getEncodedNameAsBytes() {
554        return new byte[0];
555      }
556
557      @Override
558      public byte[] getStartKey() {
559        return new byte[0];
560      }
561
562      @Override
563      public byte[] getEndKey() {
564        return new byte[0];
565      }
566
567      @Override
568      public TableName getTable() {
569        String regionName;
570        try {
571          regionName = new String(regionNameBytes, UTF_8_CHARSET);
572        } catch (UnsupportedEncodingException e) {
573          regionName = "";
574        }
575        int regionNo = Integer.parseInt(regionName.split("_")[1]);
576        TableName tableName = TableName.valueOf("table_" + regionNo % 3);
577        return tableName;
578      }
579
580      @Override
581      public int getReplicaId() {
582        return 0;
583      }
584
585      @Override
586      public boolean isSplit() {
587        return false;
588      }
589
590      @Override
591      public boolean isOffline() {
592        return false;
593      }
594
595      @Override
596      public boolean isSplitParent() {
597        return false;
598      }
599
600      @Override
601      public boolean isMetaRegion() {
602        return false;
603      }
604
605      @Override
606      public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) {
607        return false;
608      }
609
610      @Override
611      public boolean containsRow(byte[] row) {
612        return false;
613      }
614
615    };
616    return regionInfo;
617  }
618
619  /**
620   * Simple helper class that just keeps track of whether or not its stopped.
621   */
622  private static class StoppableImplementation implements Stoppable {
623
624    private volatile boolean stop = false;
625
626    @Override
627    public void stop(String why) {
628      this.stop = true;
629    }
630
631    @Override
632    public boolean isStopped() {
633      return this.stop;
634    }
635
636  }
637
638}