Skip to content

Commit 269d61d

Browse files
aalhourndimiduk
authored andcommitted
HBASE-28354 RegionSizeCalculator throws NPE when regions are in transition (apache#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 <ndimiduk@apache.org> Signed-off-by: Hui Ruan <huiruan@apache.org>
1 parent d09d521 commit 269d61d

2 files changed

Lines changed: 34 additions & 21 deletions

File tree

hbase-mapreduce/src/main/java/org/apache/hadoop/hbase/mapreduce/RegionSizeCalculator.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
import java.util.Arrays;
2222
import java.util.Collections;
2323
import java.util.Map;
24+
import java.util.Objects;
2425
import java.util.Set;
2526
import java.util.TreeMap;
27+
import java.util.stream.Collectors;
2628
import org.apache.hadoop.conf.Configuration;
2729
import org.apache.hadoop.hbase.HRegionLocation;
2830
import org.apache.hadoop.hbase.RegionMetrics;
@@ -35,8 +37,6 @@
3537
import org.slf4j.Logger;
3638
import org.slf4j.LoggerFactory;
3739

38-
import org.apache.hbase.thirdparty.com.google.common.collect.Sets;
39-
4040
/**
4141
* Computes size of each region for given table and given column families. The value is used by
4242
* MapReduce for better scheduling.
@@ -96,12 +96,11 @@ private void init(RegionLocator regionLocator, Admin admin) throws IOException {
9696
}
9797

9898
private Set<ServerName> getRegionServersOfTable(RegionLocator regionLocator) throws IOException {
99-
100-
Set<ServerName> tableServers = Sets.newHashSet();
101-
for (HRegionLocation regionLocation : regionLocator.getAllRegionLocations()) {
102-
tableServers.add(regionLocation.getServerName());
103-
}
104-
return tableServers;
99+
// The region locations could contain `null` ServerName instances if the region is currently
100+
// in transition, we filter those out for now, which impacts the size calculation for these
101+
// regions temporarily until the ServerName gets filled in later
102+
return regionLocator.getAllRegionLocations().stream().map(HRegionLocation::getServerName)
103+
.filter(Objects::nonNull).collect(Collectors.toSet());
105104
}
106105

107106
boolean enabled(Configuration configuration) {

hbase-mapreduce/src/test/java/org/apache/hadoop/hbase/mapreduce/TestRegionSizeCalculator.java

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
import java.io.IOException;
2525
import java.util.ArrayList;
26+
import java.util.Arrays;
27+
import java.util.Collections;
2628
import java.util.List;
2729
import org.apache.hadoop.conf.Configuration;
2830
import org.apache.hadoop.hbase.HBaseClassTestRule;
@@ -36,6 +38,7 @@
3638
import org.apache.hadoop.hbase.client.RegionLocator;
3739
import org.apache.hadoop.hbase.testclassification.MiscTests;
3840
import org.apache.hadoop.hbase.testclassification.SmallTests;
41+
import org.apache.hadoop.hbase.util.Bytes;
3942
import org.junit.ClassRule;
4043
import org.junit.Test;
4144
import org.junit.experimental.categories.Category;
@@ -63,11 +66,12 @@ public void testSimpleTestCase() throws Exception {
6366

6467
RegionSizeCalculator calculator = new RegionSizeCalculator(regionLocator, admin);
6568

66-
assertEquals(123 * megabyte, calculator.getRegionSize("region1".getBytes()));
67-
assertEquals(54321 * megabyte, calculator.getRegionSize("region2".getBytes()));
68-
assertEquals(1232 * megabyte, calculator.getRegionSize("region3".getBytes()));
69+
assertEquals(123 * megabyte, calculator.getRegionSize(Bytes.toBytes("region1")));
70+
assertEquals(54321 * megabyte, calculator.getRegionSize(Bytes.toBytes("region2")));
71+
assertEquals(1232 * megabyte, calculator.getRegionSize(Bytes.toBytes("region3")));
72+
6973
// if regionCalculator does not know about a region, it should return 0
70-
assertEquals(0 * megabyte, calculator.getRegionSize("otherTableRegion".getBytes()));
74+
assertEquals(0, calculator.getRegionSize(Bytes.toBytes("otherTableRegion")));
7175

7276
assertEquals(3, calculator.getRegionSizeMap().size());
7377
}
@@ -104,24 +108,37 @@ public void testDisabled() throws Exception {
104108
// then disabled calculator.
105109
configuration.setBoolean(RegionSizeCalculator.ENABLE_REGIONSIZECALCULATOR, false);
106110
RegionSizeCalculator disabledCalculator = new RegionSizeCalculator(table, admin);
107-
assertEquals(0 * megabyte, disabledCalculator.getRegionSize(regionName.getBytes()));
108-
111+
assertEquals(0, disabledCalculator.getRegionSize(Bytes.toBytes(regionName)));
109112
assertEquals(0, disabledCalculator.getRegionSizeMap().size());
110113
}
111114

115+
@Test
116+
public void testRegionWithNullServerName() throws Exception {
117+
RegionLocator regionLocator =
118+
mockRegionLocator(null, Collections.singletonList("someBigRegion"));
119+
Admin admin = mockAdmin(mockRegion("someBigRegion", Integer.MAX_VALUE));
120+
RegionSizeCalculator calculator = new RegionSizeCalculator(regionLocator, admin);
121+
assertEquals(0, calculator.getRegionSize(Bytes.toBytes("someBigRegion")));
122+
}
123+
112124
/**
113125
* Makes some table with given region names.
114126
*/
115127
private RegionLocator mockRegionLocator(String... regionNames) throws IOException {
128+
return mockRegionLocator(sn, Arrays.asList(regionNames));
129+
}
130+
131+
private RegionLocator mockRegionLocator(ServerName serverName, List<String> regionNames)
132+
throws IOException {
116133
RegionLocator mockedTable = Mockito.mock(RegionLocator.class);
117134
when(mockedTable.getName()).thenReturn(TableName.valueOf("sizeTestTable"));
118-
List<HRegionLocation> regionLocations = new ArrayList<>(regionNames.length);
135+
List<HRegionLocation> regionLocations = new ArrayList<>(regionNames.size());
119136
when(mockedTable.getAllRegionLocations()).thenReturn(regionLocations);
120137

121138
for (String regionName : regionNames) {
122139
HRegionInfo info = Mockito.mock(HRegionInfo.class);
123-
when(info.getRegionName()).thenReturn(regionName.getBytes());
124-
regionLocations.add(new HRegionLocation(info, sn));
140+
when(info.getRegionName()).thenReturn(Bytes.toBytes(regionName));
141+
regionLocations.add(new HRegionLocation(info, serverName));
125142
}
126143

127144
return mockedTable;
@@ -132,10 +149,7 @@ private RegionLocator mockRegionLocator(String... regionNames) throws IOExceptio
132149
*/
133150
private Admin mockAdmin(RegionMetrics... regionLoadArray) throws Exception {
134151
Admin mockAdmin = Mockito.mock(Admin.class);
135-
List<RegionMetrics> regionLoads = new ArrayList<>();
136-
for (RegionMetrics regionLoad : regionLoadArray) {
137-
regionLoads.add(regionLoad);
138-
}
152+
List<RegionMetrics> regionLoads = new ArrayList<>(Arrays.asList(regionLoadArray));
139153
when(mockAdmin.getConfiguration()).thenReturn(configuration);
140154
when(mockAdmin.getRegionMetrics(sn, TableName.valueOf("sizeTestTable")))
141155
.thenReturn(regionLoads);

0 commit comments

Comments
 (0)