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

Code before:
    addReplyError(c,"FAILOVER not allowed in cluster mode. "
                    "Use CLUSTER FAILOVER command instead.");
    return 0;
}

void clusterPromoteSelfToMaster(void) {
    replicationUnsetMaster();
}

void updateNodesHealth(void) {
    dictIterator *di;
    dictEntry *de;
    clusterNode *node;
    int overall_health_changed = 0;
    di = dictGetSafeIterator(server.cluster->nodes);
    while((de = dictNext(di)) != NULL) {
        node = dictGetVal(de);
        int present_node_health;

        long long node_offset = getNodeOffSet(node);

        if (nodeFailed(node) || (nodeIsSlave(node) && node_offset == 0)) {
            present_node_health = 0;
        } else {
            present_node_health = 1;
        }

        if (present_node_health != node->node_health) {
            overall_health_changed = 1;
        }
        node->node_health = present_node_health;
    }
    dictReleaseIterator(di);

    if (overall_health_changed) clearCachedClusterSlotsResp();
}


Review comment (suggestion):
```suggestion
        int present_node_health = isReplicaAvailable(node);
```
I think this is just this right? We may want to rename `isReplicaAvailable` to something a bit more generic, we may also need to export this function.

Code after:
            }
            if ((endslot = getSlotOrReply(c,c->argv[j+1])) == C_ERR) {
                zfree(slots);
                return 1;
            }
            if (startslot > endslot) {
                addReplyErrorFormat(c,"start slot number %d is greater than end slot number %d", startslot, endslot);
                zfree(slots);
                return 1;
            }

            if (checkSlotAssignmentsOrReply(c, slots, del, startslot, endslot) == C_ERR) {
                zfree(slots);
                return 1;
            }
        }
        clusterUpdateSlots(c, slots, del);
        zfree(slots);
        clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|CLUSTER_TODO_SAVE_CONFIG);
        addReply(c,shared.ok);
    } else if (!strcasecmp(c->argv[1]->ptr,"setslot") && c->argc >= 4) {
        /* SETSLOT 10 MIGRATING <node ID> */
        /* SETSLOT 10 IMPORTING <node ID> */
        /* SETSLOT 10 STABLE */
        /* SETSLOT 10 NODE <node ID> */
        clusterCommandSetSlot(c);
    } else if (!strcasecmp(c->argv[1]->ptr,"bumpepoch") && c->argc == 2) {
        /* CLUSTER BUMPEPOCH */
        int retval = clusterBumpConfigEpochWithoutConsensus();
        sds reply = sdscatprintf(sdsempty(),"+%s %llu\r\n",
                (retval == C_OK) ? "BUMPED" : "STILL",
                (unsigned long long) myself->configEpoch);
        addReplySds(c,reply);
    } else if (!strcasecmp(c->argv[1]->ptr,"saveconfig") && c->argc == 2) {
        int retval = clusterSaveConfig(1);

        if (retval == 0)
            addReply(c,shared.ok);
        else
            addReplyErrorFormat(c,"error saving the cluster node config: %s",
                strerror(errno));
    } else if (!strcasecmp(c->argv[1]->ptr,"forget") && c->argc == 3) {
        /* CLUSTER FORGET <NODE ID> */
        clusterNode *n = clusterLookupNode(c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
        if (!n) {
            if (clusterBlacklistExists((char*)c->argv[2]->ptr))
                /* Already forgotten. The deletion may have been gossipped by
                 * another node, so we pretend it succeeded. */
                addReply(c,shared.ok);
            else
                addReplyErrorFormat(c,"Unknown node %s", (char*)c->argv[2]->ptr);