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

Code before:
     * with CLUSTERMSG_FLAG0_FORCEACK (manual failover). */
    if (clusterNodeIsPrimary(node) || primary == NULL || (!nodeFailed(primary) && !force_ack)) {
        if (clusterNodeIsPrimary(node)) {
            serverLog(LL_WARNING, "Failover auth denied to %.40s (%s) for epoch %llu: it is a primary node", node->name,
                      node->human_nodename, (unsigned long long)requestCurrentEpoch);
        } else if (primary == NULL) {
            serverLog(LL_WARNING, "Failover auth denied to %.40s (%s) for epoch %llu: I don't know its primary",
                      node->name, node->human_nodename, (unsigned long long)requestCurrentEpoch);
        } else if (!nodeFailed(primary)) {
            serverLog(LL_WARNING, "Failover auth denied to %.40s (%s) for epoch %llu: its primary is up", node->name,
                      node->human_nodename, (unsigned long long)requestCurrentEpoch);
        }
        return;
    }

    /* We did not voted for a replica about this primary for two
     * times the node timeout. This is not strictly needed for correctness
     * of the algorithm but makes the base case more linear.
     *
     * This limitation does not restrict manual failover. If a user initiates
     * a manual failover, we need to allow it to vote, otherwise the manual
     * failover may time out. */
    if (!force_ack && mstime() - node->replicaof->voted_time < server.cluster_node_timeout * 2) {
        serverLog(LL_WARNING,
                  "Failover auth denied to %.40s (%s): "
                  "can't vote about this replica before %lld milliseconds",
                  node->name, node->human_nodename,
                  (long long)((server.cluster_node_timeout * 2) - (mstime() - node->replicaof->voted_time)));
        return;
    }

    /* The replica requesting the vote must have a configEpoch for the claimed
     * slots that is >= the one of the primaries currently serving the same
     * slots in the current configuration. */
    for (j = 0; j < CLUSTER_SLOTS; j++) {
        if (bitmapTestBit(claimed_slots, j) == 0) continue;
        if (isSlotUnclaimed(j) || server.cluster->slots[j]->configEpoch <= requestConfigEpoch) {
            continue;
        }
        /* If we reached this point we found a slot that in our current slots
         * is served by a primary with a greater configEpoch than the one claimed
         * by the replica requesting our vote. Refuse to vote for this replica. */
        serverLog(LL_WARNING,
                  "Failover auth denied to %.40s (%s): "
                  "slot %d epoch (%llu) > reqEpoch (%llu)",
                  node->name, node->human_nodename, j, (unsigned long long)server.cluster->slots[j]->configEpoch,
                  (unsigned long long)requestConfigEpoch);
        return;
    }

    /* We can vote for this replica. */

Review comment (bug):
I think the text "can't vote about this primary" means to select a new primary for the shard. It is bad English though.

If we want to talk about the replica, we should say "vote for this primary". Also "before" is some wrong grammar.

```suggestion
                  "Failover auth denied to %.40s (%s): "
                  "can't vote for this replica within %lld milliseconds",
```

But the timestamp `node->replicaof->voted_time` is actually for all replicas of the same primary, so we can't vote for any of them within this time.

I don't know if there is a simle way to write this. It is actually about the primary, so shall we mention the primary node name in the log? Something like this?

```suggestion
                  "Failover auth denied to %.40s (%s): "
                  "can't vote for any replica of %.40s (%s) within %lld milliseconds",
```

Code after:
     * with CLUSTERMSG_FLAG0_FORCEACK (manual failover). */
    if (clusterNodeIsPrimary(node) || primary == NULL || (!nodeFailed(primary) && !force_ack)) {
        if (clusterNodeIsPrimary(node)) {
            serverLog(LL_WARNING, "Failover auth denied to %.40s (%s) for epoch %llu: it is a primary node", node->name,
                      node->human_nodename, (unsigned long long)requestCurrentEpoch);
        } else if (primary == NULL) {
            serverLog(LL_WARNING, "Failover auth denied to %.40s (%s) for epoch %llu: I don't know its primary",
                      node->name, node->human_nodename, (unsigned long long)requestCurrentEpoch);
        } else if (!nodeFailed(primary)) {
            serverLog(LL_WARNING, "Failover auth denied to %.40s (%s) for epoch %llu: its primary is up", node->name,
                      node->human_nodename, (unsigned long long)requestCurrentEpoch);
        }
        return;
    }

    /* We did not voted for a replica about this primary for two
     * times the node timeout. This is not strictly needed for correctness
     * of the algorithm but makes the base case more linear.
     *
     * This limitation does not restrict manual failover. If a user initiates
     * a manual failover, we need to allow it to vote, otherwise the manual
     * failover may time out. */
    if (!force_ack && mstime() - node->replicaof->voted_time < server.cluster_node_timeout * 2) {
        serverLog(LL_WARNING,
                  "Failover auth denied to %.40s (%s): "
                  "can't vote for any replica of %.40s (%s) within %lld milliseconds",
                  node->name, node->human_nodename,
                  node->replicaof->name, node->replicaof->human_nodename,
                  (long long)((server.cluster_node_timeout * 2) - (mstime() - node->replicaof->voted_time)));
        return;
    }

    /* The replica requesting the vote must have a configEpoch for the claimed
     * slots that is >= the one of the primaries currently serving the same
     * slots in the current configuration. */
    for (j = 0; j < CLUSTER_SLOTS; j++) {
        if (bitmapTestBit(claimed_slots, j) == 0) continue;
        if (isSlotUnclaimed(j) || server.cluster->slots[j]->configEpoch <= requestConfigEpoch) {
            continue;
        }
        /* If we reached this point we found a slot that in our current slots
         * is served by a primary with a greater configEpoch than the one claimed
         * by the replica requesting our vote. Refuse to vote for this replica. */
        serverLog(LL_WARNING,
                  "Failover auth denied to %.40s (%s): "
                  "slot %d epoch (%llu) > reqEpoch (%llu)",
                  node->name, node->human_nodename, j, (unsigned long long)server.cluster->slots[j]->configEpoch,
                  (unsigned long long)requestConfigEpoch);
        return;
    }