{"record":{"id":"51f6d025a56b893a","repo":"apache/druid","slug":"node-s-discovered-but-already-exists-s","errorCode":null,"errorMessage":"Node[%s] discovered but already exists [%s].","messagePattern":"Node\\[(.+?)\\] discovered but already exists \\[(.+?)\\]\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/src/main/java/org/apache/druid/discovery/DruidNodeDiscoveryProvider.java","lineNumber":182,"sourceCode":"    /**\n     * Listens for all node updates and filters them based on {@link #service}. Note: this listener is registered with\n     * the objects returned from {@link #getForNodeRole(NodeRole)}, NOT with {@link ServiceDruidNodeDiscovery} itself.\n     */\n    class FilteringUpstreamListener implements DruidNodeDiscovery.Listener\n    {\n      @Override\n      public void nodesAdded(Collection<DiscoveryDruidNode> nodesDiscovered)\n      {\n        synchronized (lock) {\n          List<DiscoveryDruidNode> nodesAdded = new ArrayList<>();\n          for (DiscoveryDruidNode node : nodesDiscovered) {\n            if (node.getServices().containsKey(service)) {\n              DiscoveryDruidNode prev = nodes.putIfAbsent(node.getDruidNode().getHostAndPortToUse(), node);\n\n              if (prev == null) {\n                nodesAdded.add(node);\n              } else {\n                log.warn(\"Node[%s] discovered but already exists [%s].\", node, prev);\n              }\n            } else {\n              log.warn(\"Node[%s] discovered but doesn't have service[%s]. Ignored.\", node, service);\n            }\n          }\n\n          if (nodesAdded.isEmpty()) {\n            // Don't bother listeners with an empty update, it doesn't make sense.\n            return;\n          }\n\n          Collection<DiscoveryDruidNode> unmodifiableNodesAdded = Collections.unmodifiableCollection(nodesAdded);\n          for (Listener listener : listeners) {\n            try {\n              listener.nodesAdded(unmodifiableNodesAdded);\n            }\n            catch (Exception ex) {\n              log.error(ex, \"Listener[%s].nodesAdded(%s) threw exception. Ignored.\", listener, nodesAdded);","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/discovery/DruidNodeDiscoveryProvider.java#L164-L200","documentation":"Log warning in DruidNodeDiscoveryProvider.NodeDiscoveryListener when a newly announced node's hostAndPort key already exists in the concurrent node map. The putIfAbsent returned an existing entry, so the duplicate announcement is discarded and listeners only see the first-seen node instance.","triggerScenarios":"A node re-announces at the same hostAndPort while the old entry is still present (e.g. fast restart where ZK session expiry has not yet removed the old ephemeral entry), or two processes are configured with the same host:port.","commonSituations":"Service restart faster than the ZK session/ephemeral-node timeout; duplicate configuration (two historicals with the same druid.host); ZK watcher lag delivering an add before the corresponding remove.","solutions":["Ensure each node has a unique druid.host configuration across the cluster.","Wait for the old ZK ephemeral node to expire and verify the entry disappears, or restart the affected node after cleanup.","Check for ZK session expiry/reconnect storms in the logs; stabilize ZK connectivity.","Verify no stale ZK znodes remain under the announcements path after abnormal shutdowns."],"exampleFix":"// before\ndruid.host=historical1:8083  # duplicated on two nodes\n// after\n# node A\ndruid.host=historical1:8083\n# node B\ndruid.host=historical2:8083","handlingStrategy":"validation","validationCode":"// Ensure unique host:port per node before deployment\nSet<String> hosts = allNodes.stream().map(NodeConfig::getHost).collect(toSet());\nif (hosts.size() != allNodes.size()) throw new ConfigException(\"Duplicate druid.host values\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Assign unique druid.host to every node","Avoid restarts faster than the ZK ephemeral session timeout, or use distinct ports during rollout","Monitor ZK session expiry storms"],"tags":["service-discovery","zookeeper","duplicate-node"],"backgroundTag":"invalid-state-transition","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}