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());
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());
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());
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    };
389    return serverMetrics;
390  }
391
392  private static RegionMetrics getRegionMetrics(byte[] regionName, int compactedStoreRefCount) {
393    RegionMetrics regionMetrics = new RegionMetrics() {
394
395      @Override
396      public byte[] getRegionName() {
397        return regionName;
398      }
399
400      @Override
401      public int getStoreCount() {
402        return 0;
403      }
404
405      @Override
406      public int getStoreFileCount() {
407        return 0;
408      }
409
410      @Override
411      public Size getStoreFileSize() {
412        return null;
413      }
414
415      @Override
416      public Size getMemStoreSize() {
417        return null;
418      }
419
420      @Override
421      public long getReadRequestCount() {
422        return 0;
423      }
424
425      @Override
426      public long getWriteRequestCount() {
427        return 0;
428      }
429
430      @Override
431      public long getFilteredReadRequestCount() {
432        return 0;
433      }
434
435      @Override
436      public Size getStoreFileIndexSize() {
437        return null;
438      }
439
440      @Override
441      public Size getStoreFileRootLevelIndexSize() {
442        return null;
443      }
444
445      @Override
446      public Size getStoreFileUncompressedDataIndexSize() {
447        return null;
448      }
449
450      @Override
451      public Size getBloomFilterSize() {
452        return null;
453      }
454
455      @Override
456      public long getCompactingCellCount() {
457        return 0;
458      }
459
460      @Override
461      public long getCompactedCellCount() {
462        return 0;
463      }
464
465      @Override
466      public long getCompletedSequenceId() {
467        return 0;
468      }
469
470      @Override
471      public Map<byte[], Long> getStoreSequenceId() {
472        return null;
473      }
474
475      @Override
476      public Size getUncompressedStoreFileSize() {
477        return null;
478      }
479
480      @Override
481      public float getDataLocality() {
482        return 0;
483      }
484
485      @Override
486      public long getLastMajorCompactionTimestamp() {
487        return 0;
488      }
489
490      @Override
491      public int getStoreRefCount() {
492        return compactedStoreRefCount;
493      }
494
495      @Override
496      public int getMaxCompactedStoreFileRefCount() {
497        return compactedStoreRefCount;
498      }
499
500      @Override
501      public float getDataLocalityForSsd() {
502        return 0;
503      }
504
505      @Override
506      public long getBlocksLocalWeight() {
507        return 0;
508      }
509
510      @Override
511      public long getBlocksLocalWithSsdWeight() {
512        return 0;
513      }
514
515      @Override
516      public long getBlocksTotalWeight() {
517        return 0;
518      }
519
520      @Override
521      public CompactionState getCompactionState() {
522        return null;
523      }
524    };
525    return regionMetrics;
526  }
527
528  private static RegionInfo getRegionInfo(byte[] regionNameBytes) {
529    RegionInfo regionInfo = new RegionInfo() {
530
531      @Override
532      public String getShortNameToLog() {
533        return null;
534      }
535
536      @Override
537      public long getRegionId() {
538        return 0;
539      }
540
541      @Override
542      public byte[] getRegionName() {
543        return new byte[0];
544      }
545
546      @Override
547      public String getRegionNameAsString() {
548        try {
549          return new String(regionNameBytes, UTF_8_CHARSET);
550        } catch (UnsupportedEncodingException e) {
551          return "";
552        }
553      }
554
555      @Override
556      public String getEncodedName() {
557        return null;
558      }
559
560      @Override
561      public byte[] getEncodedNameAsBytes() {
562        return new byte[0];
563      }
564
565      @Override
566      public byte[] getStartKey() {
567        return new byte[0];
568      }
569
570      @Override
571      public byte[] getEndKey() {
572        return new byte[0];
573      }
574
575      @Override
576      public TableName getTable() {
577        String regionName;
578        try {
579          regionName = new String(regionNameBytes, UTF_8_CHARSET);
580        } catch (UnsupportedEncodingException e) {
581          regionName = "";
582        }
583        int regionNo = Integer.parseInt(regionName.split("_")[1]);
584        TableName tableName = TableName.valueOf("table_" + regionNo % 3);
585        return tableName;
586      }
587
588      @Override
589      public int getReplicaId() {
590        return 0;
591      }
592
593      @Override
594      public boolean isSplit() {
595        return false;
596      }
597
598      @Override
599      public boolean isOffline() {
600        return false;
601      }
602
603      @Override
604      public boolean isSplitParent() {
605        return false;
606      }
607
608      @Override
609      public boolean isMetaRegion() {
610        return false;
611      }
612
613      @Override
614      public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) {
615        return false;
616      }
617
618      @Override
619      public boolean containsRow(byte[] row) {
620        return false;
621      }
622
623    };
624    return regionInfo;
625  }
626
627  /**
628   * Simple helper class that just keeps track of whether or not its stopped.
629   */
630  private static class StoppableImplementation implements Stoppable {
631
632    private volatile boolean stop = false;
633
634    @Override
635    public void stop(String why) {
636      this.stop = true;
637    }
638
639    @Override
640    public boolean isStopped() {
641      return this.stop;
642    }
643
644  }
645
646}