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