diff --git a/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/ServerGrpcChannelBackoffResetHandler.java b/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/ServerGrpcChannelBackoffResetHandler.java index da0c6904fe4c..2d4409946fa8 100644 --- a/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/ServerGrpcChannelBackoffResetHandler.java +++ b/pinot-server/src/main/java/org/apache/pinot/server/starter/helix/ServerGrpcChannelBackoffResetHandler.java @@ -77,13 +77,16 @@ public ServerGrpcChannelBackoffResetHandler(HelixAdmin helixAdmin, String cluste @Override public synchronized void onInstanceConfigChange(List instanceConfigs, NotificationContext context) { + // Only process INIT (listener registration) and CALLBACK (ZK data/child change). + // Ignore FINALIZE (listener unregistration) and other types. + // INIT: first callback when the listener is registered (full cluster snapshot). // isChildChange: an instance ZK node was added or removed under /CONFIGS/PARTICIPANT. // Both require a full scan to rebuild _shuttingDownServers from the current cluster state. NotificationContext.Type type = context.getType(); if (type == NotificationContext.Type.INIT || context.getIsChildChange()) { handleFullScan(); - } else { + } else if (type == NotificationContext.Type.CALLBACK) { // An existing instance's config changed (e.g. IS_SHUTDOWN_IN_PROGRESS toggled). // pathChanged is the ZK path of the specific instance that changed. String pathChanged = context.getPathChanged(); diff --git a/pinot-server/src/test/java/org/apache/pinot/server/starter/helix/ServerGrpcChannelBackoffResetHandlerTest.java b/pinot-server/src/test/java/org/apache/pinot/server/starter/helix/ServerGrpcChannelBackoffResetHandlerTest.java index bd1f5ece6474..f181800e43b5 100644 --- a/pinot-server/src/test/java/org/apache/pinot/server/starter/helix/ServerGrpcChannelBackoffResetHandlerTest.java +++ b/pinot-server/src/test/java/org/apache/pinot/server/starter/helix/ServerGrpcChannelBackoffResetHandlerTest.java @@ -229,6 +229,22 @@ private NotificationContext createCallbackContextForInstance(String instanceId) return context; } + @Test + public void testFinalizeNotificationIsNoOp() { + // Simulate a FINALIZE notification where pathChanged is null. The listener should ignore this. + _handler.onInstanceConfigChange(Collections.emptyList(), createFinalizeContextWithNullPath()); + + verify(_mailboxService, never()).resetConnectBackoff(any(), anyInt()); + verify(_helixAdmin, never()).getInstancesInCluster(any()); + } + + private NotificationContext createFinalizeContextWithNullPath() { + NotificationContext context = new NotificationContext(_helixManager); + context.setType(NotificationContext.Type.FINALIZE); + // pathChanged is not set, so getPathChanged() returns null + return context; + } + private NotificationContext createInitContext() { NotificationContext context = new NotificationContext(_helixManager); context.setType(NotificationContext.Type.INIT);