mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
Merge branch 'psync2' into unstable
This commit is contained in:
commit
8fb3ad2444
@ -402,6 +402,10 @@ repl-disable-tcp-nodelay no
|
|||||||
# need to elapse, starting from the time the last slave disconnected, for
|
# need to elapse, starting from the time the last slave disconnected, for
|
||||||
# the backlog buffer to be freed.
|
# the backlog buffer to be freed.
|
||||||
#
|
#
|
||||||
|
# Note that slaves never free the backlog for timeout, since they may be
|
||||||
|
# promoted to masters later, and should be able to correctly "partially
|
||||||
|
# resynchronize" with the slaves: hence they should always accumulate backlog.
|
||||||
|
#
|
||||||
# A value of 0 means to never release the backlog.
|
# A value of 0 means to never release the backlog.
|
||||||
#
|
#
|
||||||
# repl-backlog-ttl 3600
|
# repl-backlog-ttl 3600
|
||||||
|
@ -653,7 +653,7 @@ int loadAppendOnlyFile(char *filename) {
|
|||||||
serverLog(LL_NOTICE,"Reading RDB preamble from AOF file...");
|
serverLog(LL_NOTICE,"Reading RDB preamble from AOF file...");
|
||||||
if (fseek(fp,0,SEEK_SET) == -1) goto readerr;
|
if (fseek(fp,0,SEEK_SET) == -1) goto readerr;
|
||||||
rioInitWithFile(&rdb,fp);
|
rioInitWithFile(&rdb,fp);
|
||||||
if (rdbLoadRio(&rdb) != C_OK) {
|
if (rdbLoadRio(&rdb,NULL) != C_OK) {
|
||||||
serverLog(LL_WARNING,"Error reading the RDB preamble of the AOF file, AOF loading aborted");
|
serverLog(LL_WARNING,"Error reading the RDB preamble of the AOF file, AOF loading aborted");
|
||||||
goto readerr;
|
goto readerr;
|
||||||
} else {
|
} else {
|
||||||
@ -1152,7 +1152,7 @@ int rewriteAppendOnlyFile(char *filename) {
|
|||||||
|
|
||||||
if (server.aof_use_rdb_preamble) {
|
if (server.aof_use_rdb_preamble) {
|
||||||
int error;
|
int error;
|
||||||
if (rdbSaveRio(&aof,&error,RDB_SAVE_AOF_PREAMBLE) == C_ERR) {
|
if (rdbSaveRio(&aof,&error,RDB_SAVE_AOF_PREAMBLE,NULL) == C_ERR) {
|
||||||
errno = error;
|
errno = error;
|
||||||
goto werr;
|
goto werr;
|
||||||
}
|
}
|
||||||
|
2
src/db.c
2
src/db.c
@ -413,7 +413,7 @@ void flushallCommand(client *c) {
|
|||||||
/* Normally rdbSave() will reset dirty, but we don't want this here
|
/* Normally rdbSave() will reset dirty, but we don't want this here
|
||||||
* as otherwise FLUSHALL will not be replicated nor put into the AOF. */
|
* as otherwise FLUSHALL will not be replicated nor put into the AOF. */
|
||||||
int saved_dirty = server.dirty;
|
int saved_dirty = server.dirty;
|
||||||
rdbSave(server.rdb_filename);
|
rdbSave(server.rdb_filename,NULL);
|
||||||
server.dirty = saved_dirty;
|
server.dirty = saved_dirty;
|
||||||
}
|
}
|
||||||
server.dirty++;
|
server.dirty++;
|
||||||
|
@ -320,12 +320,12 @@ void debugCommand(client *c) {
|
|||||||
if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]);
|
if (c->argc >= 3) c->argv[2] = tryObjectEncoding(c->argv[2]);
|
||||||
serverAssertWithInfo(c,c->argv[0],1 == 2);
|
serverAssertWithInfo(c,c->argv[0],1 == 2);
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
|
} else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
|
||||||
if (rdbSave(server.rdb_filename) != C_OK) {
|
if (rdbSave(server.rdb_filename,NULL) != C_OK) {
|
||||||
addReply(c,shared.err);
|
addReply(c,shared.err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);
|
emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);
|
||||||
if (rdbLoad(server.rdb_filename) != C_OK) {
|
if (rdbLoad(server.rdb_filename,NULL) != C_OK) {
|
||||||
addReplyError(c,"Error trying to load the RDB dump");
|
addReplyError(c,"Error trying to load the RDB dump");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -352,6 +352,14 @@ void addReplySds(client *c, sds s) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This low level function just adds whatever protocol you send it to the
|
||||||
|
* client buffer, trying the static buffer initially, and using the string
|
||||||
|
* of objects if not possible.
|
||||||
|
*
|
||||||
|
* It is efficient because does not create an SDS object nor an Redis object
|
||||||
|
* if not needed. The object will only be created by calling
|
||||||
|
* _addReplyStringToList() if we fail to extend the existing tail object
|
||||||
|
* in the list of objects. */
|
||||||
void addReplyString(client *c, const char *s, size_t len) {
|
void addReplyString(client *c, const char *s, size_t len) {
|
||||||
if (prepareClientToWrite(c) != C_OK) return;
|
if (prepareClientToWrite(c) != C_OK) return;
|
||||||
if (_addReplyToBuffer(c,s,len) != C_OK)
|
if (_addReplyToBuffer(c,s,len) != C_OK)
|
||||||
@ -1022,7 +1030,7 @@ int processInlineBuffer(client *c) {
|
|||||||
char *newline;
|
char *newline;
|
||||||
int argc, j;
|
int argc, j;
|
||||||
sds *argv, aux;
|
sds *argv, aux;
|
||||||
size_t querylen;
|
size_t querylen, protolen;
|
||||||
|
|
||||||
/* Search for end of line */
|
/* Search for end of line */
|
||||||
newline = strchr(c->querybuf,'\n');
|
newline = strchr(c->querybuf,'\n');
|
||||||
@ -1035,6 +1043,7 @@ int processInlineBuffer(client *c) {
|
|||||||
}
|
}
|
||||||
return C_ERR;
|
return C_ERR;
|
||||||
}
|
}
|
||||||
|
protolen = (newline - c->querybuf)+1; /* Total protocol bytes of command. */
|
||||||
|
|
||||||
/* Handle the \r\n case. */
|
/* Handle the \r\n case. */
|
||||||
if (newline && newline != c->querybuf && *(newline-1) == '\r')
|
if (newline && newline != c->querybuf && *(newline-1) == '\r')
|
||||||
@ -1057,6 +1066,15 @@ int processInlineBuffer(client *c) {
|
|||||||
if (querylen == 0 && c->flags & CLIENT_SLAVE)
|
if (querylen == 0 && c->flags & CLIENT_SLAVE)
|
||||||
c->repl_ack_time = server.unixtime;
|
c->repl_ack_time = server.unixtime;
|
||||||
|
|
||||||
|
/* Newline from masters can be used to prevent timeouts, but should
|
||||||
|
* not affect the replication offset since they are always sent
|
||||||
|
* "out of band" directly writing to the socket and without passing
|
||||||
|
* from the output buffers. */
|
||||||
|
if (querylen == 0 && c->flags & CLIENT_MASTER) {
|
||||||
|
c->reploff -= protolen;
|
||||||
|
while (protolen--) chopReplicationBacklog();
|
||||||
|
}
|
||||||
|
|
||||||
/* Leave data after the first line of the query in the buffer */
|
/* Leave data after the first line of the query in the buffer */
|
||||||
sdsrange(c->querybuf,querylen+2,-1);
|
sdsrange(c->querybuf,querylen+2,-1);
|
||||||
|
|
||||||
@ -1321,7 +1339,11 @@ void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|||||||
|
|
||||||
sdsIncrLen(c->querybuf,nread);
|
sdsIncrLen(c->querybuf,nread);
|
||||||
c->lastinteraction = server.unixtime;
|
c->lastinteraction = server.unixtime;
|
||||||
if (c->flags & CLIENT_MASTER) c->reploff += nread;
|
if (c->flags & CLIENT_MASTER) {
|
||||||
|
c->reploff += nread;
|
||||||
|
replicationFeedSlavesFromMasterStream(server.slaves,
|
||||||
|
c->querybuf+qblen,nread);
|
||||||
|
}
|
||||||
server.stat_net_input_bytes += nread;
|
server.stat_net_input_bytes += nread;
|
||||||
if (sdslen(c->querybuf) > server.client_max_querybuf_len) {
|
if (sdslen(c->querybuf) > server.client_max_querybuf_len) {
|
||||||
sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty();
|
sds ci = catClientInfoString(sdsempty(),c), bytes = sdsempty();
|
||||||
|
58
src/rdb.c
58
src/rdb.c
@ -835,7 +835,7 @@ int rdbSaveAuxFieldStrInt(rio *rdb, char *key, long long val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Save a few default AUX fields with information about the RDB generated. */
|
/* Save a few default AUX fields with information about the RDB generated. */
|
||||||
int rdbSaveInfoAuxFields(rio *rdb, int flags) {
|
int rdbSaveInfoAuxFields(rio *rdb, int flags, rdbSaveInfo *rsi) {
|
||||||
int redis_bits = (sizeof(void*) == 8) ? 64 : 32;
|
int redis_bits = (sizeof(void*) == 8) ? 64 : 32;
|
||||||
int aof_preamble = (flags & RDB_SAVE_AOF_PREAMBLE) != 0;
|
int aof_preamble = (flags & RDB_SAVE_AOF_PREAMBLE) != 0;
|
||||||
|
|
||||||
@ -844,7 +844,19 @@ int rdbSaveInfoAuxFields(rio *rdb, int flags) {
|
|||||||
if (rdbSaveAuxFieldStrInt(rdb,"redis-bits",redis_bits) == -1) return -1;
|
if (rdbSaveAuxFieldStrInt(rdb,"redis-bits",redis_bits) == -1) return -1;
|
||||||
if (rdbSaveAuxFieldStrInt(rdb,"ctime",time(NULL)) == -1) return -1;
|
if (rdbSaveAuxFieldStrInt(rdb,"ctime",time(NULL)) == -1) return -1;
|
||||||
if (rdbSaveAuxFieldStrInt(rdb,"used-mem",zmalloc_used_memory()) == -1) return -1;
|
if (rdbSaveAuxFieldStrInt(rdb,"used-mem",zmalloc_used_memory()) == -1) return -1;
|
||||||
|
|
||||||
|
/* Handle saving options that generate aux fields. */
|
||||||
|
if (rsi) {
|
||||||
|
if (rsi->repl_stream_db &&
|
||||||
|
rdbSaveAuxFieldStrInt(rdb,"repl-stream-db",rsi->repl_stream_db)
|
||||||
|
== -1)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (rdbSaveAuxFieldStrInt(rdb,"aof-preamble",aof_preamble) == -1) return -1;
|
if (rdbSaveAuxFieldStrInt(rdb,"aof-preamble",aof_preamble) == -1) return -1;
|
||||||
|
if (rdbSaveAuxFieldStrStr(rdb,"repl-id",server.replid) == -1) return -1;
|
||||||
|
if (rdbSaveAuxFieldStrInt(rdb,"repl-offset",server.master_repl_offset) == -1) return -1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -856,7 +868,7 @@ int rdbSaveInfoAuxFields(rio *rdb, int flags) {
|
|||||||
* When the function returns C_ERR and if 'error' is not NULL, the
|
* When the function returns C_ERR and if 'error' is not NULL, the
|
||||||
* integer pointed by 'error' is set to the value of errno just after the I/O
|
* integer pointed by 'error' is set to the value of errno just after the I/O
|
||||||
* error. */
|
* error. */
|
||||||
int rdbSaveRio(rio *rdb, int *error, int flags) {
|
int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
|
||||||
dictIterator *di = NULL;
|
dictIterator *di = NULL;
|
||||||
dictEntry *de;
|
dictEntry *de;
|
||||||
char magic[10];
|
char magic[10];
|
||||||
@ -869,7 +881,7 @@ int rdbSaveRio(rio *rdb, int *error, int flags) {
|
|||||||
rdb->update_cksum = rioGenericUpdateChecksum;
|
rdb->update_cksum = rioGenericUpdateChecksum;
|
||||||
snprintf(magic,sizeof(magic),"REDIS%04d",RDB_VERSION);
|
snprintf(magic,sizeof(magic),"REDIS%04d",RDB_VERSION);
|
||||||
if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;
|
if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;
|
||||||
if (rdbSaveInfoAuxFields(rdb,flags) == -1) goto werr;
|
if (rdbSaveInfoAuxFields(rdb,flags,rsi) == -1) goto werr;
|
||||||
|
|
||||||
for (j = 0; j < server.dbnum; j++) {
|
for (j = 0; j < server.dbnum; j++) {
|
||||||
redisDb *db = server.db+j;
|
redisDb *db = server.db+j;
|
||||||
@ -945,7 +957,7 @@ werr:
|
|||||||
* While the suffix is the 40 bytes hex string we announced in the prefix.
|
* While the suffix is the 40 bytes hex string we announced in the prefix.
|
||||||
* This way processes receiving the payload can understand when it ends
|
* This way processes receiving the payload can understand when it ends
|
||||||
* without doing any processing of the content. */
|
* without doing any processing of the content. */
|
||||||
int rdbSaveRioWithEOFMark(rio *rdb, int *error) {
|
int rdbSaveRioWithEOFMark(rio *rdb, int *error, rdbSaveInfo *rsi) {
|
||||||
char eofmark[RDB_EOF_MARK_SIZE];
|
char eofmark[RDB_EOF_MARK_SIZE];
|
||||||
|
|
||||||
getRandomHexChars(eofmark,RDB_EOF_MARK_SIZE);
|
getRandomHexChars(eofmark,RDB_EOF_MARK_SIZE);
|
||||||
@ -953,7 +965,7 @@ int rdbSaveRioWithEOFMark(rio *rdb, int *error) {
|
|||||||
if (rioWrite(rdb,"$EOF:",5) == 0) goto werr;
|
if (rioWrite(rdb,"$EOF:",5) == 0) goto werr;
|
||||||
if (rioWrite(rdb,eofmark,RDB_EOF_MARK_SIZE) == 0) goto werr;
|
if (rioWrite(rdb,eofmark,RDB_EOF_MARK_SIZE) == 0) goto werr;
|
||||||
if (rioWrite(rdb,"\r\n",2) == 0) goto werr;
|
if (rioWrite(rdb,"\r\n",2) == 0) goto werr;
|
||||||
if (rdbSaveRio(rdb,error,RDB_SAVE_NONE) == C_ERR) goto werr;
|
if (rdbSaveRio(rdb,error,RDB_SAVE_NONE,rsi) == C_ERR) goto werr;
|
||||||
if (rioWrite(rdb,eofmark,RDB_EOF_MARK_SIZE) == 0) goto werr;
|
if (rioWrite(rdb,eofmark,RDB_EOF_MARK_SIZE) == 0) goto werr;
|
||||||
return C_OK;
|
return C_OK;
|
||||||
|
|
||||||
@ -964,7 +976,7 @@ werr: /* Write error. */
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
|
/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
|
||||||
int rdbSave(char *filename) {
|
int rdbSave(char *filename, rdbSaveInfo *rsi) {
|
||||||
char tmpfile[256];
|
char tmpfile[256];
|
||||||
char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
|
char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
@ -985,7 +997,7 @@ int rdbSave(char *filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rioInitWithFile(&rdb,fp);
|
rioInitWithFile(&rdb,fp);
|
||||||
if (rdbSaveRio(&rdb,&error,RDB_SAVE_NONE) == C_ERR) {
|
if (rdbSaveRio(&rdb,&error,RDB_SAVE_NONE,rsi) == C_ERR) {
|
||||||
errno = error;
|
errno = error;
|
||||||
goto werr;
|
goto werr;
|
||||||
}
|
}
|
||||||
@ -1023,7 +1035,7 @@ werr:
|
|||||||
return C_ERR;
|
return C_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
int rdbSaveBackground(char *filename) {
|
int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
|
||||||
pid_t childpid;
|
pid_t childpid;
|
||||||
long long start;
|
long long start;
|
||||||
|
|
||||||
@ -1040,7 +1052,7 @@ int rdbSaveBackground(char *filename) {
|
|||||||
/* Child */
|
/* Child */
|
||||||
closeListeningSockets(0);
|
closeListeningSockets(0);
|
||||||
redisSetProcTitle("redis-rdb-bgsave");
|
redisSetProcTitle("redis-rdb-bgsave");
|
||||||
retval = rdbSave(filename);
|
retval = rdbSave(filename,rsi);
|
||||||
if (retval == C_OK) {
|
if (retval == C_OK) {
|
||||||
size_t private_dirty = zmalloc_get_private_dirty(-1);
|
size_t private_dirty = zmalloc_get_private_dirty(-1);
|
||||||
|
|
||||||
@ -1410,7 +1422,7 @@ void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
|
|||||||
|
|
||||||
/* Load an RDB file from the rio stream 'rdb'. On success C_OK is returned,
|
/* Load an RDB file from the rio stream 'rdb'. On success C_OK is returned,
|
||||||
* otherwise C_ERR is returned and 'errno' is set accordingly. */
|
* otherwise C_ERR is returned and 'errno' is set accordingly. */
|
||||||
int rdbLoadRio(rio *rdb) {
|
int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi) {
|
||||||
uint64_t dbid;
|
uint64_t dbid;
|
||||||
int type, rdbver;
|
int type, rdbver;
|
||||||
redisDb *db = server.db+0;
|
redisDb *db = server.db+0;
|
||||||
@ -1501,6 +1513,15 @@ int rdbLoadRio(rio *rdb) {
|
|||||||
serverLog(LL_NOTICE,"RDB '%s': %s",
|
serverLog(LL_NOTICE,"RDB '%s': %s",
|
||||||
(char*)auxkey->ptr,
|
(char*)auxkey->ptr,
|
||||||
(char*)auxval->ptr);
|
(char*)auxval->ptr);
|
||||||
|
} else if (!strcasecmp(auxkey->ptr,"repl-stream-db")) {
|
||||||
|
if (rsi) rsi->repl_stream_db = atoi(auxval->ptr);
|
||||||
|
} else if (!strcasecmp(auxkey->ptr,"repl-id")) {
|
||||||
|
if (rsi && sdslen(auxval->ptr) == CONFIG_RUN_ID_SIZE) {
|
||||||
|
memcpy(rsi->repl_id,auxval->ptr,CONFIG_RUN_ID_SIZE+1);
|
||||||
|
rsi->repl_id_is_set = 1;
|
||||||
|
}
|
||||||
|
} else if (!strcasecmp(auxkey->ptr,"repl-offset")) {
|
||||||
|
if (rsi) rsi->repl_offset = strtoll(auxval->ptr,NULL,10);
|
||||||
} else {
|
} else {
|
||||||
/* We ignore fields we don't understand, as by AUX field
|
/* We ignore fields we don't understand, as by AUX field
|
||||||
* contract. */
|
* contract. */
|
||||||
@ -1559,8 +1580,11 @@ eoferr: /* unexpected end of file is handled here with a fatal exit */
|
|||||||
/* Like rdbLoadRio() but takes a filename instead of a rio stream. The
|
/* Like rdbLoadRio() but takes a filename instead of a rio stream. The
|
||||||
* filename is open for reading and a rio stream object created in order
|
* filename is open for reading and a rio stream object created in order
|
||||||
* to do the actual loading. Moreover the ETA displayed in the INFO
|
* to do the actual loading. Moreover the ETA displayed in the INFO
|
||||||
* output is initialized and finalized. */
|
* output is initialized and finalized.
|
||||||
int rdbLoad(char *filename) {
|
*
|
||||||
|
* If you pass an 'rsi' structure initialied with RDB_SAVE_OPTION_INIT, the
|
||||||
|
* loading code will fiil the information fields in the structure. */
|
||||||
|
int rdbLoad(char *filename, rdbSaveInfo *rsi) {
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
rio rdb;
|
rio rdb;
|
||||||
int retval;
|
int retval;
|
||||||
@ -1568,7 +1592,7 @@ int rdbLoad(char *filename) {
|
|||||||
if ((fp = fopen(filename,"r")) == NULL) return C_ERR;
|
if ((fp = fopen(filename,"r")) == NULL) return C_ERR;
|
||||||
startLoading(fp);
|
startLoading(fp);
|
||||||
rioInitWithFile(&rdb,fp);
|
rioInitWithFile(&rdb,fp);
|
||||||
retval = rdbLoadRio(&rdb);
|
retval = rdbLoadRio(&rdb,rsi);
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
stopLoading();
|
stopLoading();
|
||||||
return retval;
|
return retval;
|
||||||
@ -1721,7 +1745,7 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
|
|||||||
|
|
||||||
/* Spawn an RDB child that writes the RDB to the sockets of the slaves
|
/* Spawn an RDB child that writes the RDB to the sockets of the slaves
|
||||||
* that are currently in SLAVE_STATE_WAIT_BGSAVE_START state. */
|
* that are currently in SLAVE_STATE_WAIT_BGSAVE_START state. */
|
||||||
int rdbSaveToSlavesSockets(void) {
|
int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
|
||||||
int *fds;
|
int *fds;
|
||||||
uint64_t *clientids;
|
uint64_t *clientids;
|
||||||
int numfds;
|
int numfds;
|
||||||
@ -1779,7 +1803,7 @@ int rdbSaveToSlavesSockets(void) {
|
|||||||
closeListeningSockets(0);
|
closeListeningSockets(0);
|
||||||
redisSetProcTitle("redis-rdb-to-slaves");
|
redisSetProcTitle("redis-rdb-to-slaves");
|
||||||
|
|
||||||
retval = rdbSaveRioWithEOFMark(&slave_sockets,NULL);
|
retval = rdbSaveRioWithEOFMark(&slave_sockets,NULL,rsi);
|
||||||
if (retval == C_OK && rioFlush(&slave_sockets) == 0)
|
if (retval == C_OK && rioFlush(&slave_sockets) == 0)
|
||||||
retval = C_ERR;
|
retval = C_ERR;
|
||||||
|
|
||||||
@ -1884,7 +1908,7 @@ void saveCommand(client *c) {
|
|||||||
addReplyError(c,"Background save already in progress");
|
addReplyError(c,"Background save already in progress");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (rdbSave(server.rdb_filename) == C_OK) {
|
if (rdbSave(server.rdb_filename,NULL) == C_OK) {
|
||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
} else {
|
} else {
|
||||||
addReply(c,shared.err);
|
addReply(c,shared.err);
|
||||||
@ -1918,7 +1942,7 @@ void bgsaveCommand(client *c) {
|
|||||||
"Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenver "
|
"Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenver "
|
||||||
"possible.");
|
"possible.");
|
||||||
}
|
}
|
||||||
} else if (rdbSaveBackground(server.rdb_filename) == C_OK) {
|
} else if (rdbSaveBackground(server.rdb_filename,NULL) == C_OK) {
|
||||||
addReplyStatus(c,"Background saving started");
|
addReplyStatus(c,"Background saving started");
|
||||||
} else {
|
} else {
|
||||||
addReply(c,shared.err);
|
addReply(c,shared.err);
|
||||||
|
10
src/rdb.h
10
src/rdb.h
@ -118,11 +118,11 @@ uint64_t rdbLoadLen(rio *rdb, int *isencoded);
|
|||||||
int rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr);
|
int rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr);
|
||||||
int rdbSaveObjectType(rio *rdb, robj *o);
|
int rdbSaveObjectType(rio *rdb, robj *o);
|
||||||
int rdbLoadObjectType(rio *rdb);
|
int rdbLoadObjectType(rio *rdb);
|
||||||
int rdbLoad(char *filename);
|
int rdbLoad(char *filename, rdbSaveInfo *rsi);
|
||||||
int rdbSaveBackground(char *filename);
|
int rdbSaveBackground(char *filename, rdbSaveInfo *rsi);
|
||||||
int rdbSaveToSlavesSockets(void);
|
int rdbSaveToSlavesSockets(rdbSaveInfo *rsi);
|
||||||
void rdbRemoveTempFile(pid_t childpid);
|
void rdbRemoveTempFile(pid_t childpid);
|
||||||
int rdbSave(char *filename);
|
int rdbSave(char *filename, rdbSaveInfo *rsi);
|
||||||
ssize_t rdbSaveObject(rio *rdb, robj *o);
|
ssize_t rdbSaveObject(rio *rdb, robj *o);
|
||||||
size_t rdbSavedObjectLen(robj *o);
|
size_t rdbSavedObjectLen(robj *o);
|
||||||
robj *rdbLoadObject(int type, rio *rdb);
|
robj *rdbLoadObject(int type, rio *rdb);
|
||||||
@ -136,6 +136,6 @@ int rdbSaveBinaryDoubleValue(rio *rdb, double val);
|
|||||||
int rdbLoadBinaryDoubleValue(rio *rdb, double *val);
|
int rdbLoadBinaryDoubleValue(rio *rdb, double *val);
|
||||||
int rdbSaveBinaryFloatValue(rio *rdb, float val);
|
int rdbSaveBinaryFloatValue(rio *rdb, float val);
|
||||||
int rdbLoadBinaryFloatValue(rio *rdb, float *val);
|
int rdbLoadBinaryFloatValue(rio *rdb, float *val);
|
||||||
int rdbLoadRio(rio *rdb);
|
int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -79,11 +79,6 @@ void createReplicationBacklog(void) {
|
|||||||
server.repl_backlog = zmalloc(server.repl_backlog_size);
|
server.repl_backlog = zmalloc(server.repl_backlog_size);
|
||||||
server.repl_backlog_histlen = 0;
|
server.repl_backlog_histlen = 0;
|
||||||
server.repl_backlog_idx = 0;
|
server.repl_backlog_idx = 0;
|
||||||
/* When a new backlog buffer is created, we increment the replication
|
|
||||||
* offset by one to make sure we'll not be able to PSYNC with any
|
|
||||||
* previous slave. This is needed because we avoid incrementing the
|
|
||||||
* master_repl_offset if no backlog exists nor slaves are attached. */
|
|
||||||
server.master_repl_offset++;
|
|
||||||
|
|
||||||
/* We don't have any data inside our buffer, but virtually the first
|
/* We don't have any data inside our buffer, but virtually the first
|
||||||
* byte we have is the next byte that will be generated for the
|
* byte we have is the next byte that will be generated for the
|
||||||
@ -153,6 +148,22 @@ void feedReplicationBacklog(void *ptr, size_t len) {
|
|||||||
server.repl_backlog_histlen + 1;
|
server.repl_backlog_histlen + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove the last byte from the replication backlog. This
|
||||||
|
* is useful when we receive an out of band "\n" to keep the connection
|
||||||
|
* alive but don't want to count it as replication stream.
|
||||||
|
*
|
||||||
|
* As a side effect this function adjusts the master replication offset
|
||||||
|
* of this instance to account for the missing byte. */
|
||||||
|
void chopReplicationBacklog(void) {
|
||||||
|
if (!server.repl_backlog || !server.repl_backlog_histlen) return;
|
||||||
|
if (server.repl_backlog_idx == 0)
|
||||||
|
server.repl_backlog_idx = server.repl_backlog_size-1;
|
||||||
|
else
|
||||||
|
server.repl_backlog_idx--;
|
||||||
|
server.master_repl_offset--;
|
||||||
|
server.repl_backlog_histlen--;
|
||||||
|
}
|
||||||
|
|
||||||
/* Wrapper for feedReplicationBacklog() that takes Redis string objects
|
/* Wrapper for feedReplicationBacklog() that takes Redis string objects
|
||||||
* as input. */
|
* as input. */
|
||||||
void feedReplicationBacklogWithObject(robj *o) {
|
void feedReplicationBacklogWithObject(robj *o) {
|
||||||
@ -170,12 +181,24 @@ void feedReplicationBacklogWithObject(robj *o) {
|
|||||||
feedReplicationBacklog(p,len);
|
feedReplicationBacklog(p,len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Propagate write commands to slaves, and populate the replication backlog
|
||||||
|
* as well. This function is used if the instance is a master: we use
|
||||||
|
* the commands received by our clients in order to create the replication
|
||||||
|
* stream. Instead if the instance is a slave and has sub-slaves attached,
|
||||||
|
* we use replicationFeedSlavesFromMaster() */
|
||||||
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
|
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
|
||||||
listNode *ln;
|
listNode *ln;
|
||||||
listIter li;
|
listIter li;
|
||||||
int j, len;
|
int j, len;
|
||||||
char llstr[LONG_STR_SIZE];
|
char llstr[LONG_STR_SIZE];
|
||||||
|
|
||||||
|
/* If the instance is not a top level master, return ASAP: we'll just proxy
|
||||||
|
* the stream of data we receive from our master instead, in order to
|
||||||
|
* propagate *identical* replication stream. In this way this slave can
|
||||||
|
* advertise the same replication ID as the master (since it shares the
|
||||||
|
* master replication history and has the same backlog and offsets). */
|
||||||
|
if (server.masterhost != NULL) return;
|
||||||
|
|
||||||
/* If there aren't slaves, and there is no backlog buffer to populate,
|
/* If there aren't slaves, and there is no backlog buffer to populate,
|
||||||
* we can return ASAP. */
|
* we can return ASAP. */
|
||||||
if (server.repl_backlog == NULL && listLength(slaves) == 0) return;
|
if (server.repl_backlog == NULL && listLength(slaves) == 0) return;
|
||||||
@ -265,6 +288,34 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function is used in order to proxy what we receive from our master
|
||||||
|
* to our sub-slaves. */
|
||||||
|
#include <ctype.h>
|
||||||
|
void replicationFeedSlavesFromMasterStream(list *slaves, char *buf, size_t buflen) {
|
||||||
|
listNode *ln;
|
||||||
|
listIter li;
|
||||||
|
|
||||||
|
/* Debugging: this is handy to see the stream sent from master
|
||||||
|
* to slaves. Disabled with if(0). */
|
||||||
|
if (0) {
|
||||||
|
printf("%zu:",buflen);
|
||||||
|
for (size_t j = 0; j < buflen; j++) {
|
||||||
|
printf("%c", isprint(buf[j]) ? buf[j] : '.');
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server.repl_backlog) feedReplicationBacklog(buf,buflen);
|
||||||
|
listRewind(slaves,&li);
|
||||||
|
while((ln = listNext(&li))) {
|
||||||
|
client *slave = ln->value;
|
||||||
|
|
||||||
|
/* Don't feed slaves that are still waiting for BGSAVE to start */
|
||||||
|
if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START) continue;
|
||||||
|
addReplyString(slave,buf,buflen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv, int argc) {
|
void replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv, int argc) {
|
||||||
listNode *ln;
|
listNode *ln;
|
||||||
listIter li;
|
listIter li;
|
||||||
@ -329,7 +380,7 @@ long long addReplyReplicationBacklog(client *c, long long offset) {
|
|||||||
skip = offset - server.repl_backlog_off;
|
skip = offset - server.repl_backlog_off;
|
||||||
serverLog(LL_DEBUG, "[PSYNC] Skipping: %lld", skip);
|
serverLog(LL_DEBUG, "[PSYNC] Skipping: %lld", skip);
|
||||||
|
|
||||||
/* Point j to the oldest byte, that is actaully our
|
/* Point j to the oldest byte, that is actually our
|
||||||
* server.repl_backlog_off byte. */
|
* server.repl_backlog_off byte. */
|
||||||
j = (server.repl_backlog_idx +
|
j = (server.repl_backlog_idx +
|
||||||
(server.repl_backlog_size-server.repl_backlog_histlen)) %
|
(server.repl_backlog_size-server.repl_backlog_histlen)) %
|
||||||
@ -361,18 +412,14 @@ long long addReplyReplicationBacklog(client *c, long long offset) {
|
|||||||
* the BGSAVE process started and before executing any other command
|
* the BGSAVE process started and before executing any other command
|
||||||
* from clients. */
|
* from clients. */
|
||||||
long long getPsyncInitialOffset(void) {
|
long long getPsyncInitialOffset(void) {
|
||||||
long long psync_offset = server.master_repl_offset;
|
return server.master_repl_offset;
|
||||||
/* Add 1 to psync_offset if it the replication backlog does not exists
|
|
||||||
* as when it will be created later we'll increment the offset by one. */
|
|
||||||
if (server.repl_backlog == NULL) psync_offset++;
|
|
||||||
return psync_offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send a FULLRESYNC reply in the specific case of a full resynchronization,
|
/* Send a FULLRESYNC reply in the specific case of a full resynchronization,
|
||||||
* as a side effect setup the slave for a full sync in different ways:
|
* as a side effect setup the slave for a full sync in different ways:
|
||||||
*
|
*
|
||||||
* 1) Remember, into the slave client structure, the offset we sent
|
* 1) Remember, into the slave client structure, the replication offset
|
||||||
* here, so that if new slaves will later attach to the same
|
* we sent here, so that if new slaves will later attach to the same
|
||||||
* background RDB saving process (by duplicating this client output
|
* background RDB saving process (by duplicating this client output
|
||||||
* buffer), we can get the right offset from this slave.
|
* buffer), we can get the right offset from this slave.
|
||||||
* 2) Set the replication state of the slave to WAIT_BGSAVE_END so that
|
* 2) Set the replication state of the slave to WAIT_BGSAVE_END so that
|
||||||
@ -392,14 +439,14 @@ int replicationSetupSlaveForFullResync(client *slave, long long offset) {
|
|||||||
slave->replstate = SLAVE_STATE_WAIT_BGSAVE_END;
|
slave->replstate = SLAVE_STATE_WAIT_BGSAVE_END;
|
||||||
/* We are going to accumulate the incremental changes for this
|
/* We are going to accumulate the incremental changes for this
|
||||||
* slave as well. Set slaveseldb to -1 in order to force to re-emit
|
* slave as well. Set slaveseldb to -1 in order to force to re-emit
|
||||||
* a SLEECT statement in the replication stream. */
|
* a SELECT statement in the replication stream. */
|
||||||
server.slaveseldb = -1;
|
server.slaveseldb = -1;
|
||||||
|
|
||||||
/* Don't send this reply to slaves that approached us with
|
/* Don't send this reply to slaves that approached us with
|
||||||
* the old SYNC command. */
|
* the old SYNC command. */
|
||||||
if (!(slave->flags & CLIENT_PRE_PSYNC)) {
|
if (!(slave->flags & CLIENT_PRE_PSYNC)) {
|
||||||
buflen = snprintf(buf,sizeof(buf),"+FULLRESYNC %s %lld\r\n",
|
buflen = snprintf(buf,sizeof(buf),"+FULLRESYNC %s %lld\r\n",
|
||||||
server.runid,offset);
|
server.replid,offset);
|
||||||
if (write(slave->fd,buf,buflen) != buflen) {
|
if (write(slave->fd,buf,buflen) != buflen) {
|
||||||
freeClientAsync(slave);
|
freeClientAsync(slave);
|
||||||
return C_ERR;
|
return C_ERR;
|
||||||
@ -415,19 +462,32 @@ int replicationSetupSlaveForFullResync(client *slave, long long offset) {
|
|||||||
* with the usual full resync. */
|
* with the usual full resync. */
|
||||||
int masterTryPartialResynchronization(client *c) {
|
int masterTryPartialResynchronization(client *c) {
|
||||||
long long psync_offset, psync_len;
|
long long psync_offset, psync_len;
|
||||||
char *master_runid = c->argv[1]->ptr;
|
char *master_replid = c->argv[1]->ptr;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
int buflen;
|
int buflen;
|
||||||
|
|
||||||
/* Is the runid of this master the same advertised by the wannabe slave
|
/* Parse the replication offset asked by the slave. Go to full sync
|
||||||
* via PSYNC? If runid changed this master is a different instance and
|
* on parse error: this should never happen but we try to handle
|
||||||
* there is no way to continue. */
|
* it in a robust way compared to aborting. */
|
||||||
if (strcasecmp(master_runid, server.runid)) {
|
if (getLongLongFromObjectOrReply(c,c->argv[2],&psync_offset,NULL) !=
|
||||||
|
C_OK) goto need_full_resync;
|
||||||
|
|
||||||
|
/* Is the replication ID of this master the same advertised by the wannabe
|
||||||
|
* slave via PSYNC? If the replication ID changed this master has a
|
||||||
|
* different replication history, and there is no way to continue.
|
||||||
|
*
|
||||||
|
* Note that there are two potentially valid replication IDs: the ID1
|
||||||
|
* and the ID2. The ID2 however is only valid up to a specific offset. */
|
||||||
|
if (strcasecmp(master_replid, server.replid) &&
|
||||||
|
(strcasecmp(master_replid, server.replid2) ||
|
||||||
|
psync_offset > server.second_replid_offset))
|
||||||
|
{
|
||||||
/* Run id "?" is used by slaves that want to force a full resync. */
|
/* Run id "?" is used by slaves that want to force a full resync. */
|
||||||
if (master_runid[0] != '?') {
|
if (master_replid[0] != '?') {
|
||||||
serverLog(LL_NOTICE,"Partial resynchronization not accepted: "
|
serverLog(LL_NOTICE,"Partial resynchronization not accepted: "
|
||||||
"Runid mismatch (Client asked for runid '%s', my runid is '%s')",
|
"Replication ID mismatch (Slave asked for '%s', my replication "
|
||||||
master_runid, server.runid);
|
"ID is '%s')",
|
||||||
|
master_replid, server.replid);
|
||||||
} else {
|
} else {
|
||||||
serverLog(LL_NOTICE,"Full resync requested by slave %s",
|
serverLog(LL_NOTICE,"Full resync requested by slave %s",
|
||||||
replicationGetSlaveName(c));
|
replicationGetSlaveName(c));
|
||||||
@ -436,8 +496,6 @@ int masterTryPartialResynchronization(client *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* We still have the data our slave is asking for? */
|
/* We still have the data our slave is asking for? */
|
||||||
if (getLongLongFromObjectOrReply(c,c->argv[2],&psync_offset,NULL) !=
|
|
||||||
C_OK) goto need_full_resync;
|
|
||||||
if (!server.repl_backlog ||
|
if (!server.repl_backlog ||
|
||||||
psync_offset < server.repl_backlog_off ||
|
psync_offset < server.repl_backlog_off ||
|
||||||
psync_offset > (server.repl_backlog_off + server.repl_backlog_histlen))
|
psync_offset > (server.repl_backlog_off + server.repl_backlog_histlen))
|
||||||
@ -463,7 +521,11 @@ int masterTryPartialResynchronization(client *c) {
|
|||||||
/* We can't use the connection buffers since they are used to accumulate
|
/* We can't use the connection buffers since they are used to accumulate
|
||||||
* new commands at this stage. But we are sure the socket send buffer is
|
* new commands at this stage. But we are sure the socket send buffer is
|
||||||
* empty so this write will never fail actually. */
|
* empty so this write will never fail actually. */
|
||||||
|
if (c->slave_capa & SLAVE_CAPA_PSYNC2) {
|
||||||
|
buflen = snprintf(buf,sizeof(buf),"+CONTINUE %s\r\n", server.replid);
|
||||||
|
} else {
|
||||||
buflen = snprintf(buf,sizeof(buf),"+CONTINUE\r\n");
|
buflen = snprintf(buf,sizeof(buf),"+CONTINUE\r\n");
|
||||||
|
}
|
||||||
if (write(c->fd,buf,buflen) != buflen) {
|
if (write(c->fd,buf,buflen) != buflen) {
|
||||||
freeClientAsync(c);
|
freeClientAsync(c);
|
||||||
return C_OK;
|
return C_OK;
|
||||||
@ -515,10 +577,18 @@ int startBgsaveForReplication(int mincapa) {
|
|||||||
serverLog(LL_NOTICE,"Starting BGSAVE for SYNC with target: %s",
|
serverLog(LL_NOTICE,"Starting BGSAVE for SYNC with target: %s",
|
||||||
socket_target ? "slaves sockets" : "disk");
|
socket_target ? "slaves sockets" : "disk");
|
||||||
|
|
||||||
|
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
|
||||||
|
/* If we are saving for a chained slave (that is, if we are,
|
||||||
|
* in turn, a slave of another instance), make sure after
|
||||||
|
* loadig the RDB, our slaves select the right DB: we'll just
|
||||||
|
* send the replication stream we receive from our master, so
|
||||||
|
* no way to send SELECT commands. */
|
||||||
|
if (server.master) rsi.repl_stream_db = server.master->db->id;
|
||||||
|
|
||||||
if (socket_target)
|
if (socket_target)
|
||||||
retval = rdbSaveToSlavesSockets();
|
retval = rdbSaveToSlavesSockets(&rsi);
|
||||||
else
|
else
|
||||||
retval = rdbSaveBackground(server.rdb_filename);
|
retval = rdbSaveBackground(server.rdb_filename,&rsi);
|
||||||
|
|
||||||
/* If we failed to BGSAVE, remove the slaves waiting for a full
|
/* If we failed to BGSAVE, remove the slaves waiting for a full
|
||||||
* resynchorinization from the list of salves, inform them with
|
* resynchorinization from the list of salves, inform them with
|
||||||
@ -589,22 +659,22 @@ void syncCommand(client *c) {
|
|||||||
* when this happens masterTryPartialResynchronization() already
|
* when this happens masterTryPartialResynchronization() already
|
||||||
* replied with:
|
* replied with:
|
||||||
*
|
*
|
||||||
* +FULLRESYNC <runid> <offset>
|
* +FULLRESYNC <replid> <offset>
|
||||||
*
|
*
|
||||||
* So the slave knows the new runid and offset to try a PSYNC later
|
* So the slave knows the new replid and offset to try a PSYNC later
|
||||||
* if the connection with the master is lost. */
|
* if the connection with the master is lost. */
|
||||||
if (!strcasecmp(c->argv[0]->ptr,"psync")) {
|
if (!strcasecmp(c->argv[0]->ptr,"psync")) {
|
||||||
if (masterTryPartialResynchronization(c) == C_OK) {
|
if (masterTryPartialResynchronization(c) == C_OK) {
|
||||||
server.stat_sync_partial_ok++;
|
server.stat_sync_partial_ok++;
|
||||||
return; /* No full resync needed, return. */
|
return; /* No full resync needed, return. */
|
||||||
} else {
|
} else {
|
||||||
char *master_runid = c->argv[1]->ptr;
|
char *master_replid = c->argv[1]->ptr;
|
||||||
|
|
||||||
/* Increment stats for failed PSYNCs, but only if the
|
/* Increment stats for failed PSYNCs, but only if the
|
||||||
* runid is not "?", as this is used by slaves to force a full
|
* replid is not "?", as this is used by slaves to force a full
|
||||||
* resync on purpose when they are not albe to partially
|
* resync on purpose when they are not albe to partially
|
||||||
* resync. */
|
* resync. */
|
||||||
if (master_runid[0] != '?') server.stat_sync_partial_err++;
|
if (master_replid[0] != '?') server.stat_sync_partial_err++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* If a slave uses SYNC, we are dealing with an old implementation
|
/* If a slave uses SYNC, we are dealing with an old implementation
|
||||||
@ -625,6 +695,16 @@ void syncCommand(client *c) {
|
|||||||
c->flags |= CLIENT_SLAVE;
|
c->flags |= CLIENT_SLAVE;
|
||||||
listAddNodeTail(server.slaves,c);
|
listAddNodeTail(server.slaves,c);
|
||||||
|
|
||||||
|
/* Create the replication backlog if needed. */
|
||||||
|
if (listLength(server.slaves) == 1 && server.repl_backlog == NULL) {
|
||||||
|
/* When we create the backlog from scratch, we always use a new
|
||||||
|
* replication ID and clear the ID2, since there is no valid
|
||||||
|
* past history. */
|
||||||
|
changeReplicationId();
|
||||||
|
clearReplicationId2();
|
||||||
|
createReplicationBacklog();
|
||||||
|
}
|
||||||
|
|
||||||
/* CASE 1: BGSAVE is in progress, with disk target. */
|
/* CASE 1: BGSAVE is in progress, with disk target. */
|
||||||
if (server.rdb_child_pid != -1 &&
|
if (server.rdb_child_pid != -1 &&
|
||||||
server.rdb_child_type == RDB_CHILD_TYPE_DISK)
|
server.rdb_child_type == RDB_CHILD_TYPE_DISK)
|
||||||
@ -685,9 +765,6 @@ void syncCommand(client *c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (listLength(server.slaves) == 1 && server.repl_backlog == NULL)
|
|
||||||
createReplicationBacklog();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,6 +812,8 @@ void replconfCommand(client *c) {
|
|||||||
/* Ignore capabilities not understood by this master. */
|
/* Ignore capabilities not understood by this master. */
|
||||||
if (!strcasecmp(c->argv[j+1]->ptr,"eof"))
|
if (!strcasecmp(c->argv[j+1]->ptr,"eof"))
|
||||||
c->slave_capa |= SLAVE_CAPA_EOF;
|
c->slave_capa |= SLAVE_CAPA_EOF;
|
||||||
|
else if (!strcasecmp(c->argv[j+1]->ptr,"psync2"))
|
||||||
|
c->slave_capa |= SLAVE_CAPA_PSYNC2;
|
||||||
} else if (!strcasecmp(c->argv[j]->ptr,"ack")) {
|
} else if (!strcasecmp(c->argv[j]->ptr,"ack")) {
|
||||||
/* REPLCONF ACK is used by slave to inform the master the amount
|
/* REPLCONF ACK is used by slave to inform the master the amount
|
||||||
* of replication stream that it processed so far. It is an
|
* of replication stream that it processed so far. It is an
|
||||||
@ -928,6 +1007,43 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type) {
|
|||||||
if (startbgsave) startBgsaveForReplication(mincapa);
|
if (startbgsave) startBgsaveForReplication(mincapa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Change the current instance replication ID with a new, random one.
|
||||||
|
* This will prevent successful PSYNCs between this master and other
|
||||||
|
* slaves, so the command should be called when something happens that
|
||||||
|
* alters the current story of the dataset. */
|
||||||
|
void changeReplicationId(void) {
|
||||||
|
getRandomHexChars(server.replid,CONFIG_RUN_ID_SIZE);
|
||||||
|
server.replid[CONFIG_RUN_ID_SIZE] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear (invalidate) the secondary replication ID. This happens, for
|
||||||
|
* example, after a full resynchronization, when we start a new replication
|
||||||
|
* history. */
|
||||||
|
void clearReplicationId2(void) {
|
||||||
|
memset(server.replid2,'0',sizeof(server.replid));
|
||||||
|
server.replid2[CONFIG_RUN_ID_SIZE] = '\0';
|
||||||
|
server.second_replid_offset = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use the current replication ID / offset as secondary replication
|
||||||
|
* ID, and change the current one in order to start a new history.
|
||||||
|
* This should be used when an instance is switched from slave to master
|
||||||
|
* so that it can serve PSYNC requests performed using the master
|
||||||
|
* replication ID. */
|
||||||
|
void shiftReplicationId(void) {
|
||||||
|
memcpy(server.replid2,server.replid,sizeof(server.replid));
|
||||||
|
/* We set the second replid offset to the master offset + 1, since
|
||||||
|
* the slave will ask for the first byte it has not yet received, so
|
||||||
|
* we need to add one to the offset: for example if, as a slave, we are
|
||||||
|
* sure we have the same history as the master for 50 bytes, after we
|
||||||
|
* are turned into a master, we can accept a PSYNC request with offset
|
||||||
|
* 51, since the slave asking has the same history up to the 50th
|
||||||
|
* byte, and is asking for the new bytes starting at offset 51. */
|
||||||
|
server.second_replid_offset = server.master_repl_offset+1;
|
||||||
|
changeReplicationId();
|
||||||
|
serverLog(LL_WARNING,"Setting secondary replication ID to %s, valid up to offset: %lld. New replication ID is %s", server.replid2, server.second_replid_offset, server.replid);
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------- SLAVE -------------------------------- */
|
/* ----------------------------------- SLAVE -------------------------------- */
|
||||||
|
|
||||||
/* Returns 1 if the given replication state is a handshake state,
|
/* Returns 1 if the given replication state is a handshake state,
|
||||||
@ -965,18 +1081,18 @@ void replicationEmptyDbCallback(void *privdata) {
|
|||||||
/* Once we have a link with the master and the synchroniziation was
|
/* Once we have a link with the master and the synchroniziation was
|
||||||
* performed, this function materializes the master client we store
|
* performed, this function materializes the master client we store
|
||||||
* at server.master, starting from the specified file descriptor. */
|
* at server.master, starting from the specified file descriptor. */
|
||||||
void replicationCreateMasterClient(int fd) {
|
void replicationCreateMasterClient(int fd, int dbid) {
|
||||||
server.master = createClient(fd);
|
server.master = createClient(fd);
|
||||||
server.master->flags |= CLIENT_MASTER;
|
server.master->flags |= CLIENT_MASTER;
|
||||||
server.master->authenticated = 1;
|
server.master->authenticated = 1;
|
||||||
server.repl_state = REPL_STATE_CONNECTED;
|
server.master->reploff = server.master_initial_offset;
|
||||||
server.master->reploff = server.repl_master_initial_offset;
|
memcpy(server.master->replid, server.master_replid,
|
||||||
memcpy(server.master->replrunid, server.repl_master_runid,
|
sizeof(server.master_replid));
|
||||||
sizeof(server.repl_master_runid));
|
|
||||||
/* If master offset is set to -1, this master is old and is not
|
/* If master offset is set to -1, this master is old and is not
|
||||||
* PSYNC capable, so we flag it accordingly. */
|
* PSYNC capable, so we flag it accordingly. */
|
||||||
if (server.master->reploff == -1)
|
if (server.master->reploff == -1)
|
||||||
server.master->flags |= CLIENT_PRE_PSYNC;
|
server.master->flags |= CLIENT_PRE_PSYNC;
|
||||||
|
if (dbid != -1) selectDb(server.master,dbid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Asynchronously read the SYNC payload we receive from a master */
|
/* Asynchronously read the SYNC payload we receive from a master */
|
||||||
@ -1137,7 +1253,8 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|||||||
* time for non blocking loading. */
|
* time for non blocking loading. */
|
||||||
aeDeleteFileEvent(server.el,server.repl_transfer_s,AE_READABLE);
|
aeDeleteFileEvent(server.el,server.repl_transfer_s,AE_READABLE);
|
||||||
serverLog(LL_NOTICE, "MASTER <-> SLAVE sync: Loading DB in memory");
|
serverLog(LL_NOTICE, "MASTER <-> SLAVE sync: Loading DB in memory");
|
||||||
if (rdbLoad(server.rdb_filename) != C_OK) {
|
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
|
||||||
|
if (rdbLoad(server.rdb_filename,&rsi) != C_OK) {
|
||||||
serverLog(LL_WARNING,"Failed trying to load the MASTER synchronization DB from disk");
|
serverLog(LL_WARNING,"Failed trying to load the MASTER synchronization DB from disk");
|
||||||
cancelReplicationHandshake();
|
cancelReplicationHandshake();
|
||||||
return;
|
return;
|
||||||
@ -1145,7 +1262,20 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|||||||
/* Final setup of the connected slave <- master link */
|
/* Final setup of the connected slave <- master link */
|
||||||
zfree(server.repl_transfer_tmpfile);
|
zfree(server.repl_transfer_tmpfile);
|
||||||
close(server.repl_transfer_fd);
|
close(server.repl_transfer_fd);
|
||||||
replicationCreateMasterClient(server.repl_transfer_s);
|
replicationCreateMasterClient(server.repl_transfer_s,rsi.repl_stream_db);
|
||||||
|
server.repl_state = REPL_STATE_CONNECTED;
|
||||||
|
/* After a full resynchroniziation we use the replication ID and
|
||||||
|
* offset of the master. The secondary ID / offset are cleared since
|
||||||
|
* we are starting a new history. */
|
||||||
|
memcpy(server.replid,server.master->replid,sizeof(server.replid));
|
||||||
|
server.master_repl_offset = server.master->reploff;
|
||||||
|
clearReplicationId2();
|
||||||
|
/* Let's create the replication backlog if needed. Slaves need to
|
||||||
|
* accumulate the backlog regardless of the fact they have sub-slaves
|
||||||
|
* or not, in order to behave correctly if they are promoted to
|
||||||
|
* masters after a failover. */
|
||||||
|
if (server.repl_backlog == NULL) createReplicationBacklog();
|
||||||
|
|
||||||
serverLog(LL_NOTICE, "MASTER <-> SLAVE sync: Finished with success");
|
serverLog(LL_NOTICE, "MASTER <-> SLAVE sync: Finished with success");
|
||||||
/* Restart the AOF subsystem now that we finished the sync. This
|
/* Restart the AOF subsystem now that we finished the sync. This
|
||||||
* will trigger an AOF rewrite, and when done will start appending
|
* will trigger an AOF rewrite, and when done will start appending
|
||||||
@ -1270,7 +1400,7 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
|
|||||||
*
|
*
|
||||||
* 1) As a side effect of the function call the function removes the readable
|
* 1) As a side effect of the function call the function removes the readable
|
||||||
* event handler from "fd", unless the return value is PSYNC_WAIT_REPLY.
|
* event handler from "fd", unless the return value is PSYNC_WAIT_REPLY.
|
||||||
* 2) server.repl_master_initial_offset is set to the right value according
|
* 2) server.master_initial_offset is set to the right value according
|
||||||
* to the master reply. This will be used to populate the 'server.master'
|
* to the master reply. This will be used to populate the 'server.master'
|
||||||
* structure replication offset.
|
* structure replication offset.
|
||||||
*/
|
*/
|
||||||
@ -1281,31 +1411,31 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
|
|||||||
#define PSYNC_FULLRESYNC 3
|
#define PSYNC_FULLRESYNC 3
|
||||||
#define PSYNC_NOT_SUPPORTED 4
|
#define PSYNC_NOT_SUPPORTED 4
|
||||||
int slaveTryPartialResynchronization(int fd, int read_reply) {
|
int slaveTryPartialResynchronization(int fd, int read_reply) {
|
||||||
char *psync_runid;
|
char *psync_replid;
|
||||||
char psync_offset[32];
|
char psync_offset[32];
|
||||||
sds reply;
|
sds reply;
|
||||||
|
|
||||||
/* Writing half */
|
/* Writing half */
|
||||||
if (!read_reply) {
|
if (!read_reply) {
|
||||||
/* Initially set repl_master_initial_offset to -1 to mark the current
|
/* Initially set master_initial_offset to -1 to mark the current
|
||||||
* master run_id and offset as not valid. Later if we'll be able to do
|
* master run_id and offset as not valid. Later if we'll be able to do
|
||||||
* a FULL resync using the PSYNC command we'll set the offset at the
|
* a FULL resync using the PSYNC command we'll set the offset at the
|
||||||
* right value, so that this information will be propagated to the
|
* right value, so that this information will be propagated to the
|
||||||
* client structure representing the master into server.master. */
|
* client structure representing the master into server.master. */
|
||||||
server.repl_master_initial_offset = -1;
|
server.master_initial_offset = -1;
|
||||||
|
|
||||||
if (server.cached_master) {
|
if (server.cached_master) {
|
||||||
psync_runid = server.cached_master->replrunid;
|
psync_replid = server.cached_master->replid;
|
||||||
snprintf(psync_offset,sizeof(psync_offset),"%lld", server.cached_master->reploff+1);
|
snprintf(psync_offset,sizeof(psync_offset),"%lld", server.cached_master->reploff+1);
|
||||||
serverLog(LL_NOTICE,"Trying a partial resynchronization (request %s:%s).", psync_runid, psync_offset);
|
serverLog(LL_NOTICE,"Trying a partial resynchronization (request %s:%s).", psync_replid, psync_offset);
|
||||||
} else {
|
} else {
|
||||||
serverLog(LL_NOTICE,"Partial resynchronization not possible (no cached master)");
|
serverLog(LL_NOTICE,"Partial resynchronization not possible (no cached master)");
|
||||||
psync_runid = "?";
|
psync_replid = "?";
|
||||||
memcpy(psync_offset,"-1",3);
|
memcpy(psync_offset,"-1",3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Issue the PSYNC command */
|
/* Issue the PSYNC command */
|
||||||
reply = sendSynchronousCommand(SYNC_CMD_WRITE,fd,"PSYNC",psync_runid,psync_offset,NULL);
|
reply = sendSynchronousCommand(SYNC_CMD_WRITE,fd,"PSYNC",psync_replid,psync_offset,NULL);
|
||||||
if (reply != NULL) {
|
if (reply != NULL) {
|
||||||
serverLog(LL_WARNING,"Unable to send PSYNC to master: %s",reply);
|
serverLog(LL_WARNING,"Unable to send PSYNC to master: %s",reply);
|
||||||
sdsfree(reply);
|
sdsfree(reply);
|
||||||
@ -1327,31 +1457,31 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
|
|||||||
aeDeleteFileEvent(server.el,fd,AE_READABLE);
|
aeDeleteFileEvent(server.el,fd,AE_READABLE);
|
||||||
|
|
||||||
if (!strncmp(reply,"+FULLRESYNC",11)) {
|
if (!strncmp(reply,"+FULLRESYNC",11)) {
|
||||||
char *runid = NULL, *offset = NULL;
|
char *replid = NULL, *offset = NULL;
|
||||||
|
|
||||||
/* FULL RESYNC, parse the reply in order to extract the run id
|
/* FULL RESYNC, parse the reply in order to extract the run id
|
||||||
* and the replication offset. */
|
* and the replication offset. */
|
||||||
runid = strchr(reply,' ');
|
replid = strchr(reply,' ');
|
||||||
if (runid) {
|
if (replid) {
|
||||||
runid++;
|
replid++;
|
||||||
offset = strchr(runid,' ');
|
offset = strchr(replid,' ');
|
||||||
if (offset) offset++;
|
if (offset) offset++;
|
||||||
}
|
}
|
||||||
if (!runid || !offset || (offset-runid-1) != CONFIG_RUN_ID_SIZE) {
|
if (!replid || !offset || (offset-replid-1) != CONFIG_RUN_ID_SIZE) {
|
||||||
serverLog(LL_WARNING,
|
serverLog(LL_WARNING,
|
||||||
"Master replied with wrong +FULLRESYNC syntax.");
|
"Master replied with wrong +FULLRESYNC syntax.");
|
||||||
/* This is an unexpected condition, actually the +FULLRESYNC
|
/* This is an unexpected condition, actually the +FULLRESYNC
|
||||||
* reply means that the master supports PSYNC, but the reply
|
* reply means that the master supports PSYNC, but the reply
|
||||||
* format seems wrong. To stay safe we blank the master
|
* format seems wrong. To stay safe we blank the master
|
||||||
* runid to make sure next PSYNCs will fail. */
|
* replid to make sure next PSYNCs will fail. */
|
||||||
memset(server.repl_master_runid,0,CONFIG_RUN_ID_SIZE+1);
|
memset(server.master_replid,0,CONFIG_RUN_ID_SIZE+1);
|
||||||
} else {
|
} else {
|
||||||
memcpy(server.repl_master_runid, runid, offset-runid-1);
|
memcpy(server.master_replid, replid, offset-replid-1);
|
||||||
server.repl_master_runid[CONFIG_RUN_ID_SIZE] = '\0';
|
server.master_replid[CONFIG_RUN_ID_SIZE] = '\0';
|
||||||
server.repl_master_initial_offset = strtoll(offset,NULL,10);
|
server.master_initial_offset = strtoll(offset,NULL,10);
|
||||||
serverLog(LL_NOTICE,"Full resync from master: %s:%lld",
|
serverLog(LL_NOTICE,"Full resync from master: %s:%lld",
|
||||||
server.repl_master_runid,
|
server.master_replid,
|
||||||
server.repl_master_initial_offset);
|
server.master_initial_offset);
|
||||||
}
|
}
|
||||||
/* We are going to full resync, discard the cached master structure. */
|
/* We are going to full resync, discard the cached master structure. */
|
||||||
replicationDiscardCachedMaster();
|
replicationDiscardCachedMaster();
|
||||||
@ -1360,9 +1490,40 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!strncmp(reply,"+CONTINUE",9)) {
|
if (!strncmp(reply,"+CONTINUE",9)) {
|
||||||
/* Partial resync was accepted, set the replication state accordingly */
|
/* Partial resync was accepted. */
|
||||||
serverLog(LL_NOTICE,
|
serverLog(LL_NOTICE,
|
||||||
"Successful partial resynchronization with master.");
|
"Successful partial resynchronization with master.");
|
||||||
|
|
||||||
|
/* Check the new replication ID advertised by the master. If it
|
||||||
|
* changed, we need to set the new ID as primary ID, and set or
|
||||||
|
* secondary ID as the old master ID up to the current offset, so
|
||||||
|
* that our sub-slaves will be able to PSYNC with us after a
|
||||||
|
* disconnection. */
|
||||||
|
char *start = reply+10;
|
||||||
|
char *end = reply+9;
|
||||||
|
while(end[0] != '\r' && end[0] != '\n' && end[0] != '\0') end++;
|
||||||
|
if (end-start == CONFIG_RUN_ID_SIZE) {
|
||||||
|
char new[CONFIG_RUN_ID_SIZE+1];
|
||||||
|
memcpy(new,start,CONFIG_RUN_ID_SIZE);
|
||||||
|
new[CONFIG_RUN_ID_SIZE] = '\0';
|
||||||
|
|
||||||
|
if (strcmp(new,server.cached_master->replid)) {
|
||||||
|
/* Master ID changed. */
|
||||||
|
serverLog(LL_WARNING,"Master replication ID changed to %s",new);
|
||||||
|
|
||||||
|
/* Set the old ID as our ID2, up to the current offset+1. */
|
||||||
|
memcpy(server.replid2,server.cached_master->replid,
|
||||||
|
sizeof(server.replid2));
|
||||||
|
server.second_replid_offset = server.master_repl_offset+1;
|
||||||
|
|
||||||
|
/* Update the cached master ID and our own primary ID to the
|
||||||
|
* new one. */
|
||||||
|
memcpy(server.replid,new,sizeof(server.replid));
|
||||||
|
memcpy(server.cached_master->replid,new,sizeof(server.replid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup the replication to continue. */
|
||||||
sdsfree(reply);
|
sdsfree(reply);
|
||||||
replicationResurrectCachedMaster(fd);
|
replicationResurrectCachedMaster(fd);
|
||||||
return PSYNC_CONTINUE;
|
return PSYNC_CONTINUE;
|
||||||
@ -1386,6 +1547,8 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
|
|||||||
return PSYNC_NOT_SUPPORTED;
|
return PSYNC_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This handler fires when the non blocking connect was able to
|
||||||
|
* establish a connection with the master. */
|
||||||
void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
|
void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||||
char tmpfile[256], *err = NULL;
|
char tmpfile[256], *err = NULL;
|
||||||
int dfd, maxtries = 5;
|
int dfd, maxtries = 5;
|
||||||
@ -1402,7 +1565,8 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for errors in the socket. */
|
/* Check for errors in the socket: after a non blocking connect() we
|
||||||
|
* may find that the socket is in error state. */
|
||||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) == -1)
|
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) == -1)
|
||||||
sockerr = errno;
|
sockerr = errno;
|
||||||
if (sockerr) {
|
if (sockerr) {
|
||||||
@ -1531,13 +1695,15 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|||||||
server.repl_state = REPL_STATE_SEND_CAPA;
|
server.repl_state = REPL_STATE_SEND_CAPA;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Inform the master of our capabilities. While we currently send
|
/* Inform the master of our (slave) capabilities.
|
||||||
* just one capability, it is possible to chain new capabilities here
|
*
|
||||||
* in the form of REPLCONF capa X capa Y capa Z ...
|
* EOF: supports EOF-style RDB transfer for diskless replication.
|
||||||
|
* PSYNC2: supports PSYNC v2, so understands +CONTINUE <new repl ID>.
|
||||||
|
*
|
||||||
* The master will ignore capabilities it does not understand. */
|
* The master will ignore capabilities it does not understand. */
|
||||||
if (server.repl_state == REPL_STATE_SEND_CAPA) {
|
if (server.repl_state == REPL_STATE_SEND_CAPA) {
|
||||||
err = sendSynchronousCommand(SYNC_CMD_WRITE,fd,"REPLCONF",
|
err = sendSynchronousCommand(SYNC_CMD_WRITE,fd,"REPLCONF",
|
||||||
"capa","eof",NULL);
|
"capa","eof","capa","psync2",NULL);
|
||||||
if (err) goto write_error;
|
if (err) goto write_error;
|
||||||
sdsfree(err);
|
sdsfree(err);
|
||||||
server.repl_state = REPL_STATE_RECEIVE_CAPA;
|
server.repl_state = REPL_STATE_RECEIVE_CAPA;
|
||||||
@ -1591,14 +1757,14 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* PSYNC failed or is not supported: we want our slaves to resync with us
|
/* PSYNC failed or is not supported: we want our slaves to resync with us
|
||||||
* as well, if we have any (chained replication case). The mater may
|
* as well, if we have any sub-slaves. The mater may transfer us an
|
||||||
* transfer us an entirely different data set and we have no way to
|
* entirely different data set and we have no way to incrementally feed
|
||||||
* incrementally feed our slaves after that. */
|
* our slaves after that. */
|
||||||
disconnectSlaves(); /* Force our slaves to resync with us as well. */
|
disconnectSlaves(); /* Force our slaves to resync with us as well. */
|
||||||
freeReplicationBacklog(); /* Don't allow our chained slaves to PSYNC. */
|
freeReplicationBacklog(); /* Don't allow our chained slaves to PSYNC. */
|
||||||
|
|
||||||
/* Fall back to SYNC if needed. Otherwise psync_result == PSYNC_FULLRESYNC
|
/* Fall back to SYNC if needed. Otherwise psync_result == PSYNC_FULLRESYNC
|
||||||
* and the server.repl_master_runid and repl_master_initial_offset are
|
* and the server.master_replid and master_initial_offset are
|
||||||
* already populated. */
|
* already populated. */
|
||||||
if (psync_result == PSYNC_NOT_SUPPORTED) {
|
if (psync_result == PSYNC_NOT_SUPPORTED) {
|
||||||
serverLog(LL_NOTICE,"Retrying with SYNC...");
|
serverLog(LL_NOTICE,"Retrying with SYNC...");
|
||||||
@ -1727,15 +1893,23 @@ int cancelReplicationHandshake(void) {
|
|||||||
|
|
||||||
/* Set replication to the specified master address and port. */
|
/* Set replication to the specified master address and port. */
|
||||||
void replicationSetMaster(char *ip, int port) {
|
void replicationSetMaster(char *ip, int port) {
|
||||||
|
int was_master = server.masterhost == NULL;
|
||||||
|
|
||||||
sdsfree(server.masterhost);
|
sdsfree(server.masterhost);
|
||||||
server.masterhost = sdsnew(ip);
|
server.masterhost = sdsnew(ip);
|
||||||
server.masterport = port;
|
server.masterport = port;
|
||||||
if (server.master) freeClient(server.master);
|
if (server.master) {
|
||||||
|
freeClient(server.master);
|
||||||
|
}
|
||||||
disconnectAllBlockedClients(); /* Clients blocked in master, now slave. */
|
disconnectAllBlockedClients(); /* Clients blocked in master, now slave. */
|
||||||
disconnectSlaves(); /* Force our slaves to resync with us as well. */
|
|
||||||
replicationDiscardCachedMaster(); /* Don't try a PSYNC. */
|
/* Force our slaves to resync with us as well. They may hopefully be able
|
||||||
freeReplicationBacklog(); /* Don't allow our chained slaves to PSYNC. */
|
* to partially resync with us, but we can notify the replid change. */
|
||||||
|
disconnectSlaves();
|
||||||
cancelReplicationHandshake();
|
cancelReplicationHandshake();
|
||||||
|
/* Before destroying our master state, create a cached master using
|
||||||
|
* our own parameters, to later PSYNC with the new master. */
|
||||||
|
if (was_master) replicationCacheMasterUsingMyself();
|
||||||
server.repl_state = REPL_STATE_CONNECT;
|
server.repl_state = REPL_STATE_CONNECT;
|
||||||
server.master_repl_offset = 0;
|
server.master_repl_offset = 0;
|
||||||
server.repl_down_since = 0;
|
server.repl_down_since = 0;
|
||||||
@ -1746,20 +1920,26 @@ void replicationUnsetMaster(void) {
|
|||||||
if (server.masterhost == NULL) return; /* Nothing to do. */
|
if (server.masterhost == NULL) return; /* Nothing to do. */
|
||||||
sdsfree(server.masterhost);
|
sdsfree(server.masterhost);
|
||||||
server.masterhost = NULL;
|
server.masterhost = NULL;
|
||||||
if (server.master) {
|
/* When a slave is turned into a master, the current replication ID
|
||||||
if (listLength(server.slaves) == 0) {
|
* (that was inherited from the master at synchronization time) is
|
||||||
/* If this instance is turned into a master and there are no
|
* used as secondary ID up to the current offset, and a new replication
|
||||||
* slaves, it inherits the replication offset from the master.
|
* ID is created to continue with a new replication history. */
|
||||||
* Under certain conditions this makes replicas comparable by
|
shiftReplicationId();
|
||||||
* replication offset to understand what is the most updated. */
|
if (server.master) freeClient(server.master);
|
||||||
server.master_repl_offset = server.master->reploff;
|
|
||||||
freeReplicationBacklog();
|
|
||||||
}
|
|
||||||
freeClient(server.master);
|
|
||||||
}
|
|
||||||
replicationDiscardCachedMaster();
|
replicationDiscardCachedMaster();
|
||||||
cancelReplicationHandshake();
|
cancelReplicationHandshake();
|
||||||
|
/* Disconnecting all the slaves is required: we need to inform slaves
|
||||||
|
* of the replication ID change (see shiftReplicationId() call). However
|
||||||
|
* the slaves will be able to partially resync with us, so it will be
|
||||||
|
* a very fast reconnection. */
|
||||||
|
disconnectSlaves();
|
||||||
server.repl_state = REPL_STATE_NONE;
|
server.repl_state = REPL_STATE_NONE;
|
||||||
|
|
||||||
|
/* We need to make sure the new master will start the replication stream
|
||||||
|
* with a SELECT statement. This is forced after a full resync, but
|
||||||
|
* with PSYNC version 2, there is no need for full resync after a
|
||||||
|
* master switch. */
|
||||||
|
server.slaveseldb = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function is called when the slave lose the connection with the
|
/* This function is called when the slave lose the connection with the
|
||||||
@ -1931,6 +2111,31 @@ void replicationCacheMaster(client *c) {
|
|||||||
replicationHandleMasterDisconnection();
|
replicationHandleMasterDisconnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function is called when a master is turend into a slave, in order to
|
||||||
|
* create from scratch a cached master for the new client, that will allow
|
||||||
|
* to PSYNC with the slave that was promoted as the new master after a
|
||||||
|
* failover.
|
||||||
|
*
|
||||||
|
* Assuming this instance was previously the master instance of the new master,
|
||||||
|
* the new master will accept its replication ID, and potentiall also the
|
||||||
|
* current offset if no data was lost during the failover. So we use our
|
||||||
|
* current replication ID and offset in order to synthesize a cached master. */
|
||||||
|
void replicationCacheMasterUsingMyself(void) {
|
||||||
|
/* The master client we create can be set to any DBID, because
|
||||||
|
* the new master will start its replication stream with SELECT. */
|
||||||
|
server.master_initial_offset = server.master_repl_offset;
|
||||||
|
replicationCreateMasterClient(-1,-1);
|
||||||
|
|
||||||
|
/* Use our own ID / offset. */
|
||||||
|
memcpy(server.master->replid, server.replid, sizeof(server.replid));
|
||||||
|
|
||||||
|
/* Set as cached master. */
|
||||||
|
unlinkClient(server.master);
|
||||||
|
server.cached_master = server.master;
|
||||||
|
server.master = NULL;
|
||||||
|
serverLog(LL_NOTICE,"Before turning into a slave, using my master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.");
|
||||||
|
}
|
||||||
|
|
||||||
/* Free a cached master, called when there are no longer the conditions for
|
/* Free a cached master, called when there are no longer the conditions for
|
||||||
* a partial resync on reconnection. */
|
* a partial resync on reconnection. */
|
||||||
void replicationDiscardCachedMaster(void) {
|
void replicationDiscardCachedMaster(void) {
|
||||||
@ -2290,7 +2495,9 @@ void replicationCron(void) {
|
|||||||
robj *ping_argv[1];
|
robj *ping_argv[1];
|
||||||
|
|
||||||
/* First, send PING according to ping_slave_period. */
|
/* First, send PING according to ping_slave_period. */
|
||||||
if ((replication_cron_loops % server.repl_ping_slave_period) == 0) {
|
if ((replication_cron_loops % server.repl_ping_slave_period) == 0 &&
|
||||||
|
listLength(server.slaves))
|
||||||
|
{
|
||||||
ping_argv[0] = createStringObject("PING",4);
|
ping_argv[0] = createStringObject("PING",4);
|
||||||
replicationFeedSlaves(server.slaves, server.slaveseldb,
|
replicationFeedSlaves(server.slaves, server.slaveseldb,
|
||||||
ping_argv, 1);
|
ping_argv, 1);
|
||||||
@ -2299,20 +2506,32 @@ void replicationCron(void) {
|
|||||||
|
|
||||||
/* Second, send a newline to all the slaves in pre-synchronization
|
/* Second, send a newline to all the slaves in pre-synchronization
|
||||||
* stage, that is, slaves waiting for the master to create the RDB file.
|
* stage, that is, slaves waiting for the master to create the RDB file.
|
||||||
|
*
|
||||||
|
* Also send the a newline to all the chained slaves we have, if we lost
|
||||||
|
* connection from our master, to keep the slaves aware that their
|
||||||
|
* master is online. This is needed since sub-slaves only receive proxied
|
||||||
|
* data from top-level masters, so there is no explicit pinging in order
|
||||||
|
* to avoid altering the replication offsets. This special out of band
|
||||||
|
* pings (newlines) can be sent, they will have no effect in the offset.
|
||||||
|
*
|
||||||
* The newline will be ignored by the slave but will refresh the
|
* The newline will be ignored by the slave but will refresh the
|
||||||
* last-io timer preventing a timeout. In this case we ignore the
|
* last interaction timer preventing a timeout. In this case we ignore the
|
||||||
* ping period and refresh the connection once per second since certain
|
* ping period and refresh the connection once per second since certain
|
||||||
* timeouts are set at a few seconds (example: PSYNC response). */
|
* timeouts are set at a few seconds (example: PSYNC response). */
|
||||||
listRewind(server.slaves,&li);
|
listRewind(server.slaves,&li);
|
||||||
while((ln = listNext(&li))) {
|
while((ln = listNext(&li))) {
|
||||||
client *slave = ln->value;
|
client *slave = ln->value;
|
||||||
|
|
||||||
if (slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START ||
|
int is_presync =
|
||||||
|
(slave->replstate == SLAVE_STATE_WAIT_BGSAVE_START ||
|
||||||
(slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END &&
|
(slave->replstate == SLAVE_STATE_WAIT_BGSAVE_END &&
|
||||||
server.rdb_child_type != RDB_CHILD_TYPE_SOCKET))
|
server.rdb_child_type != RDB_CHILD_TYPE_SOCKET));
|
||||||
{
|
int is_subslave = server.masterhost && server.master == NULL &&
|
||||||
|
slave->replstate == SLAVE_STATE_ONLINE;
|
||||||
|
|
||||||
|
if (is_presync || is_subslave) {
|
||||||
if (write(slave->fd, "\n", 1) == -1) {
|
if (write(slave->fd, "\n", 1) == -1) {
|
||||||
/* Don't worry, it's just a ping. */
|
/* Don't worry about socket errors, it's just a ping. */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2337,10 +2556,14 @@ void replicationCron(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we have no attached slaves and there is a replication backlog
|
/* If this is a master without attached slaves and there is a replication
|
||||||
* using memory, free it after some (configured) time. */
|
* backlog active, in order to reclaim memory we can free it after some
|
||||||
|
* (configured) time. Note that this cannot be done for slaves: slaves
|
||||||
|
* without sub-slaves attached should still accumulate data into the
|
||||||
|
* backlog, in order to reply to PSYNC queries if they are turned into
|
||||||
|
* masters after a failover. */
|
||||||
if (listLength(server.slaves) == 0 && server.repl_backlog_time_limit &&
|
if (listLength(server.slaves) == 0 && server.repl_backlog_time_limit &&
|
||||||
server.repl_backlog)
|
server.repl_backlog && server.masterhost == NULL)
|
||||||
{
|
{
|
||||||
time_t idle = server.unixtime - server.repl_no_slaves_since;
|
time_t idle = server.unixtime - server.repl_no_slaves_since;
|
||||||
|
|
||||||
|
30
src/server.c
30
src/server.c
@ -1079,7 +1079,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
|||||||
{
|
{
|
||||||
serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...",
|
serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...",
|
||||||
sp->changes, (int)sp->seconds);
|
sp->changes, (int)sp->seconds);
|
||||||
rdbSaveBackground(server.rdb_filename);
|
rdbSaveBackground(server.rdb_filename,NULL);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1151,7 +1151,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
|||||||
(server.unixtime-server.lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY ||
|
(server.unixtime-server.lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY ||
|
||||||
server.lastbgsave_status == C_OK))
|
server.lastbgsave_status == C_OK))
|
||||||
{
|
{
|
||||||
if (rdbSaveBackground(server.rdb_filename) == C_OK)
|
if (rdbSaveBackground(server.rdb_filename,NULL) == C_OK)
|
||||||
server.rdb_bgsave_scheduled = 0;
|
server.rdb_bgsave_scheduled = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1309,10 +1309,11 @@ void initServerConfig(void) {
|
|||||||
int j;
|
int j;
|
||||||
|
|
||||||
getRandomHexChars(server.runid,CONFIG_RUN_ID_SIZE);
|
getRandomHexChars(server.runid,CONFIG_RUN_ID_SIZE);
|
||||||
|
server.runid[CONFIG_RUN_ID_SIZE] = '\0';
|
||||||
|
changeReplicationId();
|
||||||
server.configfile = NULL;
|
server.configfile = NULL;
|
||||||
server.executable = NULL;
|
server.executable = NULL;
|
||||||
server.hz = CONFIG_DEFAULT_HZ;
|
server.hz = CONFIG_DEFAULT_HZ;
|
||||||
server.runid[CONFIG_RUN_ID_SIZE] = '\0';
|
|
||||||
server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
|
server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
|
||||||
server.port = CONFIG_DEFAULT_SERVER_PORT;
|
server.port = CONFIG_DEFAULT_SERVER_PORT;
|
||||||
server.tcp_backlog = CONFIG_DEFAULT_TCP_BACKLOG;
|
server.tcp_backlog = CONFIG_DEFAULT_TCP_BACKLOG;
|
||||||
@ -1409,7 +1410,7 @@ void initServerConfig(void) {
|
|||||||
server.masterport = 6379;
|
server.masterport = 6379;
|
||||||
server.master = NULL;
|
server.master = NULL;
|
||||||
server.cached_master = NULL;
|
server.cached_master = NULL;
|
||||||
server.repl_master_initial_offset = -1;
|
server.master_initial_offset = -1;
|
||||||
server.repl_state = REPL_STATE_NONE;
|
server.repl_state = REPL_STATE_NONE;
|
||||||
server.repl_syncio_timeout = CONFIG_REPL_SYNCIO_TIMEOUT;
|
server.repl_syncio_timeout = CONFIG_REPL_SYNCIO_TIMEOUT;
|
||||||
server.repl_serve_stale_data = CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA;
|
server.repl_serve_stale_data = CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA;
|
||||||
@ -2471,7 +2472,7 @@ int prepareForShutdown(int flags) {
|
|||||||
if ((server.saveparamslen > 0 && !nosave) || save) {
|
if ((server.saveparamslen > 0 && !nosave) || save) {
|
||||||
serverLog(LL_NOTICE,"Saving the final RDB snapshot before exiting.");
|
serverLog(LL_NOTICE,"Saving the final RDB snapshot before exiting.");
|
||||||
/* Snapshotting. Perform a SYNC SAVE and exit */
|
/* Snapshotting. Perform a SYNC SAVE and exit */
|
||||||
if (rdbSave(server.rdb_filename) != C_OK) {
|
if (rdbSave(server.rdb_filename,NULL) != C_OK) {
|
||||||
/* Ooops.. error saving! The best we can do is to continue
|
/* Ooops.. error saving! The best we can do is to continue
|
||||||
* operating. Note that if there was a background saving process,
|
* operating. Note that if there was a background saving process,
|
||||||
* in the next cron() Redis will be notified that the background
|
* in the next cron() Redis will be notified that the background
|
||||||
@ -3135,12 +3136,18 @@ sds genRedisInfoString(char *section) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
info = sdscatprintf(info,
|
info = sdscatprintf(info,
|
||||||
|
"master_replid:%s\r\n"
|
||||||
|
"master_replid2:%s\r\n"
|
||||||
"master_repl_offset:%lld\r\n"
|
"master_repl_offset:%lld\r\n"
|
||||||
|
"second_repl_offset:%lld\r\n"
|
||||||
"repl_backlog_active:%d\r\n"
|
"repl_backlog_active:%d\r\n"
|
||||||
"repl_backlog_size:%lld\r\n"
|
"repl_backlog_size:%lld\r\n"
|
||||||
"repl_backlog_first_byte_offset:%lld\r\n"
|
"repl_backlog_first_byte_offset:%lld\r\n"
|
||||||
"repl_backlog_histlen:%lld\r\n",
|
"repl_backlog_histlen:%lld\r\n",
|
||||||
|
server.replid,
|
||||||
|
server.replid2,
|
||||||
server.master_repl_offset,
|
server.master_repl_offset,
|
||||||
|
server.second_replid_offset,
|
||||||
server.repl_backlog != NULL,
|
server.repl_backlog != NULL,
|
||||||
server.repl_backlog_size,
|
server.repl_backlog_size,
|
||||||
server.repl_backlog_off,
|
server.repl_backlog_off,
|
||||||
@ -3416,9 +3423,20 @@ void loadDataFromDisk(void) {
|
|||||||
if (loadAppendOnlyFile(server.aof_filename) == C_OK)
|
if (loadAppendOnlyFile(server.aof_filename) == C_OK)
|
||||||
serverLog(LL_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
|
serverLog(LL_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
|
||||||
} else {
|
} else {
|
||||||
if (rdbLoad(server.rdb_filename) == C_OK) {
|
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
|
||||||
|
if (rdbLoad(server.rdb_filename,&rsi) == C_OK) {
|
||||||
serverLog(LL_NOTICE,"DB loaded from disk: %.3f seconds",
|
serverLog(LL_NOTICE,"DB loaded from disk: %.3f seconds",
|
||||||
(float)(ustime()-start)/1000000);
|
(float)(ustime()-start)/1000000);
|
||||||
|
|
||||||
|
/* Restore the replication ID / offset from the RDB file. */
|
||||||
|
if (rsi.repl_id_is_set && rsi.repl_offset != -1) {
|
||||||
|
memcpy(server.replid,rsi.repl_id,sizeof(server.replid));
|
||||||
|
server.master_repl_offset = rsi.repl_offset;
|
||||||
|
/* If we are a slave, create a cached master from this
|
||||||
|
* information, in order to allow partial resynchronizations
|
||||||
|
* with masters. */
|
||||||
|
if (server.masterhost) replicationCacheMasterUsingMyself();
|
||||||
|
}
|
||||||
} else if (errno != ENOENT) {
|
} else if (errno != ENOENT) {
|
||||||
serverLog(LL_WARNING,"Fatal error loading the DB: %s. Exiting.",strerror(errno));
|
serverLog(LL_WARNING,"Fatal error loading the DB: %s. Exiting.",strerror(errno));
|
||||||
exit(1);
|
exit(1);
|
||||||
|
52
src/server.h
52
src/server.h
@ -294,6 +294,7 @@ typedef long long mstime_t; /* millisecond time type. */
|
|||||||
/* Slave capabilities. */
|
/* Slave capabilities. */
|
||||||
#define SLAVE_CAPA_NONE 0
|
#define SLAVE_CAPA_NONE 0
|
||||||
#define SLAVE_CAPA_EOF (1<<0) /* Can parse the RDB EOF streaming format. */
|
#define SLAVE_CAPA_EOF (1<<0) /* Can parse the RDB EOF streaming format. */
|
||||||
|
#define SLAVE_CAPA_PSYNC2 (1<<1) /* Supports PSYNC2 protocol. */
|
||||||
|
|
||||||
/* Synchronous read timeout - slave side */
|
/* Synchronous read timeout - slave side */
|
||||||
#define CONFIG_REPL_SYNCIO_TIMEOUT 5
|
#define CONFIG_REPL_SYNCIO_TIMEOUT 5
|
||||||
@ -679,8 +680,8 @@ typedef struct client {
|
|||||||
long long psync_initial_offset; /* FULLRESYNC reply offset other slaves
|
long long psync_initial_offset; /* FULLRESYNC reply offset other slaves
|
||||||
copying this slave output buffer
|
copying this slave output buffer
|
||||||
should use. */
|
should use. */
|
||||||
char replrunid[CONFIG_RUN_ID_SIZE+1]; /* Master run id if is a master. */
|
char replid[CONFIG_RUN_ID_SIZE+1]; /* Master replication ID (if master). */
|
||||||
int slave_listening_port; /* As configured with: REPLCONF listening-port */
|
int slave_listening_port; /* As configured with: SLAVECONF listening-port */
|
||||||
char slave_ip[NET_IP_STR_LEN]; /* Optionally given by REPLCONF ip-address */
|
char slave_ip[NET_IP_STR_LEN]; /* Optionally given by REPLCONF ip-address */
|
||||||
int slave_capa; /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */
|
int slave_capa; /* Slave capabilities: SLAVE_CAPA_* bitwise OR. */
|
||||||
multiState mstate; /* MULTI/EXEC state */
|
multiState mstate; /* MULTI/EXEC state */
|
||||||
@ -803,6 +804,26 @@ struct redisMemOverhead {
|
|||||||
} *db;
|
} *db;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* This structure can be optionally passed to RDB save/load functions in
|
||||||
|
* order to implement additional functionalities, by storing and loading
|
||||||
|
* metadata to the RDB file.
|
||||||
|
*
|
||||||
|
* Currently the only use is to select a DB at load time, useful in
|
||||||
|
* replication in order to make sure that chained slaves (slaves of slaves)
|
||||||
|
* select the correct DB and are able to accept the stream coming from the
|
||||||
|
* top-level master. */
|
||||||
|
typedef struct rdbSaveInfo {
|
||||||
|
/* Used saving and loading. */
|
||||||
|
int repl_stream_db; /* DB to select in server.master client. */
|
||||||
|
|
||||||
|
/* Used only loading. */
|
||||||
|
int repl_id_is_set; /* True if repl_id field is set. */
|
||||||
|
char repl_id[CONFIG_RUN_ID_SIZE+1]; /* Replication ID. */
|
||||||
|
long long repl_offset; /* Replication offset. */
|
||||||
|
} rdbSaveInfo;
|
||||||
|
|
||||||
|
#define RDB_SAVE_INFO_INIT {-1,0,"000000000000000000000000000000",-1}
|
||||||
|
|
||||||
/*-----------------------------------------------------------------------------
|
/*-----------------------------------------------------------------------------
|
||||||
* Global server state
|
* Global server state
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
@ -988,15 +1009,19 @@ struct redisServer {
|
|||||||
char *syslog_ident; /* Syslog ident */
|
char *syslog_ident; /* Syslog ident */
|
||||||
int syslog_facility; /* Syslog facility */
|
int syslog_facility; /* Syslog facility */
|
||||||
/* Replication (master) */
|
/* Replication (master) */
|
||||||
|
char replid[CONFIG_RUN_ID_SIZE+1]; /* My current replication ID. */
|
||||||
|
char replid2[CONFIG_RUN_ID_SIZE+1]; /* replid inherited from master*/
|
||||||
|
long long master_repl_offset; /* My current replication offset */
|
||||||
|
long long second_replid_offset; /* Accept offsets up to this for replid2. */
|
||||||
int slaveseldb; /* Last SELECTed DB in replication output */
|
int slaveseldb; /* Last SELECTed DB in replication output */
|
||||||
long long master_repl_offset; /* Global replication offset */
|
|
||||||
int repl_ping_slave_period; /* Master pings the slave every N seconds */
|
int repl_ping_slave_period; /* Master pings the slave every N seconds */
|
||||||
char *repl_backlog; /* Replication backlog for partial syncs */
|
char *repl_backlog; /* Replication backlog for partial syncs */
|
||||||
long long repl_backlog_size; /* Backlog circular buffer size */
|
long long repl_backlog_size; /* Backlog circular buffer size */
|
||||||
long long repl_backlog_histlen; /* Backlog actual data length */
|
long long repl_backlog_histlen; /* Backlog actual data length */
|
||||||
long long repl_backlog_idx; /* Backlog circular buffer current offset */
|
long long repl_backlog_idx; /* Backlog circular buffer current offset,
|
||||||
long long repl_backlog_off; /* Replication offset of first byte in the
|
that is the next byte will'll write to.*/
|
||||||
backlog buffer. */
|
long long repl_backlog_off; /* Replication "master offset" of first
|
||||||
|
byte in the replication backlog buffer.*/
|
||||||
time_t repl_backlog_time_limit; /* Time without slaves after the backlog
|
time_t repl_backlog_time_limit; /* Time without slaves after the backlog
|
||||||
gets released. */
|
gets released. */
|
||||||
time_t repl_no_slaves_since; /* We have no slaves since that time.
|
time_t repl_no_slaves_since; /* We have no slaves since that time.
|
||||||
@ -1029,8 +1054,11 @@ struct redisServer {
|
|||||||
int slave_priority; /* Reported in INFO and used by Sentinel. */
|
int slave_priority; /* Reported in INFO and used by Sentinel. */
|
||||||
int slave_announce_port; /* Give the master this listening port. */
|
int slave_announce_port; /* Give the master this listening port. */
|
||||||
char *slave_announce_ip; /* Give the master this ip address. */
|
char *slave_announce_ip; /* Give the master this ip address. */
|
||||||
char repl_master_runid[CONFIG_RUN_ID_SIZE+1]; /* Master run id for PSYNC.*/
|
/* The following two fields is where we store master PSYNC replid/offset
|
||||||
long long repl_master_initial_offset; /* Master PSYNC offset. */
|
* while the PSYNC is in progress. At the end we'll copy the fields into
|
||||||
|
* the server->master client structure. */
|
||||||
|
char master_replid[CONFIG_RUN_ID_SIZE+1]; /* Master PSYNC runid. */
|
||||||
|
long long master_initial_offset; /* Master PSYNC offset. */
|
||||||
int repl_slave_lazy_flush; /* Lazy FLUSHALL before loading DB? */
|
int repl_slave_lazy_flush; /* Lazy FLUSHALL before loading DB? */
|
||||||
/* Replication script cache. */
|
/* Replication script cache. */
|
||||||
dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */
|
dict *repl_scriptcache_dict; /* SHA1 all slaves are aware of. */
|
||||||
@ -1259,6 +1287,7 @@ void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
|
|||||||
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);
|
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);
|
||||||
void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask);
|
void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask);
|
||||||
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask);
|
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask);
|
||||||
|
void addReplyString(client *c, const char *s, size_t len);
|
||||||
void addReplyBulk(client *c, robj *obj);
|
void addReplyBulk(client *c, robj *obj);
|
||||||
void addReplyBulkCString(client *c, const char *s);
|
void addReplyBulkCString(client *c, const char *s);
|
||||||
void addReplyBulkCBuffer(client *c, const void *p, size_t len);
|
void addReplyBulkCBuffer(client *c, const void *p, size_t len);
|
||||||
@ -1393,6 +1422,7 @@ ssize_t syncReadLine(int fd, char *ptr, ssize_t size, long long timeout);
|
|||||||
|
|
||||||
/* Replication */
|
/* Replication */
|
||||||
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
|
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
|
||||||
|
void replicationFeedSlavesFromMasterStream(list *slaves, char *buf, size_t buflen);
|
||||||
void replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv, int argc);
|
void replicationFeedMonitors(client *c, list *monitors, int dictid, robj **argv, int argc);
|
||||||
void updateSlavesWaitingBgsave(int bgsaveerr, int type);
|
void updateSlavesWaitingBgsave(int bgsaveerr, int type);
|
||||||
void replicationCron(void);
|
void replicationCron(void);
|
||||||
@ -1414,6 +1444,10 @@ long long replicationGetSlaveOffset(void);
|
|||||||
char *replicationGetSlaveName(client *c);
|
char *replicationGetSlaveName(client *c);
|
||||||
long long getPsyncInitialOffset(void);
|
long long getPsyncInitialOffset(void);
|
||||||
int replicationSetupSlaveForFullResync(client *slave, long long offset);
|
int replicationSetupSlaveForFullResync(client *slave, long long offset);
|
||||||
|
void changeReplicationId(void);
|
||||||
|
void clearReplicationId2(void);
|
||||||
|
void chopReplicationBacklog(void);
|
||||||
|
void replicationCacheMasterUsingMyself(void);
|
||||||
|
|
||||||
/* Generic persistence functions */
|
/* Generic persistence functions */
|
||||||
void startLoading(FILE *fp);
|
void startLoading(FILE *fp);
|
||||||
@ -1422,7 +1456,7 @@ void stopLoading(void);
|
|||||||
|
|
||||||
/* RDB persistence */
|
/* RDB persistence */
|
||||||
#include "rdb.h"
|
#include "rdb.h"
|
||||||
int rdbSaveRio(rio *rdb, int *error, int flags);
|
int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi);
|
||||||
|
|
||||||
/* AOF persistence */
|
/* AOF persistence */
|
||||||
void flushAppendOnlyFile(int force);
|
void flushAppendOnlyFile(int force);
|
||||||
|
Loading…
Reference in New Issue
Block a user