src/replication.c C 5,460 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 5,460.
1/* Asynchronous replication implementation.2 *3 * Copyright (c) 2009-Present, Redis Ltd.4 * All rights reserved.5 *6 * Copyright (c) 2024-present, Valkey contributors.7 * All rights reserved.8 *9 * Licensed under your choice of (a) the Redis Source Available License 2.010 * (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the11 * GNU Affero General Public License v3 (AGPLv3).12 *13 * Portions of this file are available under BSD3 terms; see REDISCONTRIBUTIONS for more information.14 */1516/*17 * replication.c - Replication Management18 *19 * This file contains the implementation of Redis's replication logic, which20 * enables data synchronization between master and replica instances.21 * It handles:22 * - Master-to-replica synchronization23 * - Full and partial resynchronizations24 * - Replication backlog management25 * - State machines for replica operations26 * - RDB Channel for Full Sync  (lookup "rdb channel for full sync")27 */2829#include "server.h"30#include "cluster.h"31#include "cluster_slot_stats.h"32#include "bio.h"33#include "functions.h"34#include "connection.h"35#include "cluster_asm.h"3637#include <memory.h>38#include <sys/time.h>39#include <unistd.h>40#include <fcntl.h>41#include <sys/socket.h>42#include <sys/stat.h>4344void replicationDiscardCachedMaster(void);45void replicationResurrectCachedMaster(connection *conn);46void replicationSendAck(void);47int replicaPutOnline(client *slave);48void replicaStartCommandStream(client *slave);49int cancelReplicationHandshake(int reconnect);50static void rdbChannelFullSyncWithMaster(connection *conn);51static int rdbChannelAbort(void);52static void rdbChannelBufferReplData(connection *conn);53static void rdbChannelReplDataBufInit(void);54static void rdbChannelStreamReplDataToDb(void);55static void rdbChannelCleanup(void);5657/* We take a global flag to remember if this instance generated an RDB58 * because of replication, so that we can remove the RDB file in case59 * the instance is configured to have no persistence. */60int RDBGeneratedByReplication = 0;616263/* A reference to diskless loading rio to abort it asynchronously. It's needed64 * for rdbchannel replication. While loading from rdbchannel connection, we may65 * yield back to eventloop. If main channel connection detects a network problem66 * we want to abort loading. It calls rioAbort() in this case, so next rioRead()67 * from rdbchannel connection will return error to cancel loading safely. */68static rio *disklessLoadingRio = NULL;6970/* --------------------------- Utility functions ---------------------------- */7172/* Returns 1 if the replica is rdbchannel and there is an associated main73 * channel slave with that. */74int replicationCheckHasMainChannel(client *replica) {75    if (!(replica->flags & CLIENT_REPL_RDB_CHANNEL) ||76        !replica->main_ch_client_id ||77        lookupClientByID(replica->main_ch_client_id) == NULL)78    {79        return 0;80    }81    return 1;82}8384/* During rdb channel replication, replica opens two connections. From master85 * POV, these connections are distinct replicas in server.slaves. This function86 * counts associated replicas as one and returns logical replica count. */87unsigned long replicationLogicalReplicaCount(void) {88    unsigned long count = 0;89    listNode *ln;90    listIter li;9192    listRewind(server.slaves,&li);93    while ((ln = listNext(&li))) {94        client *replica = listNodeValue(ln);95        if (!replicationCheckHasMainChannel(replica))96            count++;97    }98    return count;99}100101int replicaFromIOThreadHasPendingRead(client *c) {102    serverAssert(c->tid != IOTHREAD_MAIN_THREAD_ID);103104    int pending_read;105    atomicGetWithSync(c->pending_read, pending_read);106    return pending_read;107}108109/* Send replicas to their respective IO threads if it has pending reads or110 * writes. Otherwise it remains in main thread so it can check for new data in111 * the replication buffer ASAP. */112void putReplicasInPendingClientsToIOThreads(void) {113    if (server.io_threads_num <= 1) return;114115    serverAssert(pthread_equal(pthread_self(), server.main_thread_id));116117    listIter li;118    listNode *ln;119    listRewind(server.slaves,&li);120    while((ln = listNext(&li))) {121        client *replica = listNodeValue(ln);122123        /* We only care about replicas that need to run on IO thread but are124         * currently in main */125        if (replica->tid == IOTHREAD_MAIN_THREAD_ID ||126            replica->running_tid != IOTHREAD_MAIN_THREAD_ID)127        {128            continue;129        }130131        /* Skip the replica if it's scheduled for close */132        if (replica->flags & CLIENT_CLOSE_ASAP) continue;133134        /* The call to clientHasPendingReplies may seem redundant but in the135         * case of replica being in IO thread we can have the following case:136         * replica gets back to main thread after sending the repl buffer it137         * knows about. In the mean time main thread has accumulated new repl138         * data. In that case the replica's client wouldn't have been put in139         * the pending write queue but will still have new repl data it needs to140         * send, so we make sure to check for that and send it back to IO thread141         * if so. On the other hand if replica gets back to main thread before142         * any new repl data has accumulated then after a new cmd is propagated143         * the replica will be put in the pending write queue as usual so we144         * need to check for that also.145         * In addition, if the replica client has pending read events, we should146         * also send them to the IO thread. */147        if (replica->flags & CLIENT_PENDING_WRITE ||148            clientHasPendingReplies(replica) ||149            replicaFromIOThreadHasPendingRead(replica))150        {151            enqueuePendingClienstToIOThreads(replica);152        }153    }154}155156/* Run some cron tasks for a connected master client. Return 1 when the client157 * is freed, 0 otherwise. */158int replicationCronRunMasterClient(void) {159    if (!server.masterhost || !server.master) return 0;160161    if (server.master->running_tid != IOTHREAD_MAIN_THREAD_ID) return 0;162163    /* Timed out master when we are an already connected slave? */164    if (server.repl_state == REPL_STATE_CONNECTED &&165        (time(NULL)-server.master->lastinteraction) > server.repl_timeout)166    {167        serverLog(LL_WARNING,"MASTER timeout: no data nor PING received...");168        freeClient(server.master);169        return 1;170    }171172    /* Send ACK to master from time to time.173     * Note that we do not send periodic acks to masters that don't174     * support PSYNC and replication offsets. */175    if (!(server.master->flags & CLIENT_PRE_PSYNC))176        replicationSendAck();177178    return 0;179}180181ConnectionType *connTypeOfReplication(void) {182    if (server.tls_replication) {183        return connectionTypeTls();184    }185186    return connectionTypeTcp();187}188189/* Return the pointer to a string representing the slave ip:listening_port190 * pair. Mostly useful for logging, since we want to log a slave using its191 * IP address and its listening port which is more clear for the user, for192 * example: "Closing connection with replica 10.1.2.3:6380". */193char *replicationGetSlaveName(client *c) {194    static char buf[NET_HOST_PORT_STR_LEN];195    char ip[NET_IP_STR_LEN];196197    ip[0] = '\0';198    buf[0] = '\0';199    if (c->slave_addr ||200        connAddrPeerName(c->conn,ip,sizeof(ip),NULL) != -1)201    {202        char *addr = c->slave_addr ? c->slave_addr : ip;203        if (c->slave_listening_port)204            formatAddr(buf,sizeof(buf),addr,c->slave_listening_port);205        else206            snprintf(buf,sizeof(buf),"%s:<unknown-replica-port>",addr);207    } else {208        snprintf(buf,sizeof(buf),"client id #%llu",209            (unsigned long long) c->id);210    }211    return buf;212}213214/* Plain unlink() can block for quite some time in order to actually apply215 * the file deletion to the filesystem. This call removes the file in a216 * background thread instead. We actually just do close() in the thread,217 * by using the fact that if there is another instance of the same file open,218 * the foreground unlink() will only remove the fs name, and deleting the219 * file's storage space will only happen once the last reference is lost. */220int bg_unlink(const char *filename) {221    int fd = open(filename,O_RDONLY|O_NONBLOCK);222    if (fd == -1) {223        /* Can't open the file? Fall back to unlinking in the main thread. */224        return unlink(filename);225    } else {226        /* The following unlink() removes the name but doesn't free the227         * file contents because a process still has it open. */228        int retval = unlink(filename);229        if (retval == -1) {230            /* If we got an unlink error, we just return it, closing the231             * new reference we have to the file. */232            int old_errno = errno;233            close(fd);  /* This would overwrite our errno. So we saved it. */234            errno = old_errno;235            return -1;236        }237        bioCreateCloseJob(fd, 0, 0);238        return 0; /* Success. */239    }240}241242/* ---------------------------------- MASTER -------------------------------- */243244void createReplicationBacklog(void) {245    serverAssert(server.repl_backlog == NULL);246    server.repl_backlog = zmalloc(sizeof(replBacklog));247    server.repl_backlog->ref_repl_buf_node = NULL;248    server.repl_backlog->unindexed_count = 0;249    server.repl_backlog->blocks_index = raxNewEx(0, NULL, sizeof(uint64_t));250    server.repl_backlog->histlen = 0;251    /* We don't have any data inside our buffer, but virtually the first252     * byte we have is the next byte that will be generated for the253     * replication stream. */254    server.repl_backlog->offset = server.master_repl_offset+1;255}256257/* This function is called when the user modifies the replication backlog258 * size at runtime. It is up to the function to resize the buffer and setup it259 * so that it contains the same data as the previous one (possibly less data,260 * but the most recent bytes, or the same data and more free space in case the261 * buffer is enlarged). */262void resizeReplicationBacklog(void) {263    if (server.repl_backlog_size < CONFIG_REPL_BACKLOG_MIN_SIZE)264        server.repl_backlog_size = CONFIG_REPL_BACKLOG_MIN_SIZE;265    if (server.repl_backlog)266        incrementalTrimReplicationBacklog(REPL_BACKLOG_TRIM_BLOCKS_PER_CALL);267}268269void freeReplicationBacklog(void) {270    serverAssert(listLength(server.slaves) == 0);271    if (server.repl_backlog == NULL) return;272273    /* Decrease the start buffer node reference count. */274    if (server.repl_backlog->ref_repl_buf_node) {275        replBufBlock *o = listNodeValue(276            server.repl_backlog->ref_repl_buf_node);277        serverAssert(o->refcount == 1); /* Last reference. */278        o->refcount--;279    }280281    /* Replication buffer blocks are completely released when we free the282     * backlog, since the backlog is released only when there are no replicas283     * and the backlog keeps the last reference of all blocks. */284    freeReplicationBacklogRefMemAsync(server.repl_buffer_blocks,285                            server.repl_backlog->blocks_index);286    resetReplicationBuffer();287    zfree(server.repl_backlog);288    server.repl_backlog = NULL;289}290291/* To make search offset from replication buffer blocks quickly292 * when replicas ask partial resynchronization, we create one index293 * block every REPL_BACKLOG_INDEX_PER_BLOCKS blocks. */294void createReplicationBacklogIndex(listNode *ln) {295    server.repl_backlog->unindexed_count++;296    if (server.repl_backlog->unindexed_count >= REPL_BACKLOG_INDEX_PER_BLOCKS) {297        replBufBlock *o = listNodeValue(ln);298        uint64_t encoded_offset = htonu64(o->repl_offset);299        raxInsert(server.repl_backlog->blocks_index,300                  (unsigned char*)&encoded_offset, sizeof(uint64_t),301                  ln, NULL);302        server.repl_backlog->unindexed_count = 0;303    }304}305306/* Rebase replication buffer blocks' offset since the initial307 * setting offset starts from 0 when master restart. */308void rebaseReplicationBuffer(long long base_repl_offset) {309    raxFree(server.repl_backlog->blocks_index);310    server.repl_backlog->blocks_index = raxNewEx(0, NULL, sizeof(uint64_t));311    server.repl_backlog->unindexed_count = 0;312313    listIter li;314    listNode *ln;315    listRewind(server.repl_buffer_blocks, &li);316    while ((ln = listNext(&li))) {317        replBufBlock *o = listNodeValue(ln);318        o->repl_offset += base_repl_offset;319        createReplicationBacklogIndex(ln);320    }321}322323void resetReplicationBuffer(void) {324    server.repl_buffer_mem = 0;325    server.repl_buffer_blocks = listCreate();326    listSetFreeMethod(server.repl_buffer_blocks, zfree);327}328329int canFeedReplicaReplBuffer(client *replica) {330    /* Don't feed replicas that only want the RDB or main channels of migration331     * destinations which need filtered stream for migrating slot ranges. */332    if (replica->flags & CLIENT_REPL_RDBONLY ||333        replica->flags & CLIENT_ASM_MIGRATING) return 0;334335    /* Don't feed replicas that are still waiting for BGSAVE to start. */336    if (replica->replstate == SLAVE_STATE_WAIT_BGSAVE_START ||337        replica->replstate == SLAVE_STATE_WAIT_RDB_CHANNEL) return 0;338339    /* Don't feed replicas that are going to be closed ASAP. */340    if (replica->flags & CLIENT_CLOSE_ASAP) return 0;341342    return 1;343}344345/* Create the replication backlog if needed. */346void createReplicationBacklogIfNeeded(void) {347    if (listLength(server.slaves) == 1 && server.repl_backlog == NULL) {348        /* When we create the backlog from scratch, we always use a new349         * replication ID and clear the ID2, since there is no valid350         * past history. */351        changeReplicationId();352        clearReplicationId2();353        createReplicationBacklog();354        serverLog(LL_NOTICE,"Replication backlog created, my new "355                            "replication IDs are '%s' and '%s'",356                            server.replid, server.replid2);357    }358}359/* Similar with 'prepareClientToWrite', note that we must call this function360 * before feeding replication stream into global replication buffer, since361 * clientHasPendingReplies in prepareClientToWrite will access the global362 * replication buffer to make judgements. */363int prepareReplicasToWrite(void) {364    listIter li;365    listNode *ln;366    int prepared = 0;367368    listRewind(server.slaves,&li);369    while((ln = listNext(&li))) {370        client *slave = ln->value;371        if (!canFeedReplicaReplBuffer(slave)) continue;372        if (prepareClientToWrite(slave) == C_ERR) continue;373        prepared++;374    }375376    return prepared;377}378379/* Generally, we only have one replication buffer block to trim when replication380 * backlog size exceeds our setting and no replica reference it. But if replica381 * clients disconnect, we need to free many replication buffer blocks that are382 * referenced. It would cost much time if there are a lots blocks to free, that383 * will freeze server, so we trim replication backlog incrementally. */384void incrementalTrimReplicationBacklog(size_t max_blocks) {385    serverAssert(server.repl_backlog != NULL);386387    size_t trimmed_blocks = 0;388    while (server.repl_backlog->histlen > server.repl_backlog_size &&389           trimmed_blocks < max_blocks)390    {391        /* We never trim backlog to less than one block. */392        if (listLength(server.repl_buffer_blocks) <= 1) break;393394        /* Replicas increment the refcount of the first replication buffer block395         * they refer to, in that case, we don't trim the backlog even if396         * backlog_histlen exceeds backlog_size. This implicitly makes backlog397         * bigger than our setting, but makes the master accept partial resync as398         * much as possible. So that backlog must be the last reference of399         * replication buffer blocks. */400        listNode *first = listFirst(server.repl_buffer_blocks);401        serverAssert(first == server.repl_backlog->ref_repl_buf_node);402        replBufBlock *fo = listNodeValue(first);403        if (fo->refcount != 1) break;404405        /* We don't try trim backlog if backlog valid size will be lessen than406         * setting backlog size once we release the first repl buffer block. */407        if (server.repl_backlog->histlen - (long long)fo->size <=408            server.repl_backlog_size) break;409410        /* Decr refcount and release the first block later. */411        fo->refcount--;412        trimmed_blocks++;413        server.repl_backlog->histlen -= fo->size;414415        /* Go to use next replication buffer block node. */416        listNode *next = listNextNode(first);417        server.repl_backlog->ref_repl_buf_node = next;418        serverAssert(server.repl_backlog->ref_repl_buf_node != NULL);419        /* Incr reference count to keep the new head node. */420        ((replBufBlock *)listNodeValue(next))->refcount++;421422        /* Remove the node in recorded blocks. */423        uint64_t encoded_offset = htonu64(fo->repl_offset);424        raxRemove(server.repl_backlog->blocks_index,425            (unsigned char*)&encoded_offset, sizeof(uint64_t), NULL);426427        /* Delete the first node from global replication buffer. */428        serverAssert(fo->refcount == 0 && fo->used == fo->size);429        server.repl_buffer_mem -= (fo->size +430            sizeof(listNode) + sizeof(replBufBlock));431        listDelNode(server.repl_buffer_blocks, first);432    }433434    /* Set the offset of the first byte we have in the backlog. */435    server.repl_backlog->offset = server.master_repl_offset -436                              server.repl_backlog->histlen + 1;437}438439/* Free replication buffer blocks that are referenced by this client. */440void freeReplicaReferencedReplBuffer(client *replica) {441    serverAssert(replica->running_tid == IOTHREAD_MAIN_THREAD_ID);442443    if (replica->ref_repl_buf_node != NULL) {444        /* Decrease the start buffer node reference count. */445        replBufBlock *o = listNodeValue(replica->ref_repl_buf_node);446        serverAssert(o->refcount > 0);447        o->refcount--;448        incrementalTrimReplicationBacklog(REPL_BACKLOG_TRIM_BLOCKS_PER_CALL);449    }450    replica->ref_repl_buf_node = NULL;451    replica->ref_block_pos = 0;452}453454/* Batched write API for the global replication backlog, optimized for minimal455 * overhead per append: data writes are just memcpys into the tail block.456 * All bookkeeping is deferred to replBufWriterEnd(). */457typedef struct replBufWriter {458    listNode *start_node;  /* First repl buffer block written to. */459    size_t start_pos;      /* Byte offset within start_node where writing began. */460    size_t total_len;      /* Total bytes written across all writes. */461    int new_blocks;        /* Number of new blocks allocated during this stream. */462    replBufBlock *tail;    /* Current tail block. */463} replBufWriter;464465/* Initialize the writer, cache the current tail position. */466static void replBufWriterBegin(replBufWriter *wr) {467    listNode *ln = listLast(server.repl_buffer_blocks);468    replBufBlock *tail = ln ? listNodeValue(ln) : NULL;469470    if (tail && tail->used < tail->size) {471        wr->start_node = ln;472        wr->start_pos = tail->used;473    } else {474        wr->start_node = NULL;475        wr->start_pos = 0;476    }477478    wr->total_len = 0;479    wr->new_blocks = 0;480    wr->tail = tail;481}482483/* Allocate a new replication backlog block. Called when current block is full. */484static void replBufWriterAllocBlock(replBufWriter *wr, size_t hint) {485    static long long repl_block_id = 0;486    size_t usable_size;487    /* Avoid creating nodes smaller than PROTO_REPLY_CHUNK_BYTES, so that we can append more data into them,488     * and also avoid creating nodes bigger than repl_backlog_size / 16, so that we won't have huge nodes that can't489     * trim when we only still need to hold a small portion from them. */490    size_t limit = max((size_t)server.repl_backlog_size / 16, (size_t)PROTO_REPLY_CHUNK_BYTES);491    size_t bsize = min(max(hint, (size_t)PROTO_REPLY_CHUNK_BYTES), limit);492    replBufBlock *tail = zmalloc_usable(bsize + sizeof(replBufBlock), &usable_size);493    /* Take over the allocation's internal fragmentation */494    tail->size = usable_size - sizeof(replBufBlock);495    tail->used = 0;496    tail->refcount = 0;497    tail->repl_offset = server.master_repl_offset + wr->total_len + 1;498    tail->id = repl_block_id++;499    listAddNodeTail(server.repl_buffer_blocks, tail);500    server.repl_buffer_mem += (usable_size + sizeof(listNode));501    createReplicationBacklogIndex(listLast(server.repl_buffer_blocks));502503    /* Update stream state. */504    wr->tail = tail;505    wr->new_blocks++;506    if (wr->start_node == NULL) {507        wr->start_node = listLast(server.repl_buffer_blocks);508        wr->start_pos = 0;509    }510}511512/* Slow path: fill remainder of current block + allocate as needed. */513static void replBufWriterAppendSlow(replBufWriter *wr, const char *buf, size_t len) {514    while (len > 0) {515        size_t avail = wr->tail ? wr->tail->size - wr->tail->used : 0;516        if (avail > 0) {517            size_t copy = (avail >= len) ? len : avail;518            memcpy(wr->tail->buf + wr->tail->used, buf, copy);519            wr->tail->used += copy;520            wr->total_len += copy;521            buf += copy;522            len -= copy;523        }524525        if (len > 0)526            replBufWriterAllocBlock(wr, len);527    }528}529530/* Write data into the replication buffer. The slow path is split out to give 531 * the compiler a chance to inline the common case where the write fits entirely532 * in the current block. */533static inline void replBufWriterAppend(replBufWriter *wr, const char *buf, size_t len) {534    size_t avail = wr->tail ? wr->tail->size - wr->tail->used : 0;535    if (len > 0 && avail >= len) {536        memcpy(wr->tail->buf + wr->tail->used, buf, len);537        wr->tail->used += len;538        wr->total_len += len;539        return;540    }541    replBufWriterAppendSlow(wr, buf, len);542}543544/* Write a RESP header prefix<value>\r\n (e.g. "$12\r\n" or "*3\r\n").545 * Uses pre-built shared objects for small values, formats manually otherwise. */546static inline void replBufWriterAppendBulkLen(replBufWriter *wr, char prefix, long long value) {547    serverAssert(prefix == '$' || prefix == '*');548    if (value >= 0 && value < OBJ_SHARED_BULKHDR_LEN) {549        robj **tbl = (prefix == '$') ? shared.bulkhdr : shared.mbulkhdr;550        replBufWriterAppend(wr, tbl[value]->ptr, OBJ_SHARED_HDR_STRLEN(value));551        return;552    }553    char buf[LONG_STR_SIZE+3];554    buf[0] = prefix;555    int len = ll2string(buf+1, sizeof(buf)-1, value);556    buf[len+1] = '\r';557    buf[len+2] = '\n';558    replBufWriterAppend(wr, buf, len+3);559}560561562/* Finalize the replication buffer write: update global offsets, set up replica563 * references for new data, check output buffer limits, and trim the564 * backlog if new blocks were allocated. */565static void replBufWriterEnd(replBufWriter *wr) {566    if (wr->total_len == 0) return;567568    serverAssert(wr->start_node != NULL);569    clusterSlotStatsIncrNetworkBytesOutForReplication(wr->total_len);570571    /* Update the current cmd's keys with the commands replication bytes*/572    hotkeyMetrics metrics = {0, wr->total_len};573    hotkeyStatsUpdateCurrentCmd(server.hotkeys, metrics);574575    server.master_repl_offset += wr->total_len;576    server.repl_backlog->histlen += wr->total_len;577578    /* For output buffer of replicas. */579    listIter li;580    listNode *ln;581    listRewind(server.slaves,&li);582    while((ln = listNext(&li))) {583        client *slave = ln->value;584        if (!canFeedReplicaReplBuffer(slave)) continue;585586        /* Update shared replication buffer start position. */587        if (slave->ref_repl_buf_node == NULL) {588            slave->ref_repl_buf_node = wr->start_node;589            slave->ref_block_pos = wr->start_pos;590            /* Only increase the start block reference count. */591            ((replBufBlock *)listNodeValue(wr->start_node))->refcount++;592        }593594        /* Check output buffer limit only when new blocks were added. */595        if (wr->new_blocks) closeClientOnOutputBufferLimitReached(slave, 1);596    }597598    /* For replication backlog */599    if (server.repl_backlog->ref_repl_buf_node == NULL) {600        server.repl_backlog->ref_repl_buf_node = wr->start_node;601        /* Only increase the start block reference count. */602        ((replBufBlock *)listNodeValue(wr->start_node))->refcount++;603604        /* Replication buffer must be empty before adding replication stream605         * into replication backlog. */606        serverAssert(wr->new_blocks > 0 && wr->start_pos == 0);607    }608    if (wr->new_blocks) {609        /* It is important to trim after adding replication data to keep the backlog size close to610         * repl_backlog_size in the common case. We wait until we add a new block to avoid repeated611         * unnecessary trimming attempts when small amounts of data are added. See comments in612         * freeMemoryGetNotCountedMemory() for details on replication backlog memory tracking. */613        incrementalTrimReplicationBacklog(REPL_BACKLOG_TRIM_BLOCKS_PER_CALL);614    }615}616617/* Append bytes into the global replication buffer. */618static void feedReplicationBuffer(const char *buf, size_t len) {619    replBufWriter wr;620    replBufWriterBegin(&wr);621    replBufWriterAppend(&wr, buf, len);622    replBufWriterEnd(&wr);623}624625/* Propagate write commands to replication stream.626 *627 * This function is used if the instance is a master: we use the commands628 * received by our clients in order to create the replication stream.629 * Instead if the instance is a replica and has sub-replicas attached, we use630 * replicationFeedStreamFromMasterStream() */631void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {632    int j, len;633    char llstr[LONG_STR_SIZE];634635    /* In case we propagate a command that doesn't touch keys (PING, REPLCONF) we636     * pass dbid=-1 that indicate there is no need to replicate `select` command. */637    serverAssert(dictid == -1 || (dictid >= 0 && dictid < server.dbnum));638639    /* If the instance is not a top level master, return ASAP: we'll just proxy640     * the stream of data we receive from our master instead, in order to641     * propagate *identical* replication stream. In this way this slave can642     * advertise the same replication ID as the master (since it shares the643     * master replication history and has the same backlog and offsets). */644    if (server.masterhost != NULL) return;645646    /* If current client is marked as master, we will proxy the command stream647     * to our slaves instead of replicating them, that also happens when being648     * in atomic slot migration. */649    if (server.current_client && server.current_client->flags & CLIENT_MASTER) return;650651    /* If there aren't slaves, and there is no backlog buffer to populate,652     * we can return ASAP. */653    if (server.repl_backlog == NULL && listLength(slaves) == 0) {654        /* We increment the repl_offset anyway, since we use that for tracking AOF fsyncs655         * even when there's no replication active. This code will not be reached if AOF656         * is also disabled. */657        server.master_repl_offset += 1;658        return;659    }660661    /* We can't have slaves attached and no backlog. */662    serverAssert(!(listLength(slaves) != 0 && server.repl_backlog == NULL));663664    /* Update the time of sending replication stream to replicas. */665    server.repl_stream_lastio = server.unixtime;666667    /* Must install write handler for all replicas first before feeding668     * replication stream. */669    prepareReplicasToWrite();670671    /* Send SELECT command to every slave if needed. */672    if (dictid != -1 && server.slaveseldb != dictid) {673        robj *selectcmd;674675        /* For a few DBs we have pre-computed SELECT command. */676        if (dictid >= 0 && dictid < PROTO_SHARED_SELECT_CMDS) {677            selectcmd = shared.select[dictid];678        } else {679            int dictid_len;680681            dictid_len = ll2string(llstr,sizeof(llstr),dictid);682            selectcmd = createObject(OBJ_STRING,683                sdscatprintf(sdsempty(),684                "*2\r\n$6\r\nSELECT\r\n$%d\r\n%s\r\n",685                dictid_len, llstr));686        }687688        feedReplicationBuffer(selectcmd->ptr, sdslen(selectcmd->ptr));689690        /* Although the SELECT command is not associated with any slot,691         * its per-slot network-bytes-out accumulation is made by the above function call.692         * To cancel-out this accumulation, below adjustment is made. */693        clusterSlotStatsDecrNetworkBytesOutForReplication(sdslen(selectcmd->ptr));694695        if (dictid < 0 || dictid >= PROTO_SHARED_SELECT_CMDS)696            decrRefCount(selectcmd);697698        server.slaveseldb = dictid;699    }700701    /* Write the command to the replication buffer if any. */702    char aux[LONG_STR_SIZE+3];703    replBufWriter wr;704    replBufWriterBegin(&wr);705706    /* Write the multi bulk count */707    replBufWriterAppendBulkLen(&wr, '*', argc);708709    for (j = 0; j < argc; j++) {710        /* Write the bulk count */711        long objlen = stringObjectLen(argv[j]);712        replBufWriterAppendBulkLen(&wr, '$', objlen);713714        /* Write the bulk data */715        if (argv[j]->encoding == OBJ_ENCODING_INT) {716            len = ll2string(aux, sizeof(aux), (long)argv[j]->ptr);717            replBufWriterAppend(&wr, aux, len);718        } else {719            replBufWriterAppend(&wr, argv[j]->ptr, objlen);720        }721        replBufWriterAppend(&wr, "\r\n", 2);722    }723724    replBufWriterEnd(&wr);725}726727/* This is a debugging function that gets called when we detect something728 * wrong with the replication protocol: the goal is to peek into the729 * replication backlog and show a few final bytes to make simpler to730 * guess what kind of bug it could be. */731void showLatestBacklog(void) {732    if (server.repl_backlog == NULL) return;733    if (listLength(server.repl_buffer_blocks) == 0) return;734    if (server.hide_user_data_from_log) {735        serverLog(LL_NOTICE,"hide-user-data-from-log is on, skip logging backlog content to avoid spilling PII.");736        return;737    }738739    size_t dumplen = 256;740    if (server.repl_backlog->histlen < (long long)dumplen)741        dumplen = server.repl_backlog->histlen;742743    sds dump = sdsempty();744    listNode *node = listLast(server.repl_buffer_blocks);745    while(dumplen) {746        if (node == NULL) break;747        replBufBlock *o = listNodeValue(node);748        size_t thislen = o->used >= dumplen ? dumplen : o->used;749        sds head = sdscatrepr(sdsempty(), o->buf+o->used-thislen, thislen);750        sds tmp = sdscatsds(head, dump);751        sdsfree(dump);752        dump = tmp;753        dumplen -= thislen;754        node = listPrevNode(node);755    }756757    /* Finally log such bytes: this is vital debugging info to758     * understand what happened. */759    serverLog(LL_NOTICE,"Latest backlog is: '%s'", dump);760    sdsfree(dump);761}762763/* This function is used in order to proxy what we receive from our master764 * to our sub-slaves. Besides, we also proxy the replication stream from765 * the source node when being in atomic slot migration. */766void replicationFeedStreamFromMasterStream(char *buf, size_t buflen) {767    /* There must be replication backlog if having attached slaves. */768    if (listLength(server.slaves)) serverAssert(server.repl_backlog != NULL);769    if (server.repl_backlog) {770        /* Must install write handler for all replicas first before feeding771         * replication stream. */772        prepareReplicasToWrite();773        feedReplicationBuffer(buf,buflen);774    } else if (server.masterhost == NULL && server.aof_enabled) {775        /* We increment the repl_offset anyway, since we use that for tracking776         * AOF fsyncs even when there's no replication active. This code will777         * not be reached if AOF is also disabled.778         *779         * As we skip feeding the replication buffer in atomic slot migration,780         * so here we need to update the replication offset manually. */781        server.master_repl_offset += 1;782    }783}784785void replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv, int argc) {786    /* Fast path to return if the monitors list is empty or the server is in loading. */787    if (monitors == NULL || listLength(monitors) == 0 || server.loading) return;788    listNode *ln;789    listIter li;790    int j;791    sds cmdrepr = sdsnew("+");792    robj *cmdobj;793    struct timeval tv;794795    gettimeofday(&tv,NULL);796    cmdrepr = sdscatprintf(cmdrepr,"%ld.%06ld ",(long)tv.tv_sec,(long)tv.tv_usec);797    if (c->flags & CLIENT_SCRIPT) {798        cmdrepr = sdscatprintf(cmdrepr,"[%d lua] ",dictid);799    } else if (c->flags & CLIENT_UNIX_SOCKET) {800        cmdrepr = sdscatprintf(cmdrepr,"[%d unix:%s] ",dictid,server.unixsocket);801    } else {802        cmdrepr = sdscatprintf(cmdrepr,"[%d %s] ",dictid,getClientPeerId(c));803    }804805    for (j = 0; j < argc; j++) {806        if (argv[j]->encoding == OBJ_ENCODING_INT) {807            cmdrepr = sdscatprintf(cmdrepr, "\"%ld\"", (long)argv[j]->ptr);808        } else {809            cmdrepr = sdscatrepr(cmdrepr,(char*)argv[j]->ptr,810                        sdslen(argv[j]->ptr));811        }812        if (j != argc-1)813            cmdrepr = sdscatlen(cmdrepr," ",1);814    }815    cmdrepr = sdscatlen(cmdrepr,"\r\n",2);816    cmdobj = createObject(OBJ_STRING,cmdrepr);817818    listRewind(monitors,&li);819    while((ln = listNext(&li))) {820        client *monitor = ln->value;821        /* Do not show internal commands to non-internal clients. */822        if (c->realcmd && (c->realcmd->flags & CMD_INTERNAL) && !(monitor->flags & CLIENT_INTERNAL)) {823            continue;824        }825        addReply(monitor,cmdobj);826        updateClientMemUsageAndBucket(monitor);827    }828    decrRefCount(cmdobj);829}830831/* Feed the slave 'c' with the replication backlog starting from the832 * specified 'offset' up to the end of the backlog. */833long long addReplyReplicationBacklog(client *c, long long offset) {834    serverAssert(c->running_tid == IOTHREAD_MAIN_THREAD_ID);835836    long long skip;837838    serverLog(LL_DEBUG, "[PSYNC] Replica request offset: %lld", offset);839840    if (server.repl_backlog->histlen == 0) {841        serverLog(LL_DEBUG, "[PSYNC] Backlog history len is zero");842        return 0;843    }844845    serverLog(LL_DEBUG, "[PSYNC] Backlog size: %lld",846             server.repl_backlog_size);847    serverLog(LL_DEBUG, "[PSYNC] First byte: %lld",848             server.repl_backlog->offset);849    serverLog(LL_DEBUG, "[PSYNC] History len: %lld",850             server.repl_backlog->histlen);851852    /* Compute the amount of bytes we need to discard. */853    skip = offset - server.repl_backlog->offset;854    serverLog(LL_DEBUG, "[PSYNC] Skipping: %lld", skip);855856    /* Iterate recorded blocks, quickly search the approximate node. */857    listNode *node = NULL;858    if (raxSize(server.repl_backlog->blocks_index) > 0) {859        uint64_t encoded_offset = htonu64(offset);860        raxIterator ri;861        raxStart(&ri, server.repl_backlog->blocks_index);862        raxSeek(&ri, ">", (unsigned char*)&encoded_offset, sizeof(uint64_t));863        if (raxEOF(&ri)) {864            /* No found, so search from the last recorded node. */865            raxSeek(&ri, "$", NULL, 0);866            raxPrev(&ri);867            node = (listNode *)ri.data;868        } else {869            raxPrev(&ri); /* Skip the sought node. */870            /* We should search from the prev node since the offset of current871             * sought node exceeds searching offset. */872            if (raxPrev(&ri))873                node = (listNode *)ri.data;874            else875                node = server.repl_backlog->ref_repl_buf_node;876        }877        raxStop(&ri);878    } else {879        /* No recorded blocks, just from the start node to search. */880        node = server.repl_backlog->ref_repl_buf_node;881    }882883    /* Search the exact node. */884    while (node != NULL) {885        replBufBlock *o = listNodeValue(node);886        if (o->repl_offset + (long long)o->used >= offset) break;887        node = listNextNode(node);888    }889    serverAssert(node != NULL);890891    /* Install a writer handler first.*/892    prepareClientToWrite(c);893    /* Setting output buffer of the replica. */894    replBufBlock *o = listNodeValue(node);895    o->refcount++;896    c->ref_repl_buf_node = node;897    c->ref_block_pos = offset - o->repl_offset;898899    return server.repl_backlog->histlen - skip;900}901902/* Return the offset to provide as reply to the PSYNC command received903 * from the slave. The returned value is only valid immediately after904 * the BGSAVE process started and before executing any other command905 * from clients. */906long long getPsyncInitialOffset(void) {907    return server.master_repl_offset;908}909910/* Send a FULLRESYNC reply in the specific case of a full resynchronization,911 * as a side effect setup the slave for a full sync in different ways:912 *913 * 1) Remember, into the slave client structure, the replication offset914 *    we sent here, so that if new slaves will later attach to the same915 *    background RDB saving process (by duplicating this client output916 *    buffer), we can get the right offset from this slave.917 * 2) Set the replication state of the slave to WAIT_BGSAVE_END so that918 *    we start accumulating differences from this point.919 * 3) Force the replication stream to re-emit a SELECT statement so920 *    the new slave incremental differences will start selecting the921 *    right database number.922 *923 * Normally this function should be called immediately after a successful924 * BGSAVE for replication was started, or when there is one already in925 * progress that we attached our slave to. */926int replicationSetupSlaveForFullResync(client *slave, long long offset) {927    char buf[128];928    int buflen;929930    slave->psync_initial_offset = offset;931    slave->replstate = SLAVE_STATE_WAIT_BGSAVE_END;932    /* We are going to accumulate the incremental changes for this933     * slave as well. Set slaveseldb to -1 in order to force to re-emit934     * a SELECT statement in the replication stream. */935    server.slaveseldb = -1;936937    /* Slots snapshot. */938    if (slave->flags & CLIENT_REPL_RDB_CHANNEL &&939        slave->slave_req & SLAVE_REQ_SLOTS_SNAPSHOT)940    {941        /* Start to deliver the commands stream on migrating slots. */942        asmSlotSnapshotAndStreamStart(slave->task);943944        buflen = snprintf(buf, sizeof(buf), "+SLOTSSNAPSHOT\r\n");945        if (connWrite(slave->conn, buf, buflen) != buflen) {946            freeClientAsync(slave);947            return C_ERR;948        }949        return C_OK;950    }951952    /* Don't send this reply to slaves that approached us with953     * the old SYNC command. */954    if (!(slave->flags & CLIENT_PRE_PSYNC)) {955        if (slave->flags & CLIENT_REPL_RDB_CHANNEL) {956            /* This slave is rdbchannel. Find its associated main channel and957             * change its state so we can deliver replication stream from now958             * on, in parallel to rdb. */959            uint64_t id = slave->main_ch_client_id;960            client *c = lookupClientByID(id);961            if (c && c->replstate == SLAVE_STATE_WAIT_RDB_CHANNEL) {962                c->replstate = SLAVE_STATE_SEND_BULK_AND_STREAM;963                serverLog(LL_NOTICE, "Starting to deliver RDB and replication stream to replica: %s",964                          replicationGetSlaveName(c));965            } else {966                serverLog(LL_WARNING, "Starting to deliver RDB to replica %s"967                                      " but it has no associated main channel",968                                      replicationGetSlaveName(slave));969            }970        }971        buflen = snprintf(buf,sizeof(buf),"+FULLRESYNC %s %lld\r\n",972                          server.replid,offset);973        if (connWrite(slave->conn,buf,buflen) != buflen) {974            freeClientAsync(slave);975            return C_ERR;976        }977    }978    return C_OK;979}980981/* This function handles the PSYNC command from the point of view of a982 * master receiving a request for partial resynchronization.983 *984 * On success return C_OK, otherwise C_ERR is returned and we proceed985 * with the usual full resync. */986int masterTryPartialResynchronization(client *c, long long psync_offset) {987    long long psync_len;988    char *master_replid = c->argv[1]->ptr;989    char buf[128];990    int buflen;991992    /* Is the replication ID of this master the same advertised by the wannabe993     * slave via PSYNC? If the replication ID changed this master has a994     * different replication history, and there is no way to continue.995     *996     * Note that there are two potentially valid replication IDs: the ID1997     * and the ID2. The ID2 however is only valid up to a specific offset. */998    if (strcasecmp(master_replid, server.replid) &&999        (strcasecmp(master_replid, server.replid2) ||1000         psync_offset > server.second_replid_offset))1001    {1002        /* Replid "?" is used by slaves that want to force a full resync. */1003        if (master_replid[0] != '?') {1004            if (strcasecmp(master_replid, server.replid) &&1005                strcasecmp(master_replid, server.replid2))1006            {1007                serverLog(LL_NOTICE,"Partial resynchronization not accepted: "1008                    "Replication ID mismatch (Replica asked for '%s', my "1009                    "replication IDs are '%s' and '%s')",1010                    master_replid, server.replid, server.replid2);1011            } else {1012                serverLog(LL_NOTICE,"Partial resynchronization not accepted: "1013                    "Requested offset for second ID was %lld, but I can reply "1014                    "up to %lld", psync_offset, server.second_replid_offset);1015            }1016        } else {1017            serverLog(LL_NOTICE,"Full resync requested by replica %s %s",1018                replicationGetSlaveName(c),1019                c->flags & CLIENT_REPL_RDB_CHANNEL ? "(rdb-channel)" : "");1020        }1021        goto need_full_resync;1022    }10231024    /* We still have the data our slave is asking for? */1025    if (!server.repl_backlog ||1026        psync_offset < server.repl_backlog->offset ||1027        psync_offset > (server.repl_backlog->offset + server.repl_backlog->histlen))1028    {1029        serverLog(LL_NOTICE,1030            "Unable to partial resync with replica %s for lack of backlog (Replica request was: %lld).", replicationGetSlaveName(c), psync_offset);1031        if (psync_offset > server.master_repl_offset) {1032            serverLog(LL_WARNING,1033                "Warning: replica %s tried to PSYNC with an offset that is greater than the master replication offset.", replicationGetSlaveName(c));1034        }1035        goto need_full_resync;1036    }10371038    /* If we reached this point, we are able to perform a partial resync:1039     * 1) Set client state to make it a slave.1040     * 2) Inform the client we can continue with +CONTINUE1041     * 3) Send the backlog data (from the offset to the end) to the slave. */1042    c->flags |= CLIENT_SLAVE;1043    c->replstate = SLAVE_STATE_ONLINE;1044    c->repl_ack_time = server.unixtime;1045    c->repl_start_cmd_stream_on_ack = 0;1046    listAddNodeTail(server.slaves,c);1047    /* We can't use the connection buffers since they are used to accumulate1048     * new commands at this stage. But we are sure the socket send buffer is1049     * empty so this write will never fail actually. */1050    if (c->slave_capa & SLAVE_CAPA_PSYNC2) {1051        buflen = snprintf(buf,sizeof(buf),"+CONTINUE %s\r\n", server.replid);1052    } else {1053        buflen = snprintf(buf,sizeof(buf),"+CONTINUE\r\n");1054    }1055    if (connWrite(c->conn,buf,buflen) != buflen) {1056        freeClientAsync(c);1057        return C_OK;1058    }1059    psync_len = addReplyReplicationBacklog(c,psync_offset);1060    serverLog(LL_NOTICE,1061        "Partial resynchronization request from %s accepted. Sending %lld bytes of backlog starting from offset %lld.",1062            replicationGetSlaveName(c),1063            psync_len, psync_offset);1064    /* Note that we don't need to set the selected DB at server.slaveseldb1065     * to -1 to force the master to emit SELECT, since the slave already1066     * has this state from the previous connection with the master. */10671068    refreshGoodSlavesCount();10691070    /* Fire the replica change modules event. */1071    moduleFireServerEvent(REDISMODULE_EVENT_REPLICA_CHANGE,1072                          REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE,1073                          NULL);10741075    return C_OK; /* The caller can return, no full resync needed. */10761077need_full_resync:1078    /* We need a full resync for some reason... Note that we can't1079     * reply to PSYNC right now if a full SYNC is needed. The reply1080     * must include the master offset at the time the RDB file we transfer1081     * is generated, so we need to delay the reply to that moment. */1082    return C_ERR;1083}10841085/* Start a BGSAVE for replication goals, which is, selecting the disk or1086 * socket target depending on the configuration, and making sure that1087 * the script cache is flushed before to start.1088 *1089 * The mincapa argument is the bitwise AND among all the slaves capabilities1090 * of the slaves waiting for this BGSAVE, so represents the slave capabilities1091 * all the slaves support. Can be tested via SLAVE_CAPA_* macros.1092 *1093 * Side effects, other than starting a BGSAVE:1094 *1095 * 1) Handle the slaves in WAIT_START state, by preparing them for a full1096 *    sync if the BGSAVE was successfully started, or sending them an error1097 *    and dropping them from the list of slaves.1098 *1099 * 2) Flush the Lua scripting script cache if the BGSAVE was actually1100 *    started.1101 *1102 * Returns C_OK on success or C_ERR otherwise. */1103int startBgsaveForReplication(int mincapa, int req) {1104    int retval;1105    int socket_target = 0;1106    listIter li;1107    listNode *ln;11081109    /* We use a socket target if slave can handle the EOF marker and we're configured to do diskless syncs.1110     * Note that in case we're creating a "filtered" RDB (functions-only, for example) we also force socket replication1111     * to avoid overwriting the snapshot RDB file with filtered data. */1112    socket_target = (server.repl_diskless_sync || req & SLAVE_REQ_RDB_MASK) && (mincapa & SLAVE_CAPA_EOF);1113    /* `SYNC` should have failed with error if we don't support socket and require a filter, assert this here */1114    serverAssert(socket_target || !(req & SLAVE_REQ_RDB_MASK));11151116    int slots_req = req & SLAVE_REQ_SLOTS_SNAPSHOT;1117    serverLog(LL_NOTICE,"Starting BGSAVE for SYNC with target: %s%s",1118        socket_target ? (slots_req ? "slot migration destination socket" : "replicas sockets") : "disk",1119        (req & SLAVE_REQ_RDB_CHANNEL) ? " (rdb-channel)" : "");11201121    rdbSaveInfo rsi, *rsiptr;1122    rsiptr = rdbPopulateSaveInfo(&rsi);1123    /* Only do rdbSave* when rsiptr is not NULL,1124     * otherwise slave will miss repl-stream-db. */1125    if (rsiptr) {1126        if (socket_target)1127            retval = rdbSaveToSlavesSockets(req,rsiptr);1128        else {1129            /* Keep the page cache since it'll get used soon */1130            retval = rdbSaveBackground(req, server.rdb_filename, rsiptr, RDBFLAGS_REPLICATION | RDBFLAGS_KEEP_CACHE);1131        }1132        if (server.repl_debug_pause & REPL_DEBUG_AFTER_FORK)1133            debugPauseProcess();1134    } else {1135        serverLog(LL_WARNING,"BGSAVE for replication: replication information not available, can't generate the RDB file right now. Try later.");1136        retval = C_ERR;1137    }11381139    /* If we succeeded to start a BGSAVE with disk target, let's remember1140     * this fact, so that we can later delete the file if needed. Note1141     * that we don't set the flag to 1 if the feature is disabled, otherwise1142     * it would never be cleared: the file is not deleted. This way if1143     * the user enables it later with CONFIG SET, we are fine. */1144    if (retval == C_OK && !socket_target && server.rdb_del_sync_files)1145        RDBGeneratedByReplication = 1;11461147    /* If we failed to BGSAVE, remove the slaves waiting for a full1148     * resynchronization from the list of slaves, inform them with1149     * an error about what happened, close the connection ASAP. */1150    if (retval == C_ERR) {1151        serverLog(LL_WARNING,"BGSAVE for replication failed");1152        listRewind(server.slaves,&li);1153        while((ln = listNext(&li))) {1154            client *slave = ln->value;11551156            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {1157                slave->replstate = REPL_STATE_NONE;1158                slave->flags &= ~CLIENT_SLAVE;1159                listDelNode(server.slaves,ln);1160                addReplyError(slave,1161                    "BGSAVE failed, replication can't continue");1162                slave->flags |= CLIENT_CLOSE_AFTER_REPLY;1163            }1164        }1165        return retval;1166    }11671168    /* If the target is socket, rdbSaveToSlavesSockets() already setup1169     * the slaves for a full resync. Otherwise for disk target do it now.*/1170    if (!socket_target) {1171        listRewind(server.slaves,&li);1172        while((ln = listNext(&li))) {1173            client *slave = ln->value;11741175            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) {1176                /* Check slave has the exact requirements */1177                if (slave->slave_req != req)1178                    continue;1179                replicationSetupSlaveForFullResync(slave, getPsyncInitialOffset());1180            }1181        }1182    }11831184    return retval;1185}11861187/* SYNC and PSYNC command implementation. */1188void syncCommand(client *c) {1189    /* ignore SYNC if already slave or in monitor mode */1190    if (c->flags & CLIENT_SLAVE) return;11911192    /* Check if this is a failover request to a replica with the same replid and1193     * become a master if so. */1194    if (c->argc > 3 && !strcasecmp(c->argv[0]->ptr,"psync") && 1195        !strcasecmp(c->argv[3]->ptr,"failover"))1196    {1197        serverLog(LL_NOTICE, "Failover request received for replid %s.",1198            (unsigned char *)c->argv[1]->ptr);1199        if (!server.masterhost) {1200            addReplyError(c, "PSYNC FAILOVER can't be sent to a master.");1201            return;1202        }12031204        if (!strcasecmp(c->argv[1]->ptr,server.replid)) {1205            if (server.cluster_enabled) {1206                clusterPromoteSelfToMaster();1207            } else {1208                replicationUnsetMaster();1209            }1210            sds client = catClientInfoString(sdsempty(),c);1211            serverLog(LL_NOTICE,1212                "MASTER MODE enabled (failover request from '%s')",client);1213            sdsfree(client);1214        } else {1215            addReplyError(c, "PSYNC FAILOVER replid must match my replid.");1216            return;            1217        }1218    }12191220    /* Don't let replicas sync with us while we're failing over */1221    if (server.failover_state != NO_FAILOVER) {1222        addReplyError(c,"-NOMASTERLINK Can't SYNC while failing over");1223        return;1224    }12251226    /* Refuse SYNC requests if we are a slave but the link with our master1227     * is not ok... */1228    if (server.masterhost && server.repl_state != REPL_STATE_CONNECTED) {1229        addReplyError(c,"-NOMASTERLINK Can't SYNC while not connected with my master");1230        return;1231    }12321233    /* SYNC can't be issued when the server has pending data to send to1234     * the client about already issued commands. We need a fresh reply1235     * buffer registering the differences between the BGSAVE and the current1236     * dataset, so that we can copy to other slaves if needed. */1237    if (clientHasPendingReplies(c)) {1238        addReplyError(c,"SYNC and PSYNC are invalid with pending output");1239        return;1240    }12411242    /* Fail sync if slave doesn't support EOF capability but wants a filtered RDB. This is because we force filtered1243     * RDB's to be generated over a socket and not through a file to avoid conflicts with the snapshot files. Forcing1244     * use of a socket is handled, if needed, in `startBgsaveForReplication`. */1245    if (c->slave_req & SLAVE_REQ_RDB_MASK && !(c->slave_capa & SLAVE_CAPA_EOF)) {1246        addReplyError(c,"Filtered replica requires EOF capability");1247        return;1248    }12491250    serverLog(LL_NOTICE,"Replica %s asks for synchronization",1251        replicationGetSlaveName(c));12521253    /* Try a partial resynchronization if this is a PSYNC command.1254     * If it fails, we continue with usual full resynchronization, however1255     * when this happens replicationSetupSlaveForFullResync will replied1256     * with:1257     *1258     * +FULLRESYNC <replid> <offset>1259     *1260     * So the slave knows the new replid and offset to try a PSYNC later1261     * if the connection with the master is lost. */1262    if (!strcasecmp(c->argv[0]->ptr,"psync")) {1263        long long psync_offset;1264        if (getLongLongFromObjectOrReply(c, c->argv[2], &psync_offset, NULL) != C_OK) {1265            serverLog(LL_WARNING, "Replica %s asks for synchronization but with a wrong offset",1266                      replicationGetSlaveName(c));1267            return;1268        }12691270        if (masterTryPartialResynchronization(c, psync_offset) == C_OK) {1271            server.stat_sync_partial_ok++;1272            return; /* No full resync needed, return. */1273        } else {1274            char *master_replid = c->argv[1]->ptr;12751276            /* Increment stats for failed PSYNCs, but only if the1277             * replid is not "?", as this is used by slaves to force a full1278             * resync on purpose when they are not able to partially1279             * resync. */1280            if (master_replid[0] != '?') server.stat_sync_partial_err++;1281            if (c->slave_capa & SLAVE_CAPA_RDB_CHANNEL_REPL) {1282                int len;1283                char buf[128];1284                /* Replica is capable of rdbchannel replication. This is1285                 * replica's main channel. Let replica know full sync is needed.1286                 * Replica will open another connection (rdbchannel). Once rdb1287                 * delivery starts, we'll stream repl data to the main channel.*/1288                c->flags |= CLIENT_SLAVE;1289                c->replstate = SLAVE_STATE_WAIT_RDB_CHANNEL;1290                c->repl_ack_time = server.unixtime;1291                listAddNodeTail(server.slaves, c);1292                createReplicationBacklogIfNeeded();12931294                serverLog(LL_NOTICE,1295                          "Replica %s is capable of rdb channel synchronization, and partial sync isn't possible. "1296                          "Full sync will continue with dedicated rdb channel.",1297                          replicationGetSlaveName(c));12981299                /* Send +RDBCHANNELSYNC with client id so we can associate replica connections on master.*/1300                len = snprintf(buf, sizeof(buf), "+RDBCHANNELSYNC %llu\r\n",1301                               (unsigned long long) c->id);1302                if (connWrite(c->conn, buf, strlen(buf)) != len)1303                    freeClientAsync(c);13041305                return;1306            }1307        }1308    } else {1309        /* If a slave uses SYNC, we are dealing with an old implementation1310         * of the replication protocol (like redis-cli --slave). Flag the client1311         * so that we don't expect to receive REPLCONF ACK feedbacks. */1312        c->flags |= CLIENT_PRE_PSYNC;1313    }13141315    /* Full resynchronization. */1316    server.stat_sync_full++;13171318    /* Setup the slave as one waiting for BGSAVE to start. The following code1319     * paths will change the state if we handle the slave differently. */1320    c->replstate = SLAVE_STATE_WAIT_BGSAVE_START;1321    if (server.repl_disable_tcp_nodelay)1322        connDisableTcpNoDelay(c->conn); /* Non critical if it fails. */1323    c->repldbfd = -1;1324    c->flags |= CLIENT_SLAVE;1325    listAddNodeTail(server.slaves,c);13261327    /* Create the replication backlog if needed. */1328    createReplicationBacklogIfNeeded();13291330    /* Keep the client in the main thread to avoid data races between the1331     * connWrite call in startBgsaveForReplication and the client's event1332     * handler in IO threads. */1333    if (c->tid != IOTHREAD_MAIN_THREAD_ID) keepClientInMainThread(c);13341335    /* CASE 1: BGSAVE is in progress, with disk target. */1336    if (server.child_type == CHILD_TYPE_RDB &&1337        server.rdb_child_type == RDB_CHILD_TYPE_DISK)1338    {1339        /* Ok a background save is in progress. Let's check if it is a good1340         * one for replication, i.e. if there is another slave that is1341         * registering differences since the server forked to save. */1342        client *slave;1343        listNode *ln;1344        listIter li;13451346        listRewind(server.slaves,&li);1347        while((ln = listNext(&li))) {1348            slave = ln->value;1349            /* If the client needs a buffer of commands, we can't use1350             * a replica without replication buffer. */1351            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END &&1352                (!(slave->flags & CLIENT_REPL_RDBONLY) ||1353                 (c->flags & CLIENT_REPL_RDBONLY)))1354                break;1355        }1356        /* To attach this slave, we check that it has at least all the1357         * capabilities of the slave that triggered the current BGSAVE1358         * and its exact requirements. */1359        if (ln && ((c->slave_capa & slave->slave_capa) == slave->slave_capa) &&1360            c->slave_req == slave->slave_req) {1361            /* Perfect, the server is already registering differences for1362             * another slave. Set the right state, and copy the buffer.1363             * We don't copy buffer if clients don't want. */1364            if (!(c->flags & CLIENT_REPL_RDBONLY))1365                copyReplicaOutputBuffer(c,slave);1366            replicationSetupSlaveForFullResync(c,slave->psync_initial_offset);1367            serverLog(LL_NOTICE,"Waiting for end of BGSAVE for SYNC");1368        } else {1369            /* No way, we need to wait for the next BGSAVE in order to1370             * register differences. */1371            serverLog(LL_NOTICE,"Can't attach the replica to the current BGSAVE. Waiting for next BGSAVE for SYNC");1372        }13731374    /* CASE 2: BGSAVE is in progress, with socket target. */1375    } else if (server.child_type == CHILD_TYPE_RDB &&1376               server.rdb_child_type == RDB_CHILD_TYPE_SOCKET)1377    {1378        /* There is an RDB child process but it is writing directly to1379         * children sockets. We need to wait for the next BGSAVE1380         * in order to synchronize. */1381        serverLog(LL_NOTICE,"Current BGSAVE has socket target. Waiting for next BGSAVE for SYNC");13821383    /* CASE 3: There is no BGSAVE is in progress. */1384    } else {1385        if (server.repl_diskless_sync && (c->slave_capa & SLAVE_CAPA_EOF) &&1386            server.repl_diskless_sync_delay)1387        {1388            /* Diskless replication RDB child is created inside1389             * replicationCron() since we want to delay its start a1390             * few seconds to wait for more slaves to arrive. */1391            serverLog(LL_NOTICE,"Delay next BGSAVE for diskless SYNC");1392        } else {1393            /* We don't have a BGSAVE in progress, let's start one. Diskless1394             * or disk-based mode is determined by replica's capacity. */1395            if (!hasActiveChildProcess()) {1396                startBgsaveForReplication(c->slave_capa, c->slave_req);1397            } else {1398                serverLog(LL_NOTICE,1399                    "No BGSAVE in progress, but another BG operation is active. "1400                    "BGSAVE for replication delayed");1401            }1402        }1403    }1404    return;1405}14061407/* REPLCONF <option> <value> <option> <value> ...1408 * This command is used by a replica in order to configure the replication1409 * process before starting it with the SYNC command.1410 * This command is also used by a master in order to get the replication1411 * offset from a replica.1412 *1413 * Currently we support these options:1414 *1415 * - listening-port <port>1416 * - ip-address <ip>1417 * What is the listening ip and port of the Replica redis instance, so that1418 * the master can accurately lists replicas and their listening ports in the1419 * INFO output.1420 *1421 * - capa <eof|psync2|rdb-channel-repl>1422 * What is the capabilities of this instance.1423 * eof: supports EOF-style RDB transfer for diskless replication.1424 * psync2: supports PSYNC v2, so understands +CONTINUE <new repl ID>.1425 *1426 * - ack <offset> [fack <aofofs>]1427 * Replica informs the master the amount of replication stream that it1428 * processed so far, and optionally the replication offset fsynced to the AOF file.1429 * This special pattern doesn't reply to the caller.1430 *1431 * - getack <dummy>1432 * Unlike other subcommands, this is used by master to get the replication1433 * offset from a replica.1434 *1435 * - rdb-only <0|1>1436 * Only wants RDB snapshot without replication buffer.1437 *1438 * - rdb-filter-only <include-filters>1439 * Define "include" filters for the RDB snapshot. Currently we only support1440 * a single include filter: "functions". Passing an empty string "" will1441 * result in an empty RDB.1442 *1443 * - main-ch-client-id <client-id>1444 * Replica's main channel informs master that this is the main channel of the1445 * rdb channel identified by the client-id. */1446void replconfCommand(client *c) {1447    int j;14481449    if ((c->argc % 2) == 0) {1450        /* Number of arguments must be odd to make sure that every1451         * option has a corresponding value. */1452        addReplyErrorObject(c,shared.syntaxerr);1453        return;1454    }14551456    /* Process every option-value pair. */1457    for (j = 1; j < c->argc; j+=2) {1458        if (!strcasecmp(c->argv[j]->ptr,"listening-port")) {1459            long port;14601461            if ((getLongFromObjectOrReply(c,c->argv[j+1],1462                    &port,NULL) != C_OK))1463                return;1464            c->slave_listening_port = port;1465        } else if (!strcasecmp(c->argv[j]->ptr,"ip-address")) {1466            sds addr = c->argv[j+1]->ptr;1467            if (sdslen(addr) < NET_HOST_STR_LEN) {1468                if (c->slave_addr) sdsfree(c->slave_addr);1469                c->slave_addr = sdsdup(addr);1470            } else {1471                addReplyErrorFormat(c,"REPLCONF ip-address provided by "1472                    "replica instance is too long: %zd bytes", sdslen(addr));1473                return;1474            }1475        } else if (!strcasecmp(c->argv[j]->ptr,"capa")) {1476            /* Ignore capabilities not understood by this master. */1477            if (!strcasecmp(c->argv[j+1]->ptr,"eof"))1478                c->slave_capa |= SLAVE_CAPA_EOF;1479            else if (!strcasecmp(c->argv[j+1]->ptr,"psync2"))1480                c->slave_capa |= SLAVE_CAPA_PSYNC2;1481            else if (!strcasecmp(c->argv[j+1]->ptr,"rdb-channel-repl") && server.repl_rdb_channel &&1482                     server.repl_diskless_sync) {1483                c->slave_capa |= SLAVE_CAPA_RDB_CHANNEL_REPL;1484            }1485        } else if (!strcasecmp(c->argv[j]->ptr,"ack")) {1486            /* REPLCONF ACK is used by slave to inform the master the amount1487             * of replication stream that it processed so far. It is an1488             * internal only command that normal clients should never use. */1489            long long offset;14901491            if (!(c->flags & CLIENT_SLAVE)) return;1492            if ((getLongLongFromObject(c->argv[j+1], &offset) != C_OK))1493                return;1494            if (offset > c->repl_ack_off)1495                c->repl_ack_off = offset;1496            if (c->argc > j+3 && !strcasecmp(c->argv[j+2]->ptr,"fack")) {1497                if ((getLongLongFromObject(c->argv[j+3], &offset) != C_OK))1498                    return;1499                if (offset > c->repl_aof_off)1500                    c->repl_aof_off = offset;1501            }1502            c->repl_ack_time = server.unixtime;1503            /* If this was a diskless replication, we need to really put1504             * the slave online when the first ACK is received (which1505             * confirms slave is online and ready to get more data). This1506             * allows for simpler and less CPU intensive EOF detection1507             * when streaming RDB files.1508             * There's a chance the ACK got to us before we detected that the1509             * bgsave is done (since that depends on cron ticks), so run a1510             * quick check first (instead of waiting for the next ACK. */1511            if (server.child_type == CHILD_TYPE_RDB && c->replstate == SLAVE_STATE_WAIT_BGSAVE_END)1512                checkChildrenDone();1513            if (c->repl_start_cmd_stream_on_ack && c->replstate == SLAVE_STATE_ONLINE)1514                replicaStartCommandStream(c);1515            /* If state is send_bulk_and_stream, it means this is the main1516             * channel of the slave in rdbchannel replication. Normally, slave1517             * will be put online after rdb fork is completed. There is chance1518             * that 'ack' might be received before we detect bgsave is done. */1519            if (c->replstate == SLAVE_STATE_SEND_BULK_AND_STREAM)1520                replicaPutOnline(c);1521            /* Note: this command does not reply anything! */1522            return;1523        } else if (!strcasecmp(c->argv[j]->ptr,"getack")) {1524            /* REPLCONF GETACK is used in order to request an ACK ASAP1525             * to the slave. */1526            if (server.masterhost && server.master) replicationSendAck();1527            return;1528        } else if (!strcasecmp(c->argv[j]->ptr,"rdb-only")) {1529           /* REPLCONF RDB-ONLY is used to identify the client only wants1530            * RDB snapshot without replication buffer. */1531            long rdb_only = 0;1532            if (getRangeLongFromObjectOrReply(c,c->argv[j+1],1533                    0,1,&rdb_only,NULL) != C_OK)1534                return;1535            if (rdb_only == 1) {1536                c->flags |= CLIENT_REPL_RDBONLY;1537                /* If replicas ask for RDB only, We can apply the background1538                 * RDB transfer optimization based on the configurations. */1539                if (server.repl_rdb_channel && server.repl_diskless_sync)1540                    c->slave_req |= SLAVE_REQ_RDB_CHANNEL;1541            } else {1542                c->flags &= ~CLIENT_REPL_RDBONLY;1543                c->slave_req &= ~SLAVE_REQ_RDB_CHANNEL;1544            }1545        } else if (!strcasecmp(c->argv[j]->ptr,"rdb-filter-only")) {1546            /* REPLCONFG RDB-FILTER-ONLY is used to define "include" filters1547             * for the RDB snapshot. Currently we only support a single1548             * include filter: "functions". In the future we may want to add1549             * other filters like key patterns, key types, non-volatile, module1550             * aux fields, ...1551             * We might want to add the complementing "RDB-FILTER-EXCLUDE" to1552             * filter out certain data. */1553            int filter_count, i;1554            sds *filters;1555            if (!(filters = sdssplitargs(c->argv[j+1]->ptr, &filter_count))) {1556                addReplyError(c, "Missing rdb-filter-only values");1557                return;1558            }1559            /* By default filter out all parts of the rdb */1560            c->slave_req |= SLAVE_REQ_RDB_EXCLUDE_DATA;1561            c->slave_req |= SLAVE_REQ_RDB_EXCLUDE_FUNCTIONS;1562            for (i = 0; i < filter_count; i++) {1563                if (!strcasecmp(filters[i], "functions"))1564                    c->slave_req &= ~SLAVE_REQ_RDB_EXCLUDE_FUNCTIONS;1565                else {1566                    addReplyErrorFormat(c, "Unsupported rdb-filter-only option: %s", (char*)filters[i]);1567                    sdsfreesplitres(filters, filter_count);1568                    return;1569                }1570            }1571            sdsfreesplitres(filters, filter_count);1572        } else if (!strcasecmp(c->argv[j]->ptr, "rdb-channel")) {1573            long rdb_channel = 0;1574            if (getRangeLongFromObjectOrReply(c, c->argv[j + 1], 0, 1, &rdb_channel, NULL) != C_OK)1575                return;1576            if (rdb_channel == 1) {1577                c->flags |= CLIENT_REPL_RDB_CHANNEL;1578            } else {1579                c->flags &= ~CLIENT_REPL_RDB_CHANNEL;1580            }1581        } else if (!strcasecmp(c->argv[j]->ptr, "main-ch-client-id")) {1582            /* REPLCONF main-ch-client-id <client-id> is used to identify1583             * the current replica rdb channel with existing main channel1584             * connection. */1585            long long client_id = 0;1586            client *main_ch;1587            if (getLongLongFromObjectOrReply(c, c->argv[j + 1], &client_id, NULL) != C_OK)1588                return;1589            main_ch = lookupClientByID(client_id);1590            if (!main_ch || main_ch->replstate != SLAVE_STATE_WAIT_RDB_CHANNEL) {1591                addReplyErrorFormat(c, "Unrecognized RDB client id: %lld", client_id);1592                return;1593            }1594            c->main_ch_client_id = (uint64_t)client_id;1595            /* Inherit the rdb-no-compress and rdb-no-checksum request from the main channel. */1596            if (main_ch->slave_req & SLAVE_REQ_RDB_NO_COMPRESS)1597                c->slave_req |= SLAVE_REQ_RDB_NO_COMPRESS;1598            if (main_ch->slave_req & SLAVE_REQ_RDB_NO_CHECKSUM)1599                c->slave_req |= SLAVE_REQ_RDB_NO_CHECKSUM;1600        } else if (!strcasecmp(c->argv[j]->ptr, "rdb-no-compress")) {1601            long rdb_no_compress = 0;1602            if (getRangeLongFromObjectOrReply(c, c->argv[j + 1], 0, 1, &rdb_no_compress, NULL) != C_OK)1603                return;1604            if (rdb_no_compress == 1) {1605                c->slave_req |= SLAVE_REQ_RDB_NO_COMPRESS;1606            } else {1607                c->slave_req &= ~SLAVE_REQ_RDB_NO_COMPRESS;1608            }1609        } else if (!strcasecmp(c->argv[j]->ptr, "rdb-no-checksum")) {1610            long rdb_no_checksum = 0;1611            if (getRangeLongFromObjectOrReply(c, c->argv[j + 1], 0, 1, &rdb_no_checksum, NULL) != C_OK)1612                return;1613            if (rdb_no_checksum == 1) {1614                c->slave_req |= SLAVE_REQ_RDB_NO_CHECKSUM;1615            } else {1616                c->slave_req &= ~SLAVE_REQ_RDB_NO_CHECKSUM;1617            }1618        } else {1619            addReplyErrorFormat(c,"Unrecognized REPLCONF option: %s",1620                (char*)c->argv[j]->ptr);1621            return;1622        }1623    }1624    addReply(c,shared.ok);1625}16261627/* This function puts a replica in the online state, and should be called just1628 * after a replica received the RDB file for the initial synchronization.1629 *1630 * It does a few things:1631 * 1) Put the slave in ONLINE state.1632 * 2) Update the count of "good replicas".1633 * 3) Trigger the module event.1634 *1635 * the return value indicates that the replica should be disconnected.1636 * */1637int replicaPutOnline(client *slave) {1638    if (slave->flags & CLIENT_REPL_RDBONLY) {1639        slave->replstate = SLAVE_STATE_RDB_TRANSMITTED;1640        /* The client asked for RDB only so we should close it ASAP */1641        serverLog(LL_NOTICE,1642                  "RDB transfer completed, rdb only replica (%s) should be disconnected asap",1643                  replicationGetSlaveName(slave));1644        return 0;1645    }16461647    /* Don't put migration destination client online. */1648    if (slave->flags & CLIENT_ASM_MIGRATING) return 0;16491650    slave->replstate = SLAVE_STATE_ONLINE;1651    slave->repl_ack_time = server.unixtime; /* Prevent false timeout. */16521653    refreshGoodSlavesCount();1654    /* Fire the replica change modules event. */1655    moduleFireServerEvent(REDISMODULE_EVENT_REPLICA_CHANGE,1656                          REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE,1657                          NULL);1658    serverLog(LL_NOTICE,"Synchronization with replica %s succeeded",1659        replicationGetSlaveName(slave));1660    return 1;1661}16621663/* This function should be called just after a replica received the RDB file1664 * for the initial synchronization, and we are finally ready to send the1665 * incremental stream of commands.1666 *1667 * It does a few things:1668 * 1) Close the replica's connection async if it doesn't need replication1669 *    commands buffer stream, since it actually isn't a valid replica.1670 * 2) Make sure the writable event is re-installed, since when calling the SYNC1671 *    command we had no replies and it was disabled, and then we could1672 *    accumulate output buffer data without sending it to the replica so it1673 *    won't get mixed with the RDB stream. */1674void replicaStartCommandStream(client *slave) {1675    serverAssert(!(slave->flags & CLIENT_REPL_RDBONLY));1676    slave->repl_start_cmd_stream_on_ack = 0;16771678    putClientInPendingWriteQueue(slave);1679}16801681/* We call this function periodically to remove an RDB file that was1682 * generated because of replication, in an instance that is otherwise1683 * without any persistence. We don't want instances without persistence1684 * to take RDB files around, this violates certain policies in certain1685 * environments. */1686void removeRDBUsedToSyncReplicas(void) {1687    /* If the feature is disabled, return ASAP but also clear the1688     * RDBGeneratedByReplication flag in case it was set. Otherwise if the1689     * feature was enabled, but gets disabled later with CONFIG SET, the1690     * flag may remain set to one: then next time the feature is re-enabled1691     * via CONFIG SET we have it set even if no RDB was generated1692     * because of replication recently. */1693    if (!server.rdb_del_sync_files) {1694        RDBGeneratedByReplication = 0;1695        return;1696    }16971698    if (allPersistenceDisabled() && RDBGeneratedByReplication) {1699        client *slave;1700        listNode *ln;1701        listIter li;17021703        int delrdb = 1;1704        listRewind(server.slaves,&li);1705        while((ln = listNext(&li))) {1706            slave = ln->value;1707            if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START ||1708                slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END ||1709                slave->replstate == SLAVE_STATE_SEND_BULK)1710            {1711                delrdb = 0;1712                break; /* No need to check the other replicas. */1713            }1714        }1715        if (delrdb) {1716            struct stat sb;1717            if (lstat(server.rdb_filename,&sb) != -1) {1718                RDBGeneratedByReplication = 0;1719                serverLog(LL_NOTICE,1720                    "Removing the RDB file used to feed replicas "1721                    "in a persistence-less instance");1722                bg_unlink(server.rdb_filename);1723            }1724        }1725    }1726}17271728/* Close the repldbfd and reclaim the page cache if the client hold1729 * the last reference to replication DB */1730void closeRepldbfd(client *myself) {1731    listNode *ln;1732    listIter li;1733    int reclaim = 1;1734    listRewind(server.slaves,&li);1735    while((ln = listNext(&li))) {1736        client *slave = ln->value;1737        if (slave != myself && slave->replstate == SLAVE_STATE_SEND_BULK) {1738            reclaim = 0;1739            break;1740        }1741    }17421743    if (reclaim) {1744        bioCreateCloseJob(myself->repldbfd, 0, 1);1745    } else {1746        close(myself->repldbfd);1747    }1748    myself->repldbfd = -1;1749}17501751void sendBulkToSlave(connection *conn) {1752    client *slave = connGetPrivateData(conn);1753    char buf[PROTO_IOBUF_LEN];1754    ssize_t nwritten, buflen;17551756    /* Before sending the RDB file, we send the preamble as configured by the1757     * replication process. Currently the preamble is just the bulk count of1758     * the file in the form "$<length>\r\n". */1759    if (slave->replpreamble) {1760        nwritten = connWrite(conn,slave->replpreamble,sdslen(slave->replpreamble));1761        if (nwritten == -1) {1762            serverLog(LL_WARNING,1763                "Write error sending RDB preamble to replica: %s",1764                connGetLastError(conn));1765            freeClient(slave);1766            return;1767        }1768        atomicIncr(server.stat_net_repl_output_bytes, nwritten);1769        sdsrange(slave->replpreamble,nwritten,-1);1770        if (sdslen(slave->replpreamble) == 0) {1771            sdsfree(slave->replpreamble);1772            slave->replpreamble = NULL;1773            /* fall through sending data. */1774        } else {1775            return;1776        }1777    }17781779    /* If the preamble was already transferred, send the RDB bulk data. */1780    if (lseek(slave->repldbfd,slave->repldboff,SEEK_SET) == -1) {1781	serverLog(LL_WARNING,"Failed to lseek the RDB file to offset %lld for replica %s: %s",1782	    (long long)slave->repldboff, replicationGetSlaveName(slave), strerror(errno));1783	freeClient(slave);1784	return;1785    }1786    buflen = read(slave->repldbfd,buf,PROTO_IOBUF_LEN);1787    if (buflen <= 0) {1788        serverLog(LL_WARNING,"Read error sending DB to replica: %s",1789            (buflen == 0) ? "premature EOF" : strerror(errno));1790        freeClient(slave);1791        return;1792    }1793    if ((nwritten = connWrite(conn,buf,buflen)) == -1) {1794        if (connGetState(conn) != CONN_STATE_CONNECTED) {1795            serverLog(LL_WARNING,"Write error sending DB to replica: %s",1796                connGetLastError(conn));1797            freeClient(slave);1798        }1799        return;1800    }1801    slave->repldboff += nwritten;1802    atomicIncr(server.stat_net_repl_output_bytes, nwritten);1803    if (slave->repldboff == slave->repldbsize) {1804        closeRepldbfd(slave);1805        connSetWriteHandler(slave->conn,NULL);1806        if (!replicaPutOnline(slave)) {1807            freeClient(slave);1808            return;1809        }1810        replicaStartCommandStream(slave);1811    }1812}18131814/* Remove one write handler from the list of connections waiting to be writable1815 * during rdb pipe transfer. */1816void rdbPipeWriteHandlerConnRemoved(struct connection *conn) {1817    if (!connHasWriteHandler(conn))1818        return;1819    connSetWriteHandler(conn, NULL);1820    client *slave = connGetPrivateData(conn);1821    slave->repl_last_partial_write = 0;1822    server.rdb_pipe_numconns_writing--;1823    /* if there are no more writes for now for this conn, or write error: */1824    if (server.rdb_pipe_numconns_writing == 0) {1825        if (aeCreateFileEvent(server.el, server.rdb_pipe_read, AE_READABLE, rdbPipeReadHandler,NULL) == AE_ERR) {1826            serverPanic("Unrecoverable error creating server.rdb_pipe_read file event.");1827        }1828    }1829}18301831/* Called in diskless master during transfer of data from the rdb pipe, when1832 * the replica becomes writable again. */1833void rdbPipeWriteHandler(struct connection *conn) {1834    serverAssert(server.rdb_pipe_bufflen>0);1835    client *slave = connGetPrivateData(conn);1836    ssize_t nwritten;1837    if ((nwritten = connWrite(conn, server.rdb_pipe_buff + slave->repldboff,1838                              server.rdb_pipe_bufflen - slave->repldboff)) == -1)1839    {1840        if (connGetState(conn) == CONN_STATE_CONNECTED)1841            return; /* equivalent to EAGAIN */1842        serverLog(LL_WARNING,"Write error sending DB to replica: %s",1843            connGetLastError(conn));1844        freeClient(slave);1845        return;1846    } else {1847        slave->repldboff += nwritten;1848        atomicIncr(server.stat_net_repl_output_bytes, nwritten);1849        if (slave->repldboff < server.rdb_pipe_bufflen) {1850            slave->repl_last_partial_write = server.unixtime;1851            return; /* more data to write.. */1852        }1853    }1854    rdbPipeWriteHandlerConnRemoved(conn);1855}18561857/* Called in diskless master, when there's data to read from the child's rdb pipe */1858void rdbPipeReadHandler(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask) {1859    UNUSED(mask);1860    UNUSED(clientData);1861    UNUSED(eventLoop);1862    int i;1863    if (!server.rdb_pipe_buff)1864        server.rdb_pipe_buff = zmalloc(PROTO_IOBUF_LEN);1865    serverAssert(server.rdb_pipe_numconns_writing==0);18661867    while (1) {1868        server.rdb_pipe_bufflen = read(fd, server.rdb_pipe_buff, PROTO_IOBUF_LEN);1869        if (server.rdb_pipe_bufflen < 0) {1870            if (errno == EAGAIN || errno == EWOULDBLOCK)1871                return;1872            serverLog(LL_WARNING,"Diskless rdb transfer, read error sending DB to replicas: %s", strerror(errno));1873            for (i=0; i < server.rdb_pipe_numconns; i++) {1874                connection *conn = server.rdb_pipe_conns[i];1875                if (!conn)1876                    continue;1877                client *slave = connGetPrivateData(conn);1878                freeClient(slave);1879                server.rdb_pipe_conns[i] = NULL;1880            }1881            killRDBChild();1882            return;1883        }18841885        if (server.rdb_pipe_bufflen == 0) {1886            /* EOF - write end was closed. */1887            int stillUp = 0;1888            aeDeleteFileEvent(server.el, server.rdb_pipe_read, AE_READABLE);1889            for (i=0; i < server.rdb_pipe_numconns; i++)1890            {1891                connection *conn = server.rdb_pipe_conns[i];1892                if (!conn)1893                    continue;1894                stillUp++;1895            }1896            serverLog(LL_NOTICE,"Diskless rdb transfer, done reading from pipe, %d replicas still up.", stillUp);1897            /* Now that the replicas have finished reading, notify the child that it's safe to exit. 1898             * When the server detects the child has exited, it can mark the replica as online, and1899             * start streaming the replication buffers. */1900            close(server.rdb_child_exit_pipe);1901            server.rdb_child_exit_pipe = -1;1902            return;1903        }19041905        int stillAlive = 0;1906        for (i=0; i < server.rdb_pipe_numconns; i++)1907        {1908            ssize_t nwritten;1909            connection *conn = server.rdb_pipe_conns[i];1910            if (!conn)1911                continue;19121913            client *slave = connGetPrivateData(conn);1914            if ((nwritten = connWrite(conn, server.rdb_pipe_buff, server.rdb_pipe_bufflen)) == -1) {1915                if (connGetState(conn) != CONN_STATE_CONNECTED) {1916                    serverLog(LL_WARNING,"Diskless rdb transfer, write error sending DB to replica: %s",1917                        connGetLastError(conn));1918                    freeClient(slave);1919                    server.rdb_pipe_conns[i] = NULL;1920                    continue;1921                }1922                /* An error and still in connected state, is equivalent to EAGAIN */1923                slave->repldboff = 0;1924            } else {1925                /* Note: when use diskless replication, 'repldboff' is the offset1926                 * of 'rdb_pipe_buff' sent rather than the offset of entire RDB. */1927                slave->repldboff = nwritten;1928                atomicIncr(server.stat_net_repl_output_bytes, nwritten);1929            }1930            /* If we were unable to write all the data to one of the replicas,1931             * setup write handler (and disable pipe read handler, below) */1932            if (nwritten != server.rdb_pipe_bufflen) {1933                slave->repl_last_partial_write = server.unixtime;1934                server.rdb_pipe_numconns_writing++;1935                connSetWriteHandler(conn, rdbPipeWriteHandler);1936            }1937            stillAlive++;1938        }19391940        if (stillAlive == 0) {1941            serverLog(LL_WARNING,"Diskless rdb transfer, last replica dropped, killing fork child.");1942            /* Avoid deleting events after killRDBChild as it may trigger new bgsaves for other replicas. */1943            aeDeleteFileEvent(server.el, server.rdb_pipe_read, AE_READABLE); 1944            killRDBChild();1945            break;1946        }1947        /* Remove the pipe read handler if at least one write handler was set. */1948        else if (server.rdb_pipe_numconns_writing) {1949            aeDeleteFileEvent(server.el, server.rdb_pipe_read, AE_READABLE);1950            break;1951        }1952    }1953}19541955/* This function is called at the end of every background saving.1956 *1957 * The argument bgsaveerr is C_OK if the background saving succeeded1958 * otherwise C_ERR is passed to the function.1959 * The 'type' argument is the type of the child that terminated1960 * (if it had a disk or socket target). */1961void updateSlavesWaitingBgsave(int bgsaveerr, int type) {1962    listNode *ln;1963    listIter li;19641965    /* Note: there's a chance we got here from within the REPLCONF ACK command1966     * so we must avoid using freeClient, otherwise we'll crash on our way up. */19671968    listRewind(server.slaves,&li);1969    while((ln = listNext(&li))) {1970        client *slave = ln->value;19711972        /* We can get here via freeClient()->killRDBChild()->checkChildrenDone(). skip disconnected slaves. */1973        if (!slave->conn) continue;19741975        if (slave->replstate == SLAVE_STATE_SEND_BULK_AND_STREAM) {1976            /* This is the main channel of the slave that received the RDB.1977             * Put it online if RDB delivery is successful. */1978            if (bgsaveerr == C_OK) {1979                /* Notify the task that the snapshot bulk delivery is done */1980                if (slave->flags & CLIENT_ASM_MIGRATING)1981                    asmSlotSnapshotSucceed(slave->task);1982                replicaPutOnline(slave);1983            } else {1984                freeClientAsync(slave);1985            }1986        } else if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END) {1987            struct redis_stat buf;19881989            if (bgsaveerr != C_OK) {1990                /* Notify the task that the snapshot bulk delivery failed */1991                if (slave->flags & CLIENT_ASM_MIGRATING)1992                    asmSlotSnapshotFailed(slave->task);1993                freeClientAsync(slave);1994                serverLog(LL_WARNING,"SYNC failed. BGSAVE child returned an error");1995                continue;1996            }19971998            /* If this was an RDB on disk save, we have to prepare to send1999             * the RDB from disk to the slave socket. Otherwise if this was2000             * already an RDB -> Slaves socket transfer, used in the case of

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.