public void discover() {
        // FIXME too aggressive for a large cluster, NODE_GROUP_SIZE should be
        // considered
        Set<ClickHouseNode> allNodes = new LinkedHashSet<>();
        Set<ClickHouseNode> seeds = new LinkedHashSet<>();
        lock.readLock().lock();
        try {
            // discover nodes freely only when auto discovery is enabled
            for (ClickHouseNode node : nodes) {
                if (node.config.isAutoDiscovery()) {
                    seeds.add(node);
                } else {
                    allNodes.add(node);
                }
            }

            // seeds without protocol
            for (ClickHouseNode node : faultyNodes) {
                if (node.config.isAutoDiscovery()) {
                    seeds.add(node);
                } else {
                    allNodes.add(node);
                }
            }
        } finally {
            lock.readLock().unlock();
        }

        if (seeds.isEmpty()) {
            return;
        }

        Set<ClickHouseNode> newHealthyNodes = new LinkedHashSet<>();
        Set<ClickHouseNode> newFaultyNodes = new LinkedHashSet<>();
        Set<ClickHouseNode> useless = new LinkedHashSet<>();

        queryClusterNodes(seeds, allNodes, newHealthyNodes, newFaultyNodes, useless);

        lock.readLock().lock();
        try {
            // check if there's any new node or decommission as needed
            for (ClickHouseNode n : nodes) {
                if (!allNodes.remove(n)) {
                    useless.add(n);
                }
                newHealthyNodes.remove(n); // just in case
            }
            for (ClickHouseNode n : faultyNodes) {
                if (!allNodes.remove(n)) {
                    useless.add(n);
                }
                newFaultyNodes.remove(n); // just in case
            }
        } finally {
            lock.readLock().unlock();
        }

        boolean noUselessNode = useless.isEmpty();
        if (allNodes.isEmpty() && noUselessNode) {
            return;
        }

        for (ClickHouseNode n : allNodes) { // all new nodes
            update(n, Status.MANAGED);
        }
        for (ClickHouseNode n : newHealthyNodes) {
            update(n, Status.HEALTHY);
        }
        for (ClickHouseNode n : newFaultyNodes) {
            update(n, Status.FAULTY);
        }
        for (ClickHouseNode n : useless) {
            update(n, Status.STANDALONE);
        }

        if (!noUselessNode) {
            scheduleHealthCheck();
        }
    }
Discovers nodes in the same cluster by querying against
{@code system.clusters} table.
clickhouse-client/src/main/java/com/clickhouse/client/ClickHouseNodes.java