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