Repository: valkey-io/valkey
File: src/cluster_legacy.c (C)

Code before:
            slots_pfail++;
        } else {
            slots_ok++;
        }
    }

    dictIterator *di = dictGetIterator(server.cluster->nodes);
    dictEntry *de;
    unsigned nodes_pfail = 0, nodes_fail = 0;
    while ((de = dictNext(di)) != NULL) {
        clusterNode *node = dictGetVal(de);
        if (nodeTimedOut(node)) {
            nodes_pfail++;
        }
        if (nodeFailed(node)) {
            nodes_fail++;
        }
    }
    dictReleaseIterator(di);

    if (nodes_pfail + nodes_fail > dictSize(server.cluster->nodes) && !server.crashed) {
        serverLog(LL_WARNING, "Aggregated count of nodes marked as PFAIL and FAIL exceeds the total count of nodes."
                              "PFAIL nodes: %u, FAIL nodes: %u, total nodes: %lu",
                  nodes_pfail, nodes_fail, dictSize(server.cluster->nodes));
        serverAssert(0);
    }

    info = sdscatprintf(info,
                        "cluster_state:%s\r\n"
                        "cluster_slots_assigned:%d\r\n"
                        "cluster_slots_ok:%d\r\n"
                        "cluster_slots_pfail:%d\r\n"
                        "cluster_slots_fail:%d\r\n"
                        "cluster_nodes_pfail:%u\r\n"
                        "cluster_nodes_fail:%u\r\n"
                        "cluster_known_nodes:%lu\r\n"
                        "cluster_size:%d\r\n"
                        "cluster_current_epoch:%llu\r\n"
                        "cluster_my_epoch:%llu\r\n",
                        statestr[server.cluster->state], slots_assigned, slots_ok, slots_pfail, slots_fail,
                        nodes_pfail, nodes_fail, dictSize(server.cluster->nodes), server.cluster->size,
                        (unsigned long long)server.cluster->currentEpoch, (unsigned long long)nodeEpoch(myself));

    /* Show stats about messages sent and received. */
    long long tot_msg_sent = 0;
    long long tot_msg_received = 0;

    for (int i = 0; i < CLUSTERMSG_TYPE_COUNT; i++) {
        if (server.cluster->stats_bus_messages_sent[i] == 0) continue;
        tot_msg_sent += server.cluster->stats_bus_messages_sent[i];
        info = sdscatprintf(info, "cluster_stats_messages_%s_sent:%lld\r\n", clusterGetMessageTypeString(i),

Review comment (bug):
Recursive crash? So it means it does assert here sometimes, or only when you changed some logic to make it happen?

I was thinking an assert instead of log, if it's not supposed to happen. If it can happen, then maybe only log the warning. The situation might be harmless so we don't need to panic. Maybe.

```suggestion
    if (!server.crashed) {
        /* Prevent recursive crash */
        serverAssert(nodes_pfail + nodes_fail <= dictSize(server.cluster->nodes));
    }
```

Code after:
            slots_pfail++;
        } else {
            slots_ok++;
        }
    }

    dictIterator *di = dictGetIterator(server.cluster->nodes);
    dictEntry *de;
    unsigned nodes_pfail = 0, nodes_fail = 0, voting_nodes_pfail = 0, voting_nodes_fail = 0;
    while ((de = dictNext(di)) != NULL) {
        clusterNode *node = dictGetVal(de);
        if (nodeTimedOut(node)) {
            nodes_pfail++;
            if (clusterNodeIsVotingPrimary(node)) {
                voting_nodes_pfail++;
            }
        }
        if (nodeFailed(node)) {
            nodes_fail++;
            if (clusterNodeIsVotingPrimary(node)) {
                voting_nodes_fail++;
            }
        }
    }
    dictReleaseIterator(di);

    info = sdscatprintf(info,
                        "cluster_state:%s\r\n"
                        "cluster_slots_assigned:%d\r\n"
                        "cluster_slots_ok:%d\r\n"
                        "cluster_slots_pfail:%d\r\n"
                        "cluster_slots_fail:%d\r\n"
                        "cluster_nodes_pfail:%u\r\n"
                        "cluster_nodes_fail:%u\r\n"
                        "cluster_voting_nodes_pfail:%u\r\n"
                        "cluster_voting_nodes_fail:%u\r\n"
                        "cluster_known_nodes:%lu\r\n"
                        "cluster_size:%d\r\n"
                        "cluster_current_epoch:%llu\r\n"
                        "cluster_my_epoch:%llu\r\n",
                        statestr[server.cluster->state], slots_assigned, slots_ok, slots_pfail, slots_fail,
                        nodes_pfail, nodes_fail, voting_nodes_pfail, voting_nodes_fail, dictSize(server.cluster->nodes), server.cluster->size,
                        (unsigned long long)server.cluster->currentEpoch, (unsigned long long)nodeEpoch(myself));

    /* Show stats about messages sent and received. */
    long long tot_msg_sent = 0;
    long long tot_msg_received = 0;

    for (int i = 0; i < CLUSTERMSG_TYPE_COUNT; i++) {
        if (server.cluster->stats_bus_messages_sent[i] == 0) continue;
        tot_msg_sent += server.cluster->stats_bus_messages_sent[i];