This is an automated email from the ASF dual-hosted git repository.

ndimiduk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/master by this push:
     new 1c41e861f8f HBASE-28354 RegionSizeCalculator throws NPE when regions 
are in transition (#5699)
1c41e861f8f is described below

commit 1c41e861f8f54206ad98ee9b8ab943d55210c65f
Author: Ahmad Alhour <[email protected]>
AuthorDate: Thu Feb 29 14:55:25 2024 +0100

    HBASE-28354 RegionSizeCalculator throws NPE when regions are in transition 
(#5699)
    
    When a region is in transition, it may briefly have a null ServerName in 
meta. The
    RegionSizeCalculator calls RegionLocator.getAllRegionLocations() and does 
not handle the
    possibility that a RegionLocation.getServerName() could be null. The 
ServerName is eventually
    passed into an Admin call, which results in an NPE.
    
    This has come up in other contexts. For example, taking a look at 
getAllRegionLocations() impl, we
    have checks to ensure that we don't call null server names. We need to 
similarly handle the
    possibility of nulls in RegionSizeCalculator.
    
    Signed-off-by: Nick Dimiduk <[email protected]>
    Signed-off-by: Hui Ruan <[email protected]>
---
 .../hbase/mapreduce/RegionSizeCalculator.java      | 15 +++++------
 .../hbase/mapreduce/TestRegionSizeCalculator.java  | 31 +++++++++++++++-------
 2 files changed, 29 insertions(+), 17 deletions(-)

diff --git 
a/hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/RegionSizeCalculator.java
 
b/hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/RegionSizeCalculator.java
index 4d027196a8f..cc36ef5deb4 100644
--- 
a/hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/RegionSizeCalculator.java
+++ 
b/hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/RegionSizeCalculator.java
@@ -21,8 +21,10 @@ import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.stream.Collectors;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.HRegionLocation;
 import org.apache.hadoop.hbase.RegionMetrics;
@@ -35,8 +37,6 @@ import org.apache.yetus.audience.InterfaceAudience;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
-
 /**
  * Computes size of each region for given table and given column families. The 
value is used by
  * MapReduce for better scheduling.
@@ -96,12 +96,11 @@ public class RegionSizeCalculator {
   }
 
   private Set<ServerName> getRegionServersOfTable(RegionLocator regionLocator) 
throws IOException {
-
-    Set<ServerName> tableServers = Sets.newHashSet();
-    for (HRegionLocation regionLocation : 
regionLocator.getAllRegionLocations()) {
-      tableServers.add(regionLocation.getServerName());
-    }
-    return tableServers;
+    // The region locations could contain `null` ServerName instances if the 
region is currently
+    // in transition, we filter those out for now, which impacts the size 
calculation for these
+    // regions temporarily until the ServerName gets filled in later
+    return 
regionLocator.getAllRegionLocations().stream().map(HRegionLocation::getServerName)
+      .filter(Objects::nonNull).collect(Collectors.toSet());
   }
 
   boolean enabled(Configuration configuration) {
diff --git 
a/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestRegionSizeCalculator.java
 
b/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestRegionSizeCalculator.java
index f841bdbb61d..2fda536438a 100644
--- 
a/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestRegionSizeCalculator.java
+++ 
b/hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestRegionSizeCalculator.java
@@ -23,6 +23,8 @@ import static org.mockito.Mockito.when;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.HBaseClassTestRule;
@@ -67,8 +69,9 @@ public class TestRegionSizeCalculator {
     assertEquals(123 * megabyte, 
calculator.getRegionSize(Bytes.toBytes("region1")));
     assertEquals(54321 * megabyte, 
calculator.getRegionSize(Bytes.toBytes("region2")));
     assertEquals(1232 * megabyte, 
calculator.getRegionSize(Bytes.toBytes("region3")));
+
     // if regionCalculator does not know about a region, it should return 0
-    assertEquals(0 * megabyte, 
calculator.getRegionSize(Bytes.toBytes("otherTableRegion")));
+    assertEquals(0, 
calculator.getRegionSize(Bytes.toBytes("otherTableRegion")));
 
     assertEquals(3, calculator.getRegionSizeMap().size());
   }
@@ -105,24 +108,37 @@ public class TestRegionSizeCalculator {
     // then disabled calculator.
     configuration.setBoolean(RegionSizeCalculator.ENABLE_REGIONSIZECALCULATOR, 
false);
     RegionSizeCalculator disabledCalculator = new RegionSizeCalculator(table, 
admin);
-    assertEquals(0 * megabyte, 
disabledCalculator.getRegionSize(Bytes.toBytes(regionName)));
-
+    assertEquals(0, 
disabledCalculator.getRegionSize(Bytes.toBytes(regionName)));
     assertEquals(0, disabledCalculator.getRegionSizeMap().size());
   }
 
+  @Test
+  public void testRegionWithNullServerName() throws Exception {
+    RegionLocator regionLocator =
+      mockRegionLocator(null, Collections.singletonList("someBigRegion"));
+    Admin admin = mockAdmin(mockRegion("someBigRegion", Integer.MAX_VALUE));
+    RegionSizeCalculator calculator = new RegionSizeCalculator(regionLocator, 
admin);
+    assertEquals(0, calculator.getRegionSize(Bytes.toBytes("someBigRegion")));
+  }
+
   /**
    * Makes some table with given region names.
    */
   private RegionLocator mockRegionLocator(String... regionNames) throws 
IOException {
+    return mockRegionLocator(sn, Arrays.asList(regionNames));
+  }
+
+  private RegionLocator mockRegionLocator(ServerName serverName, List<String> 
regionNames)
+    throws IOException {
     RegionLocator mockedTable = Mockito.mock(RegionLocator.class);
     when(mockedTable.getName()).thenReturn(TableName.valueOf("sizeTestTable"));
-    List<HRegionLocation> regionLocations = new 
ArrayList<>(regionNames.length);
+    List<HRegionLocation> regionLocations = new 
ArrayList<>(regionNames.size());
     when(mockedTable.getAllRegionLocations()).thenReturn(regionLocations);
 
     for (String regionName : regionNames) {
       RegionInfo info = Mockito.mock(RegionInfo.class);
       when(info.getRegionName()).thenReturn(Bytes.toBytes(regionName));
-      regionLocations.add(new HRegionLocation(info, sn));
+      regionLocations.add(new HRegionLocation(info, serverName));
     }
 
     return mockedTable;
@@ -133,10 +149,7 @@ public class TestRegionSizeCalculator {
    */
   private Admin mockAdmin(RegionMetrics... regionLoadArray) throws Exception {
     Admin mockAdmin = Mockito.mock(Admin.class);
-    List<RegionMetrics> regionLoads = new ArrayList<>();
-    for (RegionMetrics regionLoad : regionLoadArray) {
-      regionLoads.add(regionLoad);
-    }
+    List<RegionMetrics> regionLoads = new 
ArrayList<>(Arrays.asList(regionLoadArray));
     when(mockAdmin.getConfiguration()).thenReturn(configuration);
     when(mockAdmin.getRegionMetrics(sn, TableName.valueOf("sizeTestTable")))
       .thenReturn(regionLoads);

Reply via email to