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

Code before:
    server.force_failover = force_flag;
    server.failover_state = FAILOVER_WAIT_FOR_SYNC;
    /* Cluster failover will unpause eventually */
    pauseActions(PAUSE_DURING_FAILOVER, LLONG_MAX, PAUSE_ACTIONS_CLIENT_WRITE_SET);
    addReply(c, shared.ok);
}

/* Failover cron function, checks coordinated failover state.
 *
 * Implementation note: The current implementation calls replicationSetPrimary()
 * to start the failover request, this has some unintended side effects if the
 * failover doesn't work like blocked clients will be unblocked and replicas will
 * be disconnected. This could be optimized further.
 */
void updateFailoverStatus(void) {
    if (server.failover_state != FAILOVER_WAIT_FOR_SYNC) return;
    mstime_t now = server.mstime;

    /* Check if failover operation has timed out */
    if (server.failover_end_time && server.failover_end_time <= now) {
        if (server.force_failover) {
            serverLog(LL_NOTICE, "FAILOVER to %s:%d time out exceeded, failing over.", server.target_replica_host,
                      server.target_replica_port);
            server.failover_state = FAILOVER_IN_PROGRESS;
            /* If timeout has expired force a failover if requested. */
            replicationSetPrimary(server.target_replica_host, server.target_replica_port, 0, 0);
            return;
        } else {
            /* Force was not requested, so timeout. */
            abortFailover("Replica never caught up before timeout");
            return;
        }
    }

    /* Check to see if the replica has caught up so failover can start */
    client *replica = NULL;
    if (server.target_replica_host) {
        replica = findReplica(server.target_replica_host, server.target_replica_port);
    } else {
        listIter li;
        listNode *ln;

        listRewind(server.replicas, &li);
        /* Find any replica that has matched our repl_offset */
        while ((ln = listNext(&li))) {
            replica = ln->value;
            if (replica->repl_data->repl_ack_off == server.primary_repl_offset) {
                char ip[NET_IP_STR_LEN], *replicaaddr = replica->repl_data->replica_addr;

                if (!replicaaddr) {
                    if (connAddrPeerName(replica->conn, ip, sizeof(ip), NULL) == -1) continue;

Review comment (bug):
This function has a comment that says that blocked clients are disconnected if failover fails. Maybe it should be updated?

```c
/* Failover cron function, checks coordinated failover state.
 *
 * Implementation note: The current implementation calls replicationSetPrimary()
 * to start the failover request, this has some unintended side effects if the
 * failover doesn't work like blocked clients will be unblocked and replicas will
 * be disconnected. This could be optimized further.
 */
void updateFailoverStatus(void) {
```

Code after:
    }

    /* If this is a primary without attached replicas and there is a replication
     * backlog active, in order to reclaim memory we can free it after some
     * (configured) time. Note that this cannot be done for replicas: replicas
     * without sub-replicas attached should still accumulate data into the
     * backlog, in order to reply to PSYNC queries if they are turned into
     * primaries after a failover. */
    if (listLength(server.replicas) == 0 && server.repl_backlog_time_limit && server.repl_backlog &&
        server.primary_host == NULL) {
        time_t idle = server.unixtime - server.repl_no_replicas_since;

        if (idle > server.repl_backlog_time_limit) {
            /* When we free the backlog, we always use a new
             * replication ID and clear the ID2. This is needed
             * because when there is no backlog, the primary_repl_offset
             * is not updated, but we would still retain our replication
             * ID, leading to the following problem:
             *
             * 1. We are a primary instance.
             * 2. Our replica is promoted to primary. It's repl-id-2 will
             *    be the same as our repl-id.
             * 3. We, yet as primary, receive some updates, that will not
             *    increment the primary_repl_offset.
             * 4. Later we are turned into a replica, connect to the new
             *    primary that will accept our PSYNC request by second
             *    replication ID, but there will be data inconsistency
             *    because we received writes. */
            changeReplicationId();
            clearReplicationId2();
            freeReplicationBacklog();
            serverLog(LL_NOTICE,
                      "Replication backlog freed after %d seconds "
                      "without connected replicas.",
                      (int)server.repl_backlog_time_limit);
        }
    }

    replicationStartPendingFork();

    /* Handle completion of RDB downloaded that was offloaded to a Bio thread */
    handleBioThreadFinishedRDBDownload();

    /* Remove the RDB file used for replication if the server is not running
     * with any persistence. */
    removeRDBUsedToSyncReplicas();

    /* Sanity check replication buffer, the first block of replication buffer blocks
     * must be referenced by someone, since it will be freed when not referenced,
     * otherwise, server will OOM. also, its refcount must not be more than
     * replicas number + 1(replication backlog). */