diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/RegionReplicaReplicationEndpoint.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/RegionReplicaReplicationEndpoint.java index 7dbb4f008f85..bf8316626dd3 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/RegionReplicaReplicationEndpoint.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/RegionReplicaReplicationEndpoint.java @@ -427,7 +427,6 @@ public void append(TableName tableName, byte[] encodedRegionName, byte[] row, // invalidate the cache and check from meta RegionLocations locations = null; boolean useCache = true; - int retries = 0; while (true) { // get the replicas of the primary region try { @@ -441,12 +440,12 @@ public void append(TableName tableName, byte[] encodedRegionName, byte[] row, // keep going to the cache, we will not learn of the replicas and their locations after // they come online. if (useCache && locations.size() == 1) { - if (tableDescriptors.get(tableName).getRegionReplication() > 1 && retries <= 3) { + TableDescriptor td = tableDescriptors.get(tableName); + if (td != null && td.getRegionReplication() > 1) { // Make an obnoxious log here. See how bad this issue is. Add a timer if happening // too much. LOG.info("Skipping location cache; only one location found for {}", tableName); useCache = false; - retries++; continue; } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/regionserver/TestRegionReplicaReplicationEndpoint.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/regionserver/TestRegionReplicaReplicationEndpoint.java index ffca0caabef6..861414c3709a 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/regionserver/TestRegionReplicaReplicationEndpoint.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/replication/regionserver/TestRegionReplicaReplicationEndpoint.java @@ -22,6 +22,10 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -42,7 +46,9 @@ import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionLocation; import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.RegionLocations; import org.apache.hadoop.hbase.ReplicationPeerNotFoundException; +import org.apache.hadoop.hbase.TableDescriptors; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.Waiter; import org.apache.hadoop.hbase.client.Admin; @@ -557,6 +563,59 @@ public void testMetaCacheMissTriggersRefresh() throws Exception { } } + @Test + public void testNullTableDescriptorDoesNotCauseNPE() throws Exception { + TableName tableName = TableName.valueOf(name.getMethodName()); + int regionReplication = 2; + HTableDescriptor htd = HTU.createTableDescriptor(tableName); + htd.setRegionReplication(regionReplication); + createOrEnableTableWithRetries(htd, true); + + Connection connection = ConnectionFactory.createConnection(HTU.getConfiguration()); + Table table = connection.getTable(tableName); + + try { + HTU.loadNumericRows(table, HBaseTestingUtility.fam1, 0, 100); + + RegionLocator rl = connection.getRegionLocator(tableName); + HRegionLocation hrl = rl.getRegionLocation(HConstants.EMPTY_BYTE_ARRAY); + byte[] encodedRegionName = hrl.getRegionInfo().getEncodedNameAsBytes(); + rl.close(); + + AtomicLong skippedEdits = new AtomicLong(); + RegionReplicaReplicationEndpoint.RegionReplicaOutputSink sink = + mock(RegionReplicaReplicationEndpoint.RegionReplicaOutputSink.class); + when(sink.getSkippedEditsCounter()).thenReturn(skippedEdits); + + TableDescriptors mockTableDescriptors = mock(TableDescriptors.class); + when(mockTableDescriptors.get(tableName)).thenReturn(null); + + RegionLocations singleLocation = new RegionLocations(hrl); + ClusterConnection mockConnection = mock(ClusterConnection.class); + when(mockConnection.locateRegion(eq(tableName), any(byte[].class), anyBoolean(), anyBoolean(), + anyInt())).thenReturn(singleLocation); + when(mockConnection.getConfiguration()).thenReturn(HTU.getConfiguration()); + + RegionReplicaReplicationEndpoint.RegionReplicaSinkWriter sinkWriter = + new RegionReplicaReplicationEndpoint.RegionReplicaSinkWriter(sink, mockConnection, + Executors.newSingleThreadExecutor(), Integer.MAX_VALUE, mockTableDescriptors); + + Cell cell = CellBuilderFactory.create(CellBuilderType.DEEP_COPY) + .setRow(Bytes.toBytes("testRow")).setFamily(HBaseTestingUtility.fam1) + .setValue(Bytes.toBytes("testValue")).setType(Type.Put).build(); + + Entry entry = + new Entry(new WALKeyImpl(encodedRegionName, tableName, 1), new WALEdit().add(cell)); + + sinkWriter.append(tableName, encodedRegionName, Bytes.toBytes("testRow"), + Lists.newArrayList(entry)); + + } finally { + table.close(); + connection.close(); + } + } + @Test public void testMetaCacheSkippedForSingleReplicaTable() throws Exception { TableName tableName = TableName.valueOf(name.getMethodName());