From e7fc5e86217f044fd32c4b72662a969f22b5fed4 Mon Sep 17 00:00:00 2001 From: Pankaj Kumar Date: Thu, 5 Mar 2026 15:49:03 +0530 Subject: [PATCH] HBASE-29955 HMaster getting aborted due to NPE while creating snapshot for invalid table name --- .../master/snapshot/SnapshotManager.java | 5 +-- .../hbase/client/SnapshotWithAclTestBase.java | 41 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java index bb64062cf1bf..def20b9132ba 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/SnapshotManager.java @@ -720,8 +720,7 @@ private synchronized long submitSnapshotProcedure(SnapshotDescription snapshot, .submitProcedure(new MasterProcedureUtil.NonceProcedureRunnable(master, nonceGroup, nonce) { @Override protected void run() throws IOException { - TableDescriptor tableDescriptor = - master.getTableDescriptors().get(TableName.valueOf(snapshot.getTable())); + TableDescriptor tableDescriptor = sanityCheckBeforeSnapshot(snapshot, false); MasterCoprocessorHost cpHost = getMaster().getMasterCoprocessorHost(); User user = RpcServer.getRequestUser().orElse(null); org.apache.hadoop.hbase.client.SnapshotDescription snapshotDesc = @@ -731,8 +730,6 @@ protected void run() throws IOException { cpHost.preSnapshot(snapshotDesc, tableDescriptor, user); } - sanityCheckBeforeSnapshot(snapshot, false); - long procId = submitProcedure(new SnapshotProcedure( getMaster().getMasterProcedureExecutor().getEnvironment(), snapshot)); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/SnapshotWithAclTestBase.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/SnapshotWithAclTestBase.java index 8f8fdae22eaf..4dc348548e5c 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/SnapshotWithAclTestBase.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/SnapshotWithAclTestBase.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.IOException; import java.util.List; @@ -32,6 +34,7 @@ import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.TableNameTestRule; import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; import org.apache.hadoop.hbase.master.MasterCoprocessorHost; import org.apache.hadoop.hbase.security.User; @@ -40,17 +43,23 @@ import org.apache.hadoop.hbase.security.access.Permission; import org.apache.hadoop.hbase.security.access.PermissionStorage; import org.apache.hadoop.hbase.security.access.SecureTestUtil; +import org.apache.hadoop.hbase.snapshot.SnapshotCreationException; import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils; +import org.apache.hadoop.hbase.snapshot.SnapshotDoesNotExistException; import org.apache.hadoop.hbase.snapshot.SnapshotManifest; import org.apache.hadoop.hbase.util.Bytes; import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; public abstract class SnapshotWithAclTestBase extends SecureTestUtil { + @Rule + public TableNameTestRule name = new TableNameTestRule(); + private TableName TEST_TABLE = TableName.valueOf(TEST_UTIL.getRandomUUID().toString()); private static final int ROW_COUNT = 30000; @@ -309,4 +318,36 @@ public void testDeleteSnapshot() throws Exception { TEST_UTIL.getAdmin().listSnapshots(Pattern.compile(testSnapshotName)); assertEquals(0, snapshotsAfterDelete.size()); } + + @Test + public void testCreateSnapshotWithNonExistingTable() throws Exception { + final TableName tableName = name.getTableName(); + String snapshotName = tableName.getNameAsString() + "snap1"; + + try { + // Create snapshot without creating table + assertThrows("Snapshot operation should fail, table doesn't exist", + SnapshotCreationException.class, + () -> TEST_UTIL.getAdmin().snapshot(snapshotName, tableName)); + + // Create the table + TableDescriptor htd = TableDescriptorBuilder.newBuilder(tableName).build(); + TEST_UTIL.createTable(htd, new byte[][] { TEST_FAMILY }, TEST_UTIL.getConfiguration()); + try { + TEST_UTIL.getAdmin().snapshot(snapshotName, tableName); + } catch (Exception e) { + fail("Snapshot should have been created successfully"); + } + assertTrue(TEST_UTIL.getAdmin().listSnapshots().stream() + .anyMatch(name -> name.getName().equals(snapshotName))); + } finally { + try { + TEST_UTIL.getAdmin().deleteSnapshot(snapshotName); + } catch (SnapshotDoesNotExistException e) { + } + if (TEST_UTIL.getAdmin().tableExists(tableName)) { + TEST_UTIL.deleteTable(tableName); + } + } + } }