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

Code before:
}

/* If one or more clients are blocked on the SHUTDOWN command, this function
 * sends them an error reply and unblocks them. */
void replyToClientsBlockedOnShutdown(void) {
    if (server.blocked_clients_by_type[BLOCKED_SHUTDOWN] == 0) return;
    listNode *ln;
    listIter li;
    listRewind(server.clients, &li);
    while ((ln = listNext(&li))) {
        client *c = listNodeValue(ln);
        if (c->flag.blocked && c->bstate->btype == BLOCKED_SHUTDOWN) {
            addReplyError(c, "Errors trying to SHUTDOWN. Check logs.");
            unblockClient(c, 1);
        }
    }
}

/* Mass-unblock clients because something changed in the instance that makes
 * blocking no longer safe. For example clients blocked in list operations
 * in an instance which turns from primary to replica is unsafe, so this function
 * is called when a primary turns into a replica.
 *
 * The semantics is try to send a -MOVED to the client when cluster_enabled,
 * try to send a -REDIRECT when the client has redirect capability when cluster-disabled,
 * and send an -UNBLOCKED error to the client, disconnecting
 * it at the same time. */
void disconnectAllBlockedClients(void) {
    listNode *ln;
    listIter li;

    listRewind(server.clients, &li);
    while ((ln = listNext(&li))) {
        client *c = listNodeValue(ln);

        if (c->flag.blocked) {
            /* POSTPONEd clients are an exception, when they'll be unblocked, the
             * command processing will start from scratch, and the command will
             * be either executed or rejected. (unlike LIST blocked clients for
             * which the command is already in progress in a way. */
            if (c->bstate->btype == BLOCKED_POSTPONE) continue;

            if (!server.cluster_enabled && c->capa & CLIENT_CAPA_REDIRECT) {
                addReplyErrorSds(c, sdscatprintf(sdsempty(), "-REDIRECT %s:%d", server.primary_host, server.primary_port));
                unblockClientOnError(c, NULL);
            } else if (!server.cluster_enabled || clusterRedirectBlockedClientIfNeeded(c)) {
                unblockClientOnError(c, "-UNBLOCKED force unblock from blocking operation, "
                                        "instance state changed (master -> replica?)");
                c->flag.close_after_reply = 1;
            }
        }

Review comment (suggestion):
```suggestion
 * The semantics are as follows:  
 * - Send a -MOVED to the client in cluster-enabled mode.  
 * - Send a -REDIRECT when the client has redirect capability in standalone mode.  
 * - Otherwise, send a -UNBLOCKED error to the client while disconnecting it at the same time.
```

Code after:
}

/* If one or more clients are blocked on the SHUTDOWN command, this function
 * sends them an error reply and unblocks them. */
void replyToClientsBlockedOnShutdown(void) {
    if (server.blocked_clients_by_type[BLOCKED_SHUTDOWN] == 0) return;
    listNode *ln;
    listIter li;
    listRewind(server.clients, &li);
    while ((ln = listNext(&li))) {
        client *c = listNodeValue(ln);
        if (c->flag.blocked && c->bstate->btype == BLOCKED_SHUTDOWN) {
            addReplyError(c, "Errors trying to SHUTDOWN. Check logs.");
            unblockClient(c, 1);
        }
    }
}

/* Mass-unblock clients because something changed in the instance that makes
 * blocking no longer safe. For example clients blocked in list operations
 * in an instance which turns from primary to replica is unsafe, so this function
 * is called when a primary turns into a replica.
 *
 * The semantics are as follows:
 * - If the client is read-only, and blocked by a read command we can handle, we do not unblock it.
 * - Send a -MOVED to the client in cluster-enabled mode.
 * - Send a -REDIRECT when the client has redirect capability in standalone mode.
 * - Otherwise, send a -UNBLOCKED error to the client while disconnecting it at the same time. */
void disconnectOrRedirectAllBlockedClients(void) {
    listNode *ln;
    listIter li;

    listRewind(server.clients, &li);
    while ((ln = listNext(&li))) {
        client *c = listNodeValue(ln);

        if (c->flag.blocked) {
            /* POSTPONEd clients are an exception, when they'll be unblocked, the
             * command processing will start from scratch, and the command will
             * be either executed or rejected. (unlike LIST blocked clients for
             * which the command is already in progress in a way. */
            if (c->bstate->btype == BLOCKED_POSTPONE) continue;

            if (server.cluster_enabled) {
                if (clusterRedirectBlockedClientIfNeeded(c))
                    unblockClient(c, 1);
            } else {
                /* if the client is read-only and blocked by a read command, we do not unblock it */
                if (c->flag.readonly && !(c->lastcmd->flags & CMD_WRITE)) continue;
                if (clientSupportStandAloneRedirect(c) && (c->bstate->btype == BLOCKED_LIST || c->bstate->btype == BLOCKED_ZSET ||
                                                           c->bstate->btype == BLOCKED_STREAM || c->bstate->btype == BLOCKED_MODULE)) {