From c2a248a0211eacff99915d8ef896d239c6ab8c05 Mon Sep 17 00:00:00 2001 From: Pankaj Kumar Date: Tue, 3 Mar 2026 20:18:59 +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 | 44 +++++++++++++++++++ 2 files changed, 45 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 c86af2bda5e7..75e3c03dcc77 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 @@ -724,8 +724,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 = @@ -735,8 +734,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 5e37901840b4..c4bf6df37a31 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.HBaseTestingUtil; 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; @@ -314,4 +323,39 @@ 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 { + try { + // Create snapshot without creating table + TEST_UTIL.getAdmin().snapshot(snapshotName, tableName); + fail("Snapshot operation should fail, table doesn't exist"); + } catch (Exception e) { + assertTrue(e instanceof SnapshotCreationException); + } + + // 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); + } + } + } }