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.HBaseTestingUtility;
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 HBaseTestingUtility HBASE_TESTING_UTILITY = new HBaseTestingUtility();
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 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 Size getUsedHeapSize() {
322        return null;
323      }
324
325      @Override
326      public Size getMaxHeapSize() {
327        return null;
328      }
329
330      @Override
331      public int getInfoServerPort() {
332        return 0;
333      }
334
335      @Override
336      public List<ReplicationLoadSource> getReplicationLoadSourceList() {
337        return null;
338      }
339
340      @Override
341      public Map<String, List<ReplicationLoadSource>> getReplicationLoadSourceMap() {
342        return null;
343      }
344
345      @Nullable
346      @Override
347      public ReplicationLoadSink getReplicationLoadSink() {
348        return null;
349      }
350
351      @Override
352      public Map<byte[], RegionMetrics> getRegionMetrics() {
353        Map<byte[], RegionMetrics> regionMetricsMap = new HashMap<>();
354        for (int i = 0; i < noOfRegions; i++) {
355          byte[] regionName = Bytes.toBytes("region" + regionNo + "_" + i);
356          regionMetricsMap.put(regionName,
357            TestRegionsRecoveryChore.getRegionMetrics(regionName, 100 * i));
358          ++regionNo;
359        }
360        return regionMetricsMap;
361      }
362
363      @Override
364      public Map<byte[], UserMetrics> getUserMetrics() {
365        return new HashMap<>();
366      }
367
368      @Override
369      public Set<String> getCoprocessorNames() {
370        return null;
371      }
372
373      @Override
374      public long getReportTimestamp() {
375        return 0;
376      }
377
378      @Override
379      public long getLastReportTimestamp() {
380        return 0;
381      }
382
383      @Override
384      public List<ServerTask> getTasks() {
385        return null;
386      }
387
388      @Override
389      public Map<String, Integer> getRegionCachedInfo() {
390        return new HashMap<>();
391      }
392    };
393    return serverMetrics;
394  }
395
396  private static RegionMetrics getRegionMetrics(byte[] regionName, int compactedStoreRefCount) {
397    RegionMetrics regionMetrics = new RegionMetrics() {
398
399      @Override
400      public byte[] getRegionName() {
401        return regionName;
402      }
403
404      @Override
405      public int getStoreCount() {
406        return 0;
407      }
408
409      @Override
410      public int getStoreFileCount() {
411        return 0;
412      }
413
414      @Override
415      public Size getStoreFileSize() {
416        return null;
417      }
418
419      @Override
420      public Size getMemStoreSize() {
421        return null;
422      }
423
424      @Override
425      public long getReadRequestCount() {
426        return 0;
427      }
428
429      @Override
430      public long getWriteRequestCount() {
431        return 0;
432      }
433
434      @Override
435      public long getFilteredReadRequestCount() {
436        return 0;
437      }
438
439      @Override
440      public Size getStoreFileIndexSize() {
441        return null;
442      }
443
444      @Override
445      public Size getStoreFileRootLevelIndexSize() {
446        return null;
447      }
448
449      @Override
450      public Size getStoreFileUncompressedDataIndexSize() {
451        return null;
452      }
453
454      @Override
455      public Size getBloomFilterSize() {
456        return null;
457      }
458
459      @Override
460      public long getCompactingCellCount() {
461        return 0;
462      }
463
464      @Override
465      public long getCompactedCellCount() {
466        return 0;
467      }
468
469      @Override
470      public long getCompletedSequenceId() {
471        return 0;
472      }
473
474      @Override
475      public Map<byte[], Long> getStoreSequenceId() {
476        return null;
477      }
478
479      @Override
480      public Size getUncompressedStoreFileSize() {
481        return null;
482      }
483
484      @Override
485      public float getDataLocality() {
486        return 0;
487      }
488
489      @Override
490      public long getLastMajorCompactionTimestamp() {
491        return 0;
492      }
493
494      @Override
495      public int getStoreRefCount() {
496        return compactedStoreRefCount;
497      }
498
499      @Override
500      public int getMaxCompactedStoreFileRefCount() {
501        return compactedStoreRefCount;
502      }
503
504      @Override
505      public float getDataLocalityForSsd() {
506        return 0;
507      }
508
509      @Override
510      public long getBlocksLocalWeight() {
511        return 0;
512      }
513
514      @Override
515      public long getBlocksLocalWithSsdWeight() {
516        return 0;
517      }
518
519      @Override
520      public long getBlocksTotalWeight() {
521        return 0;
522      }
523
524      @Override
525      public CompactionState getCompactionState() {
526        return null;
527      }
528
529      @Override
530      public Size getRegionSizeMB() {
531        return null;
532      }
533
534      @Override
535      public float getCurrentRegionCachedRatio() {
536        return 0.0f;
537      }
538    };
539    return regionMetrics;
540  }
541
542  private static RegionInfo getRegionInfo(byte[] regionNameBytes) {
543    RegionInfo regionInfo = new RegionInfo() {
544
545      @Override
546      public String getShortNameToLog() {
547        return null;
548      }
549
550      @Override
551      public long getRegionId() {
552        return 0;
553      }
554
555      @Override
556      public byte[] getRegionName() {
557        return new byte[0];
558      }
559
560      @Override
561      public String getRegionNameAsString() {
562        try {
563          return new String(regionNameBytes, UTF_8_CHARSET);
564        } catch (UnsupportedEncodingException e) {
565          return "";
566        }
567      }
568
569      @Override
570      public String getEncodedName() {
571        return null;
572      }
573
574      @Override
575      public byte[] getEncodedNameAsBytes() {
576        return new byte[0];
577      }
578
579      @Override
580      public byte[] getStartKey() {
581        return new byte[0];
582      }
583
584      @Override
585      public byte[] getEndKey() {
586        return new byte[0];
587      }
588
589      @Override
590      public TableName getTable() {
591        String regionName;
592        try {
593          regionName = new String(regionNameBytes, UTF_8_CHARSET);
594        } catch (UnsupportedEncodingException e) {
595          regionName = "";
596        }
597        int regionNo = Integer.parseInt(regionName.split("_")[1]);
598        TableName tableName = TableName.valueOf("table_" + regionNo % 3);
599        return tableName;
600      }
601
602      @Override
603      public int getReplicaId() {
604        return 0;
605      }
606
607      @Override
608      public boolean isSplit() {
609        return false;
610      }
611
612      @Override
613      public boolean isOffline() {
614        return false;
615      }
616
617      @Override
618      public boolean isSplitParent() {
619        return false;
620      }
621
622      @Override
623      public boolean isMetaRegion() {
624        return false;
625      }
626
627      @Override
628      public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) {
629        return false;
630      }
631
632      @Override
633      public boolean containsRow(byte[] row) {
634        return false;
635      }
636
637    };
638    return regionInfo;
639  }
640
641  /**
642   * Simple helper class that just keeps track of whether or not its stopped.
643   */
644  private static class StoppableImplementation implements Stoppable {
645
646    private volatile boolean stop = false;
647
648    @Override
649    public void stop(String why) {
650      this.stop = true;
651    }
652
653    @Override
654    public boolean isStopped() {
655      return this.stop;
656    }
657
658  }
659
660}