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

Code before:
            server.cluster->migrating_slots_to[slot] = NULL;
        }

        clusterNode *my_primary = clusterNodeGetPrimary(myself);
        int slot_was_mine = server.cluster->slots[slot] == my_primary;
        clusterDelSlot(slot);
        clusterAddSlot(n, slot);

        /* If replica migration is allowed, check if the primary of this shard
         * loses its last slot and the shard becomes empty. In this case, we
         * should turn into a replica of the new primary. */
        if (server.cluster_allow_replica_migration && slot_was_mine && my_primary->numslots == 0) {
            serverAssert(n != my_primary);
            serverLog(LL_NOTICE,
                      "Lost my last slot during slot migration. Reconfiguring myself "
                      "as a replica of %.40s (%s) in shard %.40s",
                      n->name, n->human_nodename, n->shard_id);
            /* `c` is the primary client if `myself` is a replica, prevent it
             * from being freed by clusterSetPrimary. No need to unprotect it
             * since we will free it soon. */
            if (nodeIsReplica(myself)) protectClient(c);
            /* We are migrating to a different shard that has a completely different
             * replication history, so a full sync is required. */
            clusterSetPrimary(n, 1, 1);
            clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG | CLUSTER_TODO_UPDATE_STATE | CLUSTER_TODO_FSYNC_CONFIG);
        }

        /* If this node or this node's primary was importing this slot,
         * assigning the slot to itself also clears the importing status. */
        if ((n == myself || n == myself->replicaof) && server.cluster->importing_slots_from[slot]) {
            server.cluster->importing_slots_from[slot] = NULL;

            /* Only primary broadcasts the updates */
            if (n == myself) {
                /* This slot was manually migrated, set this node configEpoch
                 * to a new epoch so that the new version can be propagated
                 * by the cluster.
                 *
                 * Note that if this ever results in a collision with another
                 * node getting the same configEpoch, for example because a
                 * failover happens at the same time we close the slot, the
                 * configEpoch collision resolution will fix it assigning
                 * a different epoch to each node. */
                if (clusterBumpConfigEpochWithoutConsensus() == C_OK) {
                    serverLog(LL_NOTICE, "ConfigEpoch updated after importing slot %d", slot);
                }
                /* After importing this slot, let the other nodes know as
                 * soon as possible. */
                clusterBroadcastPong(CLUSTER_BROADCAST_ALL);
            }
        }

Review comment (suggestion):
Sorry. I think we still need to unprotect the client so that it can be properly freed and have `server.primary` cleared.

```suggestion
            clusterSetPrimary(n, 1, 1);
            if (nodeIsReplica(myself)) unprotectClient(c);
```

Code after:
         * for it assigning the slot to another node will clear
         * the migrating status. */
        if (countKeysInSlot(slot) == 0 && server.cluster->migrating_slots_to[slot]) {
            server.cluster->migrating_slots_to[slot] = NULL;
        }

        clusterNode *my_primary = clusterNodeGetPrimary(myself);
        int slot_was_mine = server.cluster->slots[slot] == my_primary;
        clusterDelSlot(slot);
        clusterAddSlot(n, slot);

        /* If replica migration is allowed, check if the primary of this shard
         * loses its last slot and the shard becomes empty. In this case, we
         * should turn into a replica of the new primary. */
        if (server.cluster_allow_replica_migration && slot_was_mine && my_primary->numslots == 0) {
            serverAssert(n != my_primary);
            serverLog(LL_NOTICE,
                      "Lost my last slot during slot migration. Reconfiguring myself "
                      "as a replica of %.40s (%s) in shard %.40s",
                      n->name, n->human_nodename, n->shard_id);
            /* `c` is the primary client if `myself` is a replica, prevent it
             * from being freed by clusterSetPrimary. */
            if (nodeIsReplica(myself)) protectClient(c);
            /* We are migrating to a different shard that has a completely different
             * replication history, so a full sync is required. */
            clusterSetPrimary(n, 1, 1);
            if (nodeIsReplica(myself)) unprotectClient(c);
            clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG | CLUSTER_TODO_UPDATE_STATE | CLUSTER_TODO_FSYNC_CONFIG);
        }

        /* If this node or this node's primary was importing this slot,
         * assigning the slot to itself also clears the importing status. */
        if ((n == myself || n == myself->replicaof) && server.cluster->importing_slots_from[slot]) {
            server.cluster->importing_slots_from[slot] = NULL;

            /* Only primary broadcasts the updates */
            if (n == myself) {
                /* This slot was manually migrated, set this node configEpoch
                 * to a new epoch so that the new version can be propagated
                 * by the cluster.
                 *
                 * Note that if this ever results in a collision with another
                 * node getting the same configEpoch, for example because a
                 * failover happens at the same time we close the slot, the
                 * configEpoch collision resolution will fix it assigning
                 * a different epoch to each node. */
                if (clusterBumpConfigEpochWithoutConsensus() == C_OK) {
                    serverLog(LL_NOTICE, "ConfigEpoch updated after importing slot %d", slot);
                }
                /* After importing this slot, let the other nodes know as
                 * soon as possible. */