diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index c997f1c6e822..4ac8c251b932 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -1821,6 +1821,14 @@ protected void stopServiceThreads() { } LOG.debug("Stopping service threads"); + + // Stop SplitWALManager before procedure executor to unregister its WorkerAssigner from + // ServerManager and prevent NPE when serverAdded() is called after procedureExecutor is null. + // See HBASE-29804. + if (this.splitWALManager != null) { + this.splitWALManager.stop(); + } + // stop procedure executor prior to other services such as server manager and assignment // manager, as these services are important for some running procedures. See HBASE-24117 for // example. diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/SplitWALManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/SplitWALManager.java index 32b2f4d21f29..dadc6d370050 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/SplitWALManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/SplitWALManager.java @@ -176,4 +176,13 @@ public void releaseSplitWALWorker(ServerName worker) { public void addUsedSplitWALWorker(ServerName worker) { splitWorkerAssigner.addUsedWorker(worker); } + + /** + * Stop the SplitWALManager and unregister the WorkerAssigner from ServerManager. This should be + * called during shutdown to prevent NPE when serverAdded() is triggered after procedureExecutor + * is set to null. + */ + public void stop() { + splitWorkerAssigner.stop(); + } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/WorkerAssigner.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/WorkerAssigner.java index 7b1ec80cab4a..5995d3e2a0cd 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/WorkerAssigner.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/WorkerAssigner.java @@ -23,8 +23,10 @@ import java.util.Map; import java.util.Optional; import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; import org.apache.hadoop.hbase.procedure2.Procedure; import org.apache.hadoop.hbase.procedure2.ProcedureEvent; +import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException; import org.apache.yetus.audience.InterfaceAudience; @@ -80,7 +82,13 @@ public synchronized void release(ServerName serverName) { @Override public synchronized void serverAdded(ServerName worker) { if (!event.isReady()) { - event.wake(master.getMasterProcedureExecutor().getEnvironment().getProcedureScheduler()); + ProcedureExecutor executor = master.getMasterProcedureExecutor(); + if (executor != null) { + MasterProcedureEnv env = executor.getEnvironment(); + if (env != null) { + event.wake(env.getProcedureScheduler()); + } + } } } @@ -93,4 +101,15 @@ public synchronized void addUsedWorker(ServerName worker) { public Integer getAvailableWorker(ServerName serverName) { return currentWorkers.get(serverName); } + + /** + * Stop the WorkerAssigner and unregister it from ServerManager. This should be called during + * shutdown to prevent NPE when serverAdded() is triggered after procedureExecutor is set to null. + */ + public void stop() { + ServerManager sm = this.master.getServerManager(); + if (sm != null) { + sm.unregisterListener(this); + } + } } 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..d7522056c5b9 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 @@ -1255,6 +1255,11 @@ public void stop(String why) { } catch (IOException e) { LOG.error("stop ProcedureCoordinator error", e); } + // Unregister the WorkerAssigner from ServerManager to prevent NPE during shutdown + // when serverAdded() is called after procedureExecutor is set to null + if (verifyWorkerAssigner != null) { + verifyWorkerAssigner.stop(); + } } @Override