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

Code before:
        /* We add another delay that is proportional to the replica rank.
         * Specifically 1 second * rank. This way replicas that have a probably
         * less updated replication offset, are penalized. */
        server.cluster->failover_auth_time += server.cluster->failover_auth_rank * 1000;
        /* However if this is a manual failover, no delay is needed. */
        if (server.cluster->mf_end) {
            server.cluster->failover_auth_time = now;
            server.cluster->failover_auth_rank = 0;
            /* Reset auth_age since it is outdated now and we can bypass the auth_timeout
             * check in the next state and start the election ASAP. */
            auth_age = 0;
        }
        serverLog(LL_NOTICE,
                  "Start of election delayed for %lld milliseconds "
                  "(rank #%d, offset %lld).",
                  server.cluster->failover_auth_time - now, server.cluster->failover_auth_rank,
                  replicationGetReplicaOffset());
        /* Now that we have a scheduled election, broadcast our offset
         * to all the other replicas so that they'll updated their offsets
         * if our offset is better. */
        clusterBroadcastPong(CLUSTER_BROADCAST_LOCAL_REPLICAS);

        /* Return ASAP if we can't start the election. Doing this allow us to, for example,
         * a manual failover, we can get it to the next state ASAP instead of waiting for
         * the next beforeSleep to kick in. */
        if (now < server.cluster->failover_auth_time) return;
    }

    /* It is possible that we received more updated offsets from other
     * replicas for the same primary since we computed our election delay.
     * Update the delay if our rank changed.
     *
     * Not performed if this is a manual failover. */
    if (server.cluster->failover_auth_sent == 0 && server.cluster->mf_end == 0) {
        int newrank = clusterGetReplicaRank();
        if (newrank != server.cluster->failover_auth_rank) {
            long long added_delay = (newrank - server.cluster->failover_auth_rank) * 1000;
            server.cluster->failover_auth_time += added_delay;
            server.cluster->failover_auth_rank = newrank;
            serverLog(LL_NOTICE, "Replica rank updated to #%d, added %lld milliseconds of delay.", newrank,
                      added_delay);
        }
    }

    /* Return ASAP if we can't still start the election. */
    if (now < server.cluster->failover_auth_time) {
        clusterLogCantFailover(CLUSTER_CANT_FAILOVER_WAITING_DELAY);
        return;
    }

    /* Return ASAP if the election is too old to be valid. */

Review comment (bug):
"Doing this" refers to "return ASAP". But I think you mean "not returning ASAP" allows manual failover to not wait for next beforeSleep.

Idea:

```suggestion
        /* Return ASAP if we can't start the election now. In a manual failover,
         * we can start the election immediately, so in this case we continue to
         * the next state without waiting for the next beforeSleep. */
        if (now < server.cluster->failover_auth_time) return;
```

Code after:
        /* We add another delay that is proportional to the replica rank.
         * Specifically 1 second * rank. This way replicas that have a probably
         * less updated replication offset, are penalized. */
        server.cluster->failover_auth_time += server.cluster->failover_auth_rank * 1000;
        /* However if this is a manual failover, no delay is needed. */
        if (server.cluster->mf_end) {
            server.cluster->failover_auth_time = now;
            server.cluster->failover_auth_rank = 0;
            /* Reset auth_age since it is outdated now and we can bypass the auth_timeout
             * check in the next state and start the election ASAP. */
            auth_age = 0;
        }
        serverLog(LL_NOTICE,
                  "Start of election delayed for %lld milliseconds "
                  "(rank #%d, offset %lld).",
                  server.cluster->failover_auth_time - now, server.cluster->failover_auth_rank,
                  replicationGetReplicaOffset());
        /* Now that we have a scheduled election, broadcast our offset
         * to all the other replicas so that they'll updated their offsets
         * if our offset is better. */
        clusterBroadcastPong(CLUSTER_BROADCAST_LOCAL_REPLICAS);

        /* Return ASAP if we can't start the election now. In a manual failover,
         * we can start the election immediately, so in this case we continue to
         * the next state without waiting for the next beforeSleep. */
        if (now < server.cluster->failover_auth_time) return;
    }

    /* It is possible that we received more updated offsets from other
     * replicas for the same primary since we computed our election delay.
     * Update the delay if our rank changed.
     *
     * Not performed if this is a manual failover. */
    if (server.cluster->failover_auth_sent == 0 && server.cluster->mf_end == 0) {
        int newrank = clusterGetReplicaRank();
        if (newrank != server.cluster->failover_auth_rank) {
            long long added_delay = (newrank - server.cluster->failover_auth_rank) * 1000;
            server.cluster->failover_auth_time += added_delay;
            server.cluster->failover_auth_rank = newrank;
            serverLog(LL_NOTICE, "Replica rank updated to #%d, added %lld milliseconds of delay.", newrank,
                      added_delay);
        }
    }

    /* Return ASAP if we can't still start the election. */
    if (now < server.cluster->failover_auth_time) {
        clusterLogCantFailover(CLUSTER_CANT_FAILOVER_WAITING_DELAY);
        return;
    }

    /* Return ASAP if the election is too old to be valid. */