2012-11-08 12:25:23 -05:00
/* Asynchronous replication implementation.
*
* Copyright ( c ) 2009 - 2012 , Salvatore Sanfilippo < antirez at gmail dot com >
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* * Redistributions of source code must retain the above copyright notice ,
* this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR
* CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
* CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE )
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE .
*/
2015-07-26 09:14:57 -04:00
# include "server.h"
2010-06-21 18:07:48 -04:00
# include <sys/time.h>
# include <unistd.h>
# include <fcntl.h>
2012-09-17 06:45:57 -04:00
# include <sys/socket.h>
2010-06-21 18:07:48 -04:00
# include <sys/stat.h>
2013-01-30 12:33:16 -05:00
void replicationDiscardCachedMaster ( void ) ;
void replicationResurrectCachedMaster ( int newfd ) ;
2013-12-04 09:52:20 -05:00
void replicationSendAck ( void ) ;
2015-07-26 09:20:46 -04:00
void putSlaveOnline ( client * slave ) ;
2013-01-30 12:33:16 -05:00
2014-10-27 06:58:20 -04:00
/* --------------------------- Utility functions ---------------------------- */
/* Return the pointer to a string representing the slave ip:listening_port
* pair . Mostly useful for logging , since we want to log a slave using its
* IP address and it ' s listening port which is more clear for the user , for
* example : " Closing connection with slave 10.1.2.3:6380 " . */
2015-07-26 09:20:46 -04:00
char * replicationGetSlaveName ( client * c ) {
2015-07-27 03:41:48 -04:00
static char buf [ NET_PEER_ID_LEN ] ;
char ip [ NET_IP_STR_LEN ] ;
2014-10-27 06:58:20 -04:00
ip [ 0 ] = ' \0 ' ;
buf [ 0 ] = ' \0 ' ;
if ( anetPeerToString ( c - > fd , ip , sizeof ( ip ) , NULL ) ! = - 1 ) {
if ( c - > slave_listening_port )
2014-12-11 12:20:30 -05:00
anetFormatAddr ( buf , sizeof ( buf ) , ip , c - > slave_listening_port ) ;
2014-10-27 06:58:20 -04:00
else
snprintf ( buf , sizeof ( buf ) , " %s:<unknown-slave-port> " , ip ) ;
} else {
snprintf ( buf , sizeof ( buf ) , " client id #%llu " ,
( unsigned long long ) c - > id ) ;
}
return buf ;
}
2010-11-04 12:29:53 -04:00
/* ---------------------------------- MASTER -------------------------------- */
2013-01-30 12:33:16 -05:00
void createReplicationBacklog ( void ) {
2015-07-26 09:29:53 -04:00
serverAssert ( server . repl_backlog = = NULL ) ;
2013-01-30 12:33:16 -05:00
server . repl_backlog = zmalloc ( server . repl_backlog_size ) ;
server . repl_backlog_histlen = 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
* byte we have is the next byte that will be generated for the
* replication stream . */
server . repl_backlog_off = server . master_repl_offset + 1 ;
}
/* This function is called when the user modifies the replication backlog
* size at runtime . It is up to the function to both update the
* server . repl_backlog_size and to resize the buffer and setup it so that
* it contains the same data as the previous one ( possibly less data , but
* the most recent bytes , or the same data and more free space in case the
* buffer is enlarged ) . */
void resizeReplicationBacklog ( long long newsize ) {
2015-07-27 03:41:48 -04:00
if ( newsize < CONFIG_REPL_BACKLOG_MIN_SIZE )
newsize = CONFIG_REPL_BACKLOG_MIN_SIZE ;
2013-01-30 12:33:16 -05:00
if ( server . repl_backlog_size = = newsize ) return ;
server . repl_backlog_size = newsize ;
if ( server . repl_backlog ! = NULL ) {
/* What we actually do is to flush the old buffer and realloc a new
* empty one . It will refill with new data incrementally .
* The reason is that copying a few gigabytes adds latency and even
* worse often we need to alloc additional space before freeing the
* old buffer . */
zfree ( server . repl_backlog ) ;
server . repl_backlog = zmalloc ( server . repl_backlog_size ) ;
server . repl_backlog_histlen = 0 ;
server . repl_backlog_idx = 0 ;
2014-01-16 23:02:38 -05:00
/* Next byte we have is... the next since the buffer is empty. */
2013-01-30 12:33:16 -05:00
server . repl_backlog_off = server . master_repl_offset + 1 ;
}
}
void freeReplicationBacklog ( void ) {
2015-07-26 09:29:53 -04:00
serverAssert ( listLength ( server . slaves ) = = 0 ) ;
2013-01-30 12:33:16 -05:00
zfree ( server . repl_backlog ) ;
server . repl_backlog = NULL ;
}
/* Add data to the replication backlog.
* This function also increments the global replication offset stored at
* server . master_repl_offset , because there is no case where we want to feed
* the backlog without incrementing the buffer . */
void feedReplicationBacklog ( void * ptr , size_t len ) {
unsigned char * p = ptr ;
server . master_repl_offset + = len ;
/* This is a circular buffer, so write as much data we can at every
* iteration and rewind the " idx " index if we reach the limit . */
while ( len ) {
size_t thislen = server . repl_backlog_size - server . repl_backlog_idx ;
if ( thislen > len ) thislen = len ;
memcpy ( server . repl_backlog + server . repl_backlog_idx , p , thislen ) ;
server . repl_backlog_idx + = thislen ;
if ( server . repl_backlog_idx = = server . repl_backlog_size )
server . repl_backlog_idx = 0 ;
len - = thislen ;
p + = thislen ;
server . repl_backlog_histlen + = thislen ;
}
if ( server . repl_backlog_histlen > server . repl_backlog_size )
server . repl_backlog_histlen = server . repl_backlog_size ;
/* Set the offset of the first byte we have in the backlog. */
server . repl_backlog_off = server . master_repl_offset -
server . repl_backlog_histlen + 1 ;
}
/* Wrapper for feedReplicationBacklog() that takes Redis string objects
* as input . */
void feedReplicationBacklogWithObject ( robj * o ) {
2015-07-27 03:41:48 -04:00
char llstr [ LONG_STR_SIZE ] ;
2013-01-30 12:33:16 -05:00
void * p ;
size_t len ;
2015-07-26 09:28:00 -04:00
if ( o - > encoding = = OBJ_ENCODING_INT ) {
2013-01-30 12:33:16 -05:00
len = ll2string ( llstr , sizeof ( llstr ) , ( long ) o - > ptr ) ;
p = llstr ;
} else {
len = sdslen ( o - > ptr ) ;
p = o - > ptr ;
}
feedReplicationBacklog ( p , len ) ;
}
2010-06-21 18:07:48 -04:00
void replicationFeedSlaves ( list * slaves , int dictid , robj * * argv , int argc ) {
listNode * ln ;
listIter li ;
2013-08-12 06:10:38 -04:00
int j , len ;
2015-07-27 03:41:48 -04:00
char llstr [ LONG_STR_SIZE ] ;
2013-01-30 12:33:16 -05:00
/* If there aren't slaves, and there is no backlog buffer to populate,
* we can return ASAP . */
if ( server . repl_backlog = = NULL & & listLength ( slaves ) = = 0 ) return ;
/* We can't have slaves attached and no backlog. */
2015-07-26 09:29:53 -04:00
serverAssert ( ! ( listLength ( slaves ) ! = 0 & & server . repl_backlog = = NULL ) ) ;
2013-01-30 12:33:16 -05:00
2013-08-12 06:10:38 -04:00
/* Send SELECT command to every slave if needed. */
2013-01-30 12:33:16 -05:00
if ( server . slaveseldb ! = dictid ) {
2013-08-12 06:10:38 -04:00
robj * selectcmd ;
2013-01-30 12:33:16 -05:00
2013-08-12 06:10:38 -04:00
/* For a few DBs we have pre-computed SELECT command. */
2015-07-27 03:41:48 -04:00
if ( dictid > = 0 & & dictid < PROTO_SHARED_SELECT_CMDS ) {
2013-08-12 06:10:38 -04:00
selectcmd = shared . select [ dictid ] ;
2013-01-30 12:33:16 -05:00
} else {
int dictid_len ;
dictid_len = ll2string ( llstr , sizeof ( llstr ) , dictid ) ;
2015-07-26 09:28:00 -04:00
selectcmd = createObject ( OBJ_STRING ,
2013-08-12 06:10:38 -04:00
sdscatprintf ( sdsempty ( ) ,
" *2 \r \n $6 \r \n SELECT \r \n $%d \r \n %s \r \n " ,
dictid_len , llstr ) ) ;
2013-01-30 12:33:16 -05:00
}
2013-08-12 06:10:38 -04:00
/* Add the SELECT command into the backlog. */
if ( server . repl_backlog ) feedReplicationBacklogWithObject ( selectcmd ) ;
/* Send it to slaves. */
listRewind ( slaves , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2013-08-12 06:10:38 -04:00
addReply ( slave , selectcmd ) ;
2013-01-30 12:33:16 -05:00
}
2015-07-27 03:41:48 -04:00
if ( dictid < 0 | | dictid > = PROTO_SHARED_SELECT_CMDS )
2013-08-12 06:10:38 -04:00
decrRefCount ( selectcmd ) ;
}
server . slaveseldb = dictid ;
2013-01-30 12:33:16 -05:00
2013-08-12 06:10:38 -04:00
/* Write the command to the replication backlog if any. */
2013-01-30 12:33:16 -05:00
if ( server . repl_backlog ) {
2015-07-27 03:41:48 -04:00
char aux [ LONG_STR_SIZE + 3 ] ;
2013-08-12 06:10:38 -04:00
/* Add the multi bulk reply length. */
aux [ 0 ] = ' * ' ;
2013-08-28 01:44:40 -04:00
len = ll2string ( aux + 1 , sizeof ( aux ) - 1 , argc ) ;
2013-08-12 06:10:38 -04:00
aux [ len + 1 ] = ' \r ' ;
aux [ len + 2 ] = ' \n ' ;
feedReplicationBacklog ( aux , len + 3 ) ;
for ( j = 0 ; j < argc ; j + + ) {
long objlen = stringObjectLen ( argv [ j ] ) ;
2013-01-30 12:33:16 -05:00
/* We need to feed the buffer with the object as a bulk reply
2014-06-26 12:48:40 -04:00
* not just as a plain string , so create the $ . . CRLF payload len
2014-02-03 15:50:23 -05:00
* and add the final CRLF */
2013-01-30 12:33:16 -05:00
aux [ 0 ] = ' $ ' ;
2013-08-12 06:10:38 -04:00
len = ll2string ( aux + 1 , sizeof ( aux ) - 1 , objlen ) ;
2013-01-30 12:33:16 -05:00
aux [ len + 1 ] = ' \r ' ;
aux [ len + 2 ] = ' \n ' ;
feedReplicationBacklog ( aux , len + 3 ) ;
2013-08-12 06:10:38 -04:00
feedReplicationBacklogWithObject ( argv [ j ] ) ;
2013-08-12 06:38:52 -04:00
feedReplicationBacklog ( aux + len + 1 , 2 ) ;
2013-01-30 12:33:16 -05:00
}
}
2010-06-21 18:07:48 -04:00
2013-08-12 06:10:38 -04:00
/* Write the command to every slave. */
2014-10-16 03:03:52 -04:00
listRewind ( server . slaves , & li ) ;
2010-06-21 18:07:48 -04:00
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2010-06-21 18:07:48 -04:00
/* Don't feed slaves that are still waiting for BGSAVE to start */
2015-07-27 03:41:48 -04:00
if ( slave - > replstate = = SLAVE_STATE_WAIT_BGSAVE_START ) continue ;
2010-06-21 18:07:48 -04:00
2011-05-29 20:55:13 -04:00
/* Feed slaves that are waiting for the initial SYNC (so these commands
2013-01-16 12:00:20 -05:00
* are queued in the output buffer until the initial SYNC completes ) ,
2011-05-29 20:55:13 -04:00
* or are already in sync with the master . */
2013-01-30 12:33:16 -05:00
2013-08-12 06:10:38 -04:00
/* Add the multi bulk length. */
addReplyMultiBulkLen ( slave , argc ) ;
2013-01-30 12:33:16 -05:00
/* Finally any additional argument that was not stored inside the
* static buffer if any ( from j to argc ) . */
2013-08-12 06:10:38 -04:00
for ( j = 0 ; j < argc ; j + + )
addReplyBulk ( slave , argv [ j ] ) ;
2010-06-21 18:07:48 -04:00
}
}
2015-07-26 09:20:46 -04:00
void replicationFeedMonitors ( client * c , list * monitors , int dictid , robj * * argv , int argc ) {
2010-06-21 18:07:48 -04:00
listNode * ln ;
listIter li ;
2013-07-09 10:21:21 -04:00
int j ;
2010-06-21 18:07:48 -04:00
sds cmdrepr = sdsnew ( " + " ) ;
robj * cmdobj ;
struct timeval tv ;
gettimeofday ( & tv , NULL ) ;
2010-12-14 11:39:34 -05:00
cmdrepr = sdscatprintf ( cmdrepr , " %ld.%06ld " , ( long ) tv . tv_sec , ( long ) tv . tv_usec ) ;
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_LUA ) {
2012-11-01 17:10:45 -04:00
cmdrepr = sdscatprintf ( cmdrepr , " [%d lua] " , dictid ) ;
2015-07-27 03:41:48 -04:00
} else if ( c - > flags & CLIENT_UNIX_SOCKET ) {
2012-11-01 17:10:45 -04:00
cmdrepr = sdscatprintf ( cmdrepr , " [%d unix:%s] " , dictid , server . unixsocket ) ;
2012-03-07 06:12:15 -05:00
} else {
2014-04-28 11:36:57 -04:00
cmdrepr = sdscatprintf ( cmdrepr , " [%d %s] " , dictid , getClientPeerId ( c ) ) ;
2012-03-07 06:12:15 -05:00
}
2010-06-21 18:07:48 -04:00
for ( j = 0 ; j < argc ; j + + ) {
2015-07-26 09:28:00 -04:00
if ( argv [ j ] - > encoding = = OBJ_ENCODING_INT ) {
2010-07-01 14:22:46 -04:00
cmdrepr = sdscatprintf ( cmdrepr , " \" %ld \" " , ( long ) argv [ j ] - > ptr ) ;
2010-06-21 18:07:48 -04:00
} else {
cmdrepr = sdscatrepr ( cmdrepr , ( char * ) argv [ j ] - > ptr ,
sdslen ( argv [ j ] - > ptr ) ) ;
}
if ( j ! = argc - 1 )
cmdrepr = sdscatlen ( cmdrepr , " " , 1 ) ;
}
cmdrepr = sdscatlen ( cmdrepr , " \r \n " , 2 ) ;
2015-07-26 09:28:00 -04:00
cmdobj = createObject ( OBJ_STRING , cmdrepr ) ;
2010-06-21 18:07:48 -04:00
listRewind ( monitors , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * monitor = ln - > value ;
2010-06-21 18:07:48 -04:00
addReply ( monitor , cmdobj ) ;
}
decrRefCount ( cmdobj ) ;
}
2013-01-30 12:33:16 -05:00
/* Feed the slave 'c' with the replication backlog starting from the
* specified ' offset ' up to the end of the backlog . */
2015-07-26 09:20:46 -04:00
long long addReplyReplicationBacklog ( client * c , long long offset ) {
2013-01-30 12:33:16 -05:00
long long j , skip , len ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_DEBUG , " [PSYNC] Slave request offset: %lld " , offset ) ;
2013-01-30 12:33:16 -05:00
if ( server . repl_backlog_histlen = = 0 ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_DEBUG , " [PSYNC] Backlog history len is zero " ) ;
2013-01-30 12:33:16 -05:00
return 0 ;
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_DEBUG , " [PSYNC] Backlog size: %lld " ,
2013-02-09 10:33:57 -05:00
server . repl_backlog_size ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_DEBUG , " [PSYNC] First byte: %lld " ,
2013-02-09 10:33:57 -05:00
server . repl_backlog_off ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_DEBUG , " [PSYNC] History len: %lld " ,
2013-02-09 10:33:57 -05:00
server . repl_backlog_histlen ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_DEBUG , " [PSYNC] Current index: %lld " ,
2013-02-09 10:33:57 -05:00
server . repl_backlog_idx ) ;
2013-01-30 12:33:16 -05:00
/* Compute the amount of bytes we need to discard. */
skip = offset - server . repl_backlog_off ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_DEBUG , " [PSYNC] Skipping: %lld " , skip ) ;
2013-01-30 12:33:16 -05:00
/* Point j to the oldest byte, that is actaully our
* server . repl_backlog_off byte . */
j = ( server . repl_backlog_idx +
( server . repl_backlog_size - server . repl_backlog_histlen ) ) %
server . repl_backlog_size ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_DEBUG , " [PSYNC] Index of first byte: %lld " , j ) ;
2013-01-30 12:33:16 -05:00
/* Discard the amount of data to seek to the specified 'offset'. */
j = ( j + skip ) % server . repl_backlog_size ;
/* Feed slave with data. Since it is a circular buffer we have to
* split the reply in two parts if we are cross - boundary . */
len = server . repl_backlog_histlen - skip ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_DEBUG , " [PSYNC] Reply total length: %lld " , len ) ;
2013-01-30 12:33:16 -05:00
while ( len ) {
long long thislen =
( ( server . repl_backlog_size - j ) < len ) ?
( server . repl_backlog_size - j ) : len ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_DEBUG , " [PSYNC] addReply() length: %lld " , thislen ) ;
2013-01-30 12:33:16 -05:00
addReplySds ( c , sdsnewlen ( server . repl_backlog + j , thislen ) ) ;
len - = thislen ;
j = 0 ;
}
return server . repl_backlog_histlen - skip ;
}
2015-08-04 10:56:00 -04:00
/* Return the offset to provide as reply to the PSYNC command received
* from the slave . The returned value is only valid immediately after
* the BGSAVE process started and before executing any other command
* from clients . */
long long getPsyncInitialOffset ( void ) {
long long psync_offset = 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 PSYNC reply in the specific case of a full resynchronization.
* As a side effect , set into the slave client structure the offset
* we sent here , so that if new slaves will later attach to the same
* background RDB saving process ( by duplicating this client output
* buffer ) , we can get the right offset from this slave . */
int replicationSendFullresyncReply ( client * slave , long long offset ) {
char buf [ 128 ] ;
int buflen ;
slave - > psync_initial_offset = offset ;
/* Don't send this reply to slaves that approached us with
* the old SYNC command . */
if ( ! ( slave - > flags & CLIENT_PRE_PSYNC ) ) {
buflen = snprintf ( buf , sizeof ( buf ) , " +FULLRESYNC %s %lld \r \n " ,
server . runid , offset ) ;
if ( write ( slave - > fd , buf , buflen ) ! = buflen ) {
freeClientAsync ( slave ) ;
return C_ERR ;
}
}
return C_OK ;
}
2013-01-30 12:33:16 -05:00
/* This function handles the PSYNC command from the point of view of a
* master receiving a request for partial resynchronization .
*
2015-07-26 17:17:55 -04:00
* On success return C_OK , otherwise C_ERR is returned and we proceed
2013-01-30 12:33:16 -05:00
* with the usual full resync . */
2015-07-26 09:20:46 -04:00
int masterTryPartialResynchronization ( client * c ) {
2013-01-30 12:33:16 -05:00
long long psync_offset , psync_len ;
char * master_runid = c - > argv [ 1 ] - > ptr ;
2013-02-01 09:16:56 -05:00
char buf [ 128 ] ;
int buflen ;
2013-01-30 12:33:16 -05:00
/* Is the runid of this master the same advertised by the wannabe slave
* via PSYNC ? If runid changed this master is a different instance and
* there is no way to continue . */
if ( strcasecmp ( master_runid , server . runid ) ) {
/* Run id "?" is used by slaves that want to force a full resync. */
if ( master_runid [ 0 ] ! = ' ? ' ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Partial resynchronization not accepted: "
2013-09-30 05:48:09 -04:00
" Runid mismatch (Client asked for runid '%s', my runid is '%s') " ,
2013-01-30 12:33:16 -05:00
master_runid , server . runid ) ;
} else {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Full resync requested by slave %s " ,
2014-10-27 07:30:07 -04:00
replicationGetSlaveName ( c ) ) ;
2013-01-30 12:33:16 -05:00
}
goto need_full_resync ;
}
/* We still have the data our slave is asking for? */
if ( getLongLongFromObjectOrReply ( c , c - > argv [ 2 ] , & psync_offset , NULL ) ! =
2015-07-26 17:17:55 -04:00
C_OK ) goto need_full_resync ;
2013-01-30 12:33:16 -05:00
if ( ! server . repl_backlog | |
psync_offset < server . repl_backlog_off | |
2013-10-04 06:25:09 -04:00
psync_offset > ( server . repl_backlog_off + server . repl_backlog_histlen ) )
2013-01-30 12:33:16 -05:00
{
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE ,
2014-10-27 07:30:07 -04:00
" Unable to partial resync with slave %s for lack of backlog (Slave request was: %lld). " , replicationGetSlaveName ( c ) , psync_offset ) ;
2013-10-04 06:25:09 -04:00
if ( psync_offset > server . master_repl_offset ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2014-10-27 07:30:07 -04:00
" Warning: slave %s tried to PSYNC with an offset that is greater than the master replication offset. " , replicationGetSlaveName ( c ) ) ;
2013-10-04 06:25:09 -04:00
}
2013-01-30 12:33:16 -05:00
goto need_full_resync ;
}
/* If we reached this point, we are able to perform a partial resync:
* 1 ) Set client state to make it a slave .
* 2 ) Inform the client we can continue with + CONTINUE
* 3 ) Send the backlog data ( from the offset to the end ) to the slave . */
2015-07-27 03:41:48 -04:00
c - > flags | = CLIENT_SLAVE ;
c - > replstate = SLAVE_STATE_ONLINE ;
2013-05-27 05:17:17 -04:00
c - > repl_ack_time = server . unixtime ;
2014-11-11 11:12:12 -05:00
c - > repl_put_online_on_ack = 0 ;
2013-01-30 12:33:16 -05:00
listAddNodeTail ( server . slaves , c ) ;
2013-02-01 09:16:56 -05:00
/* 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
2014-01-16 23:02:38 -05:00
* empty so this write will never fail actually . */
2013-02-01 09:16:56 -05:00
buflen = snprintf ( buf , sizeof ( buf ) , " +CONTINUE \r \n " ) ;
if ( write ( c - > fd , buf , buflen ) ! = buflen ) {
freeClientAsync ( c ) ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2013-02-01 09:16:56 -05:00
}
2013-01-30 12:33:16 -05:00
psync_len = addReplyReplicationBacklog ( c , psync_offset ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE ,
2014-10-27 07:30:07 -04:00
" Partial resynchronization request from %s accepted. Sending %lld bytes of backlog starting from offset %lld. " ,
replicationGetSlaveName ( c ) ,
psync_len , psync_offset ) ;
2013-01-30 12:33:16 -05:00
/* Note that we don't need to set the selected DB at server.slaveseldb
* to - 1 to force the master to emit SELECT , since the slave already
* has this state from the previous connection with the master . */
2013-05-30 06:13:25 -04:00
refreshGoodSlavesCount ( ) ;
2015-07-26 17:17:55 -04:00
return C_OK ; /* The caller can return, no full resync needed. */
2013-01-30 12:33:16 -05:00
need_full_resync :
2015-08-04 10:56:00 -04:00
/* We need a full resync for some reason... Note that we can't
* reply to PSYNC right now if a full SYNC is needed . The reply
* must include the master offset at the time the RDB file we transfer
* is generated , so we need to delay the reply to that moment . */
2015-07-26 17:17:55 -04:00
return C_ERR ;
2013-01-30 12:33:16 -05:00
}
2014-10-14 04:11:26 -04:00
/* Start a BGSAVE for replication goals, which is, selecting the disk or
* socket target depending on the configuration , and making sure that
* the script cache is flushed before to start .
*
2015-07-26 17:17:55 -04:00
* Returns C_OK on success or C_ERR otherwise . */
2014-10-14 04:11:26 -04:00
int startBgsaveForReplication ( void ) {
int retval ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Starting BGSAVE for SYNC with target: %s " ,
2014-10-16 04:00:46 -04:00
server . repl_diskless_sync ? " slaves sockets " : " disk " ) ;
2014-10-14 04:11:26 -04:00
2014-10-16 04:00:46 -04:00
if ( server . repl_diskless_sync )
2014-10-14 04:11:26 -04:00
retval = rdbSaveToSlavesSockets ( ) ;
else
retval = rdbSaveBackground ( server . rdb_filename ) ;
/* Flush the script cache, since we need that slave differences are
* accumulated without requiring slaves to match our cached scripts . */
2015-07-26 17:17:55 -04:00
if ( retval = = C_OK ) replicationScriptCacheFlush ( ) ;
2014-10-14 04:11:26 -04:00
return retval ;
}
2014-02-03 15:50:23 -05:00
/* SYNC and PSYNC command implemenation. */
2015-07-26 09:20:46 -04:00
void syncCommand ( client * c ) {
2013-01-16 12:00:20 -05:00
/* ignore SYNC if already slave or in monitor mode */
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_SLAVE ) return ;
2010-06-21 18:07:48 -04:00
2010-08-24 10:04:13 -04:00
/* Refuse SYNC requests if we are a slave but the link with our master
* is not ok . . . */
2015-07-27 03:41:48 -04:00
if ( server . masterhost & & server . repl_state ! = REPL_STATE_CONNECTED ) {
2010-09-02 13:52:24 -04:00
addReplyError ( c , " Can't SYNC while not connected with my master " ) ;
2010-08-24 10:04:13 -04:00
return ;
}
2010-06-21 18:07:48 -04:00
/* SYNC can't be issued when the server has pending data to send to
* the client about already issued commands . We need a fresh reply
* buffer registering the differences between the BGSAVE and the current
* dataset , so that we can copy to other slaves if needed . */
2013-02-01 09:05:43 -05:00
if ( listLength ( c - > reply ) ! = 0 | | c - > bufpos ! = 0 ) {
addReplyError ( c , " SYNC and PSYNC are invalid with pending output " ) ;
2010-06-21 18:07:48 -04:00
return ;
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Slave %s asks for synchronization " ,
2014-10-27 07:30:07 -04:00
replicationGetSlaveName ( c ) ) ;
2013-01-30 12:33:16 -05:00
/* Try a partial resynchronization if this is a PSYNC command.
* If it fails , we continue with usual full resynchronization , however
* when this happens masterTryPartialResynchronization ( ) already
* replied with :
*
* + FULLRESYNC < runid > < offset >
*
* So the slave knows the new runid and offset to try a PSYNC later
* if the connection with the master is lost . */
2013-02-12 09:24:25 -05:00
if ( ! strcasecmp ( c - > argv [ 0 ] - > ptr , " psync " ) ) {
2015-07-26 17:17:55 -04:00
if ( masterTryPartialResynchronization ( c ) = = C_OK ) {
2013-02-12 09:24:25 -05:00
server . stat_sync_partial_ok + + ;
return ; /* No full resync needed, return. */
} else {
char * master_runid = c - > argv [ 1 ] - > ptr ;
2014-06-26 12:48:40 -04:00
2013-02-12 09:24:25 -05:00
/* Increment stats for failed PSYNCs, but only if the
* runid is not " ? " , as this is used by slaves to force a full
* resync on purpose when they are not albe to partially
* resync . */
if ( master_runid [ 0 ] ! = ' ? ' ) server . stat_sync_partial_err + + ;
}
2013-06-26 04:11:20 -04:00
} else {
/* If a slave uses SYNC, we are dealing with an old implementation
* of the replication protocol ( like redis - cli - - slave ) . Flag the client
* so that we don ' t expect to receive REPLCONF ACK feedbacks . */
2015-07-27 03:41:48 -04:00
c - > flags | = CLIENT_PRE_PSYNC ;
2013-02-12 09:24:25 -05:00
}
/* Full resynchronization. */
server . stat_sync_full + + ;
2013-01-30 12:33:16 -05:00
2010-06-21 18:07:48 -04:00
/* Here we need to check if there is a background saving operation
* in progress , or if it is required to start one */
2014-10-14 04:11:26 -04:00
if ( server . rdb_child_pid ! = - 1 & &
2015-07-27 03:41:48 -04:00
server . rdb_child_type = = RDB_CHILD_TYPE_DISK )
2014-10-14 04:11:26 -04:00
{
2010-06-21 18:07:48 -04:00
/* Ok a background save is in progress. Let's check if it is a good
* one for replication , i . e . if there is another slave that is
2014-10-10 10:25:58 -04:00
* registering differences since the server forked to save . */
2015-07-26 09:20:46 -04:00
client * slave ;
2010-06-21 18:07:48 -04:00
listNode * ln ;
listIter li ;
listRewind ( server . slaves , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
slave = ln - > value ;
2015-07-27 03:41:48 -04:00
if ( slave - > replstate = = SLAVE_STATE_WAIT_BGSAVE_END ) break ;
2010-06-21 18:07:48 -04:00
}
if ( ln ) {
/* Perfect, the server is already registering differences for
2014-10-14 04:11:26 -04:00
* another slave . Set the right state , and copy the buffer . */
2011-12-30 13:34:40 -05:00
copyClientOutputBuffer ( c , slave ) ;
2015-07-27 03:41:48 -04:00
c - > replstate = SLAVE_STATE_WAIT_BGSAVE_END ;
2015-08-04 10:56:00 -04:00
replicationSendFullresyncReply ( c , slave - > psync_initial_offset ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Waiting for end of BGSAVE for SYNC " ) ;
2010-06-21 18:07:48 -04:00
} else {
/* No way, we need to wait for the next BGSAVE in order to
2014-10-10 10:25:58 -04:00
* register differences . */
2015-07-27 03:41:48 -04:00
c - > replstate = SLAVE_STATE_WAIT_BGSAVE_START ;
serverLog ( LL_NOTICE , " Waiting for next BGSAVE for SYNC " ) ;
2010-06-21 18:07:48 -04:00
}
2014-10-14 04:11:26 -04:00
} else if ( server . rdb_child_pid ! = - 1 & &
2015-07-27 03:41:48 -04:00
server . rdb_child_type = = RDB_CHILD_TYPE_SOCKET )
2014-10-14 04:11:26 -04:00
{
/* There is an RDB child process but it is writing directly to
* children sockets . We need to wait for the next BGSAVE
* in order to synchronize . */
2015-07-27 03:41:48 -04:00
c - > replstate = SLAVE_STATE_WAIT_BGSAVE_START ;
serverLog ( LL_NOTICE , " Waiting for next BGSAVE for SYNC " ) ;
2010-06-21 18:07:48 -04:00
} else {
2014-10-16 04:00:46 -04:00
if ( server . repl_diskless_sync ) {
2014-10-14 04:11:26 -04:00
/* Diskless replication RDB child is created inside
* replicationCron ( ) since we want to delay its start a
* few seconds to wait for more slaves to arrive . */
2015-07-27 03:41:48 -04:00
c - > replstate = SLAVE_STATE_WAIT_BGSAVE_START ;
2014-10-27 05:48:32 -04:00
if ( server . repl_diskless_sync_delay )
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Delay next BGSAVE for SYNC " ) ;
2014-10-14 04:11:26 -04:00
} else {
/* Ok we don't have a BGSAVE in progress, let's start one. */
2015-07-26 17:17:55 -04:00
if ( startBgsaveForReplication ( ) ! = C_OK ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Replication failed, can't BGSAVE " ) ;
2014-10-14 04:11:26 -04:00
addReplyError ( c , " Unable to perform background save " ) ;
return ;
}
2015-07-27 03:41:48 -04:00
c - > replstate = SLAVE_STATE_WAIT_BGSAVE_END ;
2015-08-04 10:56:00 -04:00
replicationSendFullresyncReply ( c , getPsyncInitialOffset ( ) ) ;
2010-06-21 18:07:48 -04:00
}
}
2013-01-31 05:14:15 -05:00
if ( server . repl_disable_tcp_nodelay )
anetDisableTcpNoDelay ( NULL , c - > fd ) ; /* Non critical if it fails. */
2010-06-21 18:07:48 -04:00
c - > repldbfd = - 1 ;
2015-07-27 03:41:48 -04:00
c - > flags | = CLIENT_SLAVE ;
2012-11-02 11:31:28 -04:00
server . slaveseldb = - 1 ; /* Force to re-emit the SELECT command. */
2010-06-21 18:07:48 -04:00
listAddNodeTail ( server . slaves , c ) ;
2013-01-30 12:33:16 -05:00
if ( listLength ( server . slaves ) = = 1 & & server . repl_backlog = = NULL )
createReplicationBacklog ( ) ;
2010-06-21 18:07:48 -04:00
return ;
}
2012-06-26 03:47:47 -04:00
/* REPLCONF <option> <value> <option> <value> ...
* This command is used by a slave in order to configure the replication
* process before starting it with the SYNC command .
*
* Currently the only use of this command is to communicate to the master
* what is the listening port of the Slave redis instance , so that the
* master can accurately list slaves and their listening ports in
* the INFO output .
*
* In the future the same command can be used in order to configure
* the replication to initiate an incremental replication instead of a
* full resync . */
2015-07-26 09:20:46 -04:00
void replconfCommand ( client * c ) {
2012-06-26 03:47:47 -04:00
int j ;
if ( ( c - > argc % 2 ) = = 0 ) {
/* Number of arguments must be odd to make sure that every
* option has a corresponding value . */
addReply ( c , shared . syntaxerr ) ;
return ;
}
/* Process every option-value pair. */
for ( j = 1 ; j < c - > argc ; j + = 2 ) {
if ( ! strcasecmp ( c - > argv [ j ] - > ptr , " listening-port " ) ) {
long port ;
if ( ( getLongFromObjectOrReply ( c , c - > argv [ j + 1 ] ,
2015-07-26 17:17:55 -04:00
& port , NULL ) ! = C_OK ) )
2012-06-26 03:47:47 -04:00
return ;
c - > slave_listening_port = port ;
2013-05-24 18:37:56 -04:00
} else if ( ! strcasecmp ( c - > argv [ j ] - > ptr , " ack " ) ) {
/* REPLCONF ACK is used by slave to inform the master the amount
* of replication stream that it processed so far . It is an
* internal only command that normal clients should never use . */
long long offset ;
2015-07-27 03:41:48 -04:00
if ( ! ( c - > flags & CLIENT_SLAVE ) ) return ;
2015-07-26 17:17:55 -04:00
if ( ( getLongLongFromObject ( c - > argv [ j + 1 ] , & offset ) ! = C_OK ) )
2013-05-24 18:37:56 -04:00
return ;
if ( offset > c - > repl_ack_off )
c - > repl_ack_off = offset ;
c - > repl_ack_time = server . unixtime ;
2014-11-11 11:12:12 -05:00
/* If this was a diskless replication, we need to really put
* the slave online when the first ACK is received ( which
* confirms slave is online and ready to get more data ) . */
2015-07-27 03:41:48 -04:00
if ( c - > repl_put_online_on_ack & & c - > replstate = = SLAVE_STATE_ONLINE )
2014-11-11 11:12:12 -05:00
putSlaveOnline ( c ) ;
2013-05-24 18:37:56 -04:00
/* Note: this command does not reply anything! */
2013-05-24 19:22:27 -04:00
return ;
2013-12-04 09:52:20 -05:00
} else if ( ! strcasecmp ( c - > argv [ j ] - > ptr , " getack " ) ) {
/* REPLCONF GETACK is used in order to request an ACK ASAP
* to the slave . */
if ( server . masterhost & & server . master ) replicationSendAck ( ) ;
/* Note: this command does not reply anything! */
2012-06-26 03:47:47 -04:00
} else {
addReplyErrorFormat ( c , " Unrecognized REPLCONF option: %s " ,
( char * ) c - > argv [ j ] - > ptr ) ;
return ;
}
}
addReply ( c , shared . ok ) ;
}
2014-10-15 09:31:19 -04:00
/* This function puts a slave in the online state, and should be called just
* after a slave received the RDB file for the initial synchronization , and
* we are finally ready to send the incremental stream of commands .
*
* It does a few things :
*
2015-04-01 04:07:08 -04:00
* 1 ) Put the slave in ONLINE state ( useless when the function is called
* because state is already ONLINE but repl_put_online_on_ack is true ) .
2014-10-15 09:31:19 -04:00
* 2 ) Make sure the writable event is re - installed , since calling the SYNC
* command disables it , so that we can accumulate output buffer without
* sending it to the slave .
* 3 ) Update the count of good slaves . */
2015-07-26 09:20:46 -04:00
void putSlaveOnline ( client * slave ) {
2015-07-27 03:41:48 -04:00
slave - > replstate = SLAVE_STATE_ONLINE ;
2014-11-11 11:12:12 -05:00
slave - > repl_put_online_on_ack = 0 ;
2015-04-01 04:07:08 -04:00
slave - > repl_ack_time = server . unixtime ; /* Prevent false timeout. */
2014-10-15 09:31:19 -04:00
if ( aeCreateFileEvent ( server . el , slave - > fd , AE_WRITABLE ,
sendReplyToClient , slave ) = = AE_ERR ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Unable to register writable event for slave bulk transfer: %s " , strerror ( errno ) ) ;
2014-10-15 09:31:19 -04:00
freeClient ( slave ) ;
return ;
}
refreshGoodSlavesCount ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Synchronization with slave %s succeeded " ,
2014-11-11 11:12:12 -05:00
replicationGetSlaveName ( slave ) ) ;
2014-10-15 09:31:19 -04:00
}
2010-06-21 18:07:48 -04:00
void sendBulkToSlave ( aeEventLoop * el , int fd , void * privdata , int mask ) {
2015-07-26 09:20:46 -04:00
client * slave = privdata ;
2015-07-27 03:41:48 -04:00
UNUSED ( el ) ;
UNUSED ( mask ) ;
char buf [ PROTO_IOBUF_LEN ] ;
2010-06-21 18:07:48 -04:00
ssize_t nwritten , buflen ;
2013-08-12 04:29:14 -04:00
/* Before sending the RDB file, we send the preamble as configured by the
* replication process . Currently the preamble is just the bulk count of
* the file in the form " $<length> \r \n " . */
if ( slave - > replpreamble ) {
nwritten = write ( fd , slave - > replpreamble , sdslen ( slave - > replpreamble ) ) ;
if ( nwritten = = - 1 ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_VERBOSE , " Write error sending RDB preamble to slave: %s " ,
2013-08-12 04:29:14 -04:00
strerror ( errno ) ) ;
2010-06-21 18:07:48 -04:00
freeClient ( slave ) ;
return ;
}
2014-12-03 06:06:54 -05:00
server . stat_net_output_bytes + = nwritten ;
2013-08-12 04:29:14 -04:00
sdsrange ( slave - > replpreamble , nwritten , - 1 ) ;
if ( sdslen ( slave - > replpreamble ) = = 0 ) {
sdsfree ( slave - > replpreamble ) ;
slave - > replpreamble = NULL ;
/* fall through sending data. */
} else {
return ;
}
2010-06-21 18:07:48 -04:00
}
2013-08-12 04:29:14 -04:00
/* If the preamble was already transfered, send the RDB bulk data. */
2010-06-21 18:07:48 -04:00
lseek ( slave - > repldbfd , slave - > repldboff , SEEK_SET ) ;
2015-07-27 03:41:48 -04:00
buflen = read ( slave - > repldbfd , buf , PROTO_IOBUF_LEN ) ;
2010-06-21 18:07:48 -04:00
if ( buflen < = 0 ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Read error sending DB to slave: %s " ,
2010-06-21 18:07:48 -04:00
( buflen = = 0 ) ? " premature EOF " : strerror ( errno ) ) ;
freeClient ( slave ) ;
return ;
}
if ( ( nwritten = write ( fd , buf , buflen ) ) = = - 1 ) {
2014-02-05 10:38:10 -05:00
if ( errno ! = EAGAIN ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Write error sending DB to slave: %s " ,
2014-02-05 10:38:10 -05:00
strerror ( errno ) ) ;
freeClient ( slave ) ;
}
2010-06-21 18:07:48 -04:00
return ;
}
slave - > repldboff + = nwritten ;
2014-12-03 06:06:54 -05:00
server . stat_net_output_bytes + = nwritten ;
2010-06-21 18:07:48 -04:00
if ( slave - > repldboff = = slave - > repldbsize ) {
close ( slave - > repldbfd ) ;
slave - > repldbfd = - 1 ;
aeDeleteFileEvent ( server . el , slave - > fd , AE_WRITABLE ) ;
2014-10-15 09:31:19 -04:00
putSlaveOnline ( slave ) ;
2010-06-21 18:07:48 -04:00
}
}
2014-10-16 04:15:18 -04:00
/* This function is called at the end of every background saving,
* or when the replication RDB transfer strategy is modified from
* disk to socket or the other way around .
2010-06-21 18:07:48 -04:00
*
* The goal of this function is to handle slaves waiting for a successful
2014-10-14 04:11:26 -04:00
* background saving in order to perform non - blocking synchronization , and
* to schedule a new BGSAVE if there are slaves that attached while a
* BGSAVE was in progress , but it was not a good one for replication ( no
2014-10-16 04:15:18 -04:00
* other slave was accumulating differences ) .
*
2015-07-26 17:17:55 -04:00
* The argument bgsaveerr is C_OK if the background saving succeeded
* otherwise C_ERR is passed to the function .
2014-10-16 04:15:18 -04:00
* The ' type ' argument is the type of the child that terminated
* ( if it had a disk or socket target ) . */
2014-10-14 04:11:26 -04:00
void updateSlavesWaitingBgsave ( int bgsaveerr , int type ) {
2010-06-21 18:07:48 -04:00
listNode * ln ;
int startbgsave = 0 ;
listIter li ;
listRewind ( server . slaves , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2010-06-21 18:07:48 -04:00
2015-07-27 03:41:48 -04:00
if ( slave - > replstate = = SLAVE_STATE_WAIT_BGSAVE_START ) {
2010-06-21 18:07:48 -04:00
startbgsave = 1 ;
2015-07-27 03:41:48 -04:00
slave - > replstate = SLAVE_STATE_WAIT_BGSAVE_END ;
2015-08-04 10:56:00 -04:00
replicationSendFullresyncReply ( slave , getPsyncInitialOffset ( ) ) ;
2015-07-27 03:41:48 -04:00
} else if ( slave - > replstate = = SLAVE_STATE_WAIT_BGSAVE_END ) {
2010-06-21 18:07:48 -04:00
struct redis_stat buf ;
2014-10-15 09:31:19 -04:00
/* If this was an RDB on disk save, we have to prepare to send
* the RDB from disk to the slave socket . Otherwise if this was
* already an RDB - > Slaves socket transfer , used in the case of
* diskless replication , our work is trivial , we can just put
* the slave online . */
2015-07-27 03:41:48 -04:00
if ( type = = RDB_CHILD_TYPE_SOCKET ) {
serverLog ( LL_NOTICE ,
2014-11-11 11:12:12 -05:00
" Streamed RDB transfer with slave %s succeeded (socket). Waiting for REPLCONF ACK from slave to enable streaming " ,
2014-10-27 07:30:07 -04:00
replicationGetSlaveName ( slave ) ) ;
2014-11-11 11:12:12 -05:00
/* Note: we wait for a REPLCONF ACK message from slave in
* order to really put it online ( install the write handler
* so that the accumulated data can be transfered ) . However
* we change the replication state ASAP , since our slave
* is technically online now . */
2015-07-27 03:41:48 -04:00
slave - > replstate = SLAVE_STATE_ONLINE ;
2014-11-11 11:12:12 -05:00
slave - > repl_put_online_on_ack = 1 ;
2015-04-01 04:07:08 -04:00
slave - > repl_ack_time = server . unixtime ; /* Timeout otherwise. */
2014-10-15 09:31:19 -04:00
} else {
2015-07-26 17:17:55 -04:00
if ( bgsaveerr ! = C_OK ) {
2014-10-15 09:31:19 -04:00
freeClient ( slave ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " SYNC failed. BGSAVE child returned an error " ) ;
2014-10-15 09:31:19 -04:00
continue ;
}
if ( ( slave - > repldbfd = open ( server . rdb_filename , O_RDONLY ) ) = = - 1 | |
redis_fstat ( slave - > repldbfd , & buf ) = = - 1 ) {
freeClient ( slave ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " SYNC failed. Can't open/stat DB after BGSAVE: %s " , strerror ( errno ) ) ;
2014-10-15 09:31:19 -04:00
continue ;
}
slave - > repldboff = 0 ;
slave - > repldbsize = buf . st_size ;
2015-07-27 03:41:48 -04:00
slave - > replstate = SLAVE_STATE_SEND_BULK ;
2014-10-15 09:31:19 -04:00
slave - > replpreamble = sdscatprintf ( sdsempty ( ) , " $%lld \r \n " ,
( unsigned long long ) slave - > repldbsize ) ;
aeDeleteFileEvent ( server . el , slave - > fd , AE_WRITABLE ) ;
if ( aeCreateFileEvent ( server . el , slave - > fd , AE_WRITABLE , sendBulkToSlave , slave ) = = AE_ERR ) {
freeClient ( slave ) ;
continue ;
}
2010-06-21 18:07:48 -04:00
}
}
}
if ( startbgsave ) {
2015-07-26 17:17:55 -04:00
if ( startBgsaveForReplication ( ) ! = C_OK ) {
2010-06-21 18:07:48 -04:00
listIter li ;
listRewind ( server . slaves , & li ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " SYNC failed. BGSAVE failed " ) ;
2010-06-21 18:07:48 -04:00
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2010-06-21 18:07:48 -04:00
2015-07-27 03:41:48 -04:00
if ( slave - > replstate = = SLAVE_STATE_WAIT_BGSAVE_START )
2010-06-21 18:07:48 -04:00
freeClient ( slave ) ;
}
}
}
}
2010-11-04 12:29:53 -04:00
/* ----------------------------------- SLAVE -------------------------------- */
/* Abort the async download of the bulk dataset while SYNC-ing with master */
void replicationAbortSyncTransfer ( void ) {
2015-07-27 03:41:48 -04:00
serverAssert ( server . repl_state = = REPL_STATE_TRANSFER ) ;
2010-11-04 12:29:53 -04:00
aeDeleteFileEvent ( server . el , server . repl_transfer_s , AE_READABLE ) ;
close ( server . repl_transfer_s ) ;
close ( server . repl_transfer_fd ) ;
unlink ( server . repl_transfer_tmpfile ) ;
zfree ( server . repl_transfer_tmpfile ) ;
2015-07-27 03:41:48 -04:00
server . repl_state = REPL_STATE_CONNECT ;
2010-11-04 12:29:53 -04:00
}
2013-12-10 12:38:26 -05:00
/* Avoid the master to detect the slave is timing out while loading the
* RDB file in initial synchronization . We send a single newline character
* that is valid protocol but is guaranteed to either be sent entierly or
* not , since the byte is indivisible .
*
* The function is called in two contexts : while we flush the current
* data with emptyDb ( ) , and while we load the new data received as an
* RDB file from the master . */
void replicationSendNewlineToMaster ( void ) {
static time_t newline_sent ;
if ( time ( NULL ) ! = newline_sent ) {
newline_sent = time ( NULL ) ;
if ( write ( server . repl_transfer_s , " \n " , 1 ) = = - 1 ) {
/* Pinging back in this stage is best-effort. */
}
}
}
/* Callback used by emptyDb() while flushing away old data to load
* the new dataset received by the master . */
void replicationEmptyDbCallback ( void * privdata ) {
2015-07-27 03:41:48 -04:00
UNUSED ( privdata ) ;
2013-12-10 12:38:26 -05:00
replicationSendNewlineToMaster ( ) ;
}
2015-02-04 05:24:46 -05:00
/* Once we have a link with the master and the synchroniziation was
* performed , this function materializes the master client we store
* at server . master , starting from the specified file descriptor . */
void replicationCreateMasterClient ( int fd ) {
server . master = createClient ( fd ) ;
2015-07-27 03:41:48 -04:00
server . master - > flags | = CLIENT_MASTER ;
2015-02-04 05:24:46 -05:00
server . master - > authenticated = 1 ;
2015-07-27 03:41:48 -04:00
server . repl_state = REPL_STATE_CONNECTED ;
2015-02-04 05:24:46 -05:00
server . master - > reploff = server . repl_master_initial_offset ;
memcpy ( server . master - > replrunid , server . repl_master_runid ,
sizeof ( server . repl_master_runid ) ) ;
/* If master offset is set to -1, this master is old and is not
* PSYNC capable , so we flag it accordingly . */
if ( server . master - > reploff = = - 1 )
2015-07-27 03:41:48 -04:00
server . master - > flags | = CLIENT_PRE_PSYNC ;
2015-02-04 05:24:46 -05:00
}
2010-11-04 12:29:53 -04:00
/* Asynchronously read the SYNC payload we receive from a master */
2012-08-24 13:28:44 -04:00
# define REPL_MAX_WRITTEN_BEFORE_FSYNC (1024*1024*8) /* 8 MB */
2010-11-04 12:29:53 -04:00
void readSyncBulkPayload ( aeEventLoop * el , int fd , void * privdata , int mask ) {
2010-11-04 13:09:35 -04:00
char buf [ 4096 ] ;
2010-11-04 12:35:03 -04:00
ssize_t nread , readlen ;
2012-08-24 13:28:44 -04:00
off_t left ;
2015-07-27 03:41:48 -04:00
UNUSED ( el ) ;
UNUSED ( privdata ) ;
UNUSED ( mask ) ;
2010-11-04 12:29:53 -04:00
2014-10-16 11:09:29 -04:00
/* Static vars used to hold the EOF mark, and the last bytes received
* form the server : when they match , we reached the end of the transfer . */
2015-07-27 03:41:48 -04:00
static char eofmark [ CONFIG_RUN_ID_SIZE ] ;
static char lastbytes [ CONFIG_RUN_ID_SIZE ] ;
2014-10-16 11:09:29 -04:00
static int usemark = 0 ;
2012-08-24 13:28:44 -04:00
/* If repl_transfer_size == -1 we still have to read the bulk length
2010-11-04 13:09:35 -04:00
* from the master reply . */
2012-08-24 13:28:44 -04:00
if ( server . repl_transfer_size = = - 1 ) {
2012-03-31 05:23:30 -04:00
if ( syncReadLine ( fd , buf , 1024 , server . repl_syncio_timeout * 1000 ) = = - 1 ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2010-11-04 13:09:35 -04:00
" I/O error reading bulk count from MASTER: %s " ,
strerror ( errno ) ) ;
2011-05-22 06:41:24 -04:00
goto error ;
2010-11-04 13:09:35 -04:00
}
2011-05-22 06:41:24 -04:00
2010-11-04 13:09:35 -04:00
if ( buf [ 0 ] = = ' - ' ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2010-11-04 13:09:35 -04:00
" MASTER aborted replication with an error: %s " ,
buf + 1 ) ;
2011-05-22 06:41:24 -04:00
goto error ;
2011-01-20 07:18:23 -05:00
} else if ( buf [ 0 ] = = ' \0 ' ) {
/* At this stage just a newline works as a PING in order to take
* the connection live . So we refresh our last interaction
* timestamp . */
2012-03-27 11:39:58 -04:00
server . repl_transfer_lastio = server . unixtime ;
2011-01-20 07:18:23 -05:00
return ;
2010-11-04 13:09:35 -04:00
} else if ( buf [ 0 ] ! = ' $ ' ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Bad protocol from MASTER, the first byte is not '$' (we received '%s'), are you sure the host and port are right? " , buf ) ;
2011-05-22 06:41:24 -04:00
goto error ;
2010-11-04 13:09:35 -04:00
}
2014-10-16 11:09:29 -04:00
/* There are two possible forms for the bulk payload. One is the
* usual $ < count > bulk format . The other is used for diskless transfers
* when the master does not know beforehand the size of the file to
* transfer . In the latter case , the following format is used :
*
* $ EOF : < 40 bytes delimiter >
*
* At the end of the file the announced delimiter is transmitted . The
* delimiter is long and random enough that the probability of a
* collision with the actual file content can be ignored . */
2015-07-27 03:41:48 -04:00
if ( strncmp ( buf + 1 , " EOF: " , 4 ) = = 0 & & strlen ( buf + 5 ) > = CONFIG_RUN_ID_SIZE ) {
2014-10-16 11:09:29 -04:00
usemark = 1 ;
2015-07-27 03:41:48 -04:00
memcpy ( eofmark , buf + 5 , CONFIG_RUN_ID_SIZE ) ;
memset ( lastbytes , 0 , CONFIG_RUN_ID_SIZE ) ;
2014-10-17 04:21:18 -04:00
/* Set any repl_transfer_size to avoid entering this code path
* at the next call . */
server . repl_transfer_size = 0 ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE ,
2014-10-16 11:09:29 -04:00
" MASTER <-> SLAVE sync: receiving streamed RDB from master " ) ;
} else {
usemark = 0 ;
server . repl_transfer_size = strtol ( buf + 1 , NULL , 10 ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE ,
2014-10-16 11:09:29 -04:00
" MASTER <-> SLAVE sync: receiving %lld bytes from master " ,
( long long ) server . repl_transfer_size ) ;
}
2010-11-04 13:09:35 -04:00
return ;
}
/* Read bulk data */
2014-10-16 11:09:29 -04:00
if ( usemark ) {
2014-10-17 04:22:29 -04:00
readlen = sizeof ( buf ) ;
} else {
2014-10-16 11:09:29 -04:00
left = server . repl_transfer_size - server . repl_transfer_read ;
readlen = ( left < ( signed ) sizeof ( buf ) ) ? left : ( signed ) sizeof ( buf ) ;
}
2010-11-04 12:29:53 -04:00
nread = read ( fd , buf , readlen ) ;
if ( nread < = 0 ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " I/O error trying to sync with MASTER: %s " ,
2010-11-04 12:29:53 -04:00
( nread = = - 1 ) ? strerror ( errno ) : " connection lost " ) ;
replicationAbortSyncTransfer ( ) ;
return ;
}
2014-12-03 06:06:54 -05:00
server . stat_net_input_bytes + = nread ;
2014-10-16 11:09:29 -04:00
/* When a mark is used, we want to detect EOF asap in order to avoid
* writing the EOF mark into the file . . . */
int eof_reached = 0 ;
if ( usemark ) {
2014-11-11 11:12:12 -05:00
/* Update the last bytes array, and check if it matches our delimiter.*/
2015-07-27 03:41:48 -04:00
if ( nread > = CONFIG_RUN_ID_SIZE ) {
memcpy ( lastbytes , buf + nread - CONFIG_RUN_ID_SIZE , CONFIG_RUN_ID_SIZE ) ;
2014-10-16 11:09:29 -04:00
} else {
2015-07-27 03:41:48 -04:00
int rem = CONFIG_RUN_ID_SIZE - nread ;
2014-10-16 11:09:29 -04:00
memmove ( lastbytes , lastbytes + nread , rem ) ;
memcpy ( lastbytes + rem , buf , nread ) ;
}
2015-07-27 03:41:48 -04:00
if ( memcmp ( lastbytes , eofmark , CONFIG_RUN_ID_SIZE ) = = 0 ) eof_reached = 1 ;
2014-10-16 11:09:29 -04:00
}
2012-03-27 11:39:58 -04:00
server . repl_transfer_lastio = server . unixtime ;
2010-11-04 12:29:53 -04:00
if ( write ( server . repl_transfer_fd , buf , nread ) ! = nread ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Write error or short write writing to the DB dump file needed for MASTER <-> SLAVE synchronization: %s " , strerror ( errno ) ) ;
2011-05-22 06:41:24 -04:00
goto error ;
2010-11-04 12:29:53 -04:00
}
2012-08-24 13:28:44 -04:00
server . repl_transfer_read + = nread ;
2014-10-17 04:23:11 -04:00
/* Delete the last 40 bytes from the file if we reached EOF. */
if ( usemark & & eof_reached ) {
if ( ftruncate ( server . repl_transfer_fd ,
2015-07-27 03:41:48 -04:00
server . repl_transfer_read - CONFIG_RUN_ID_SIZE ) = = - 1 )
2014-10-17 04:23:11 -04:00
{
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Error truncating the RDB file received from the master for SYNC: %s " , strerror ( errno ) ) ;
2014-10-17 04:23:11 -04:00
goto error ;
}
}
2012-08-24 13:28:44 -04:00
/* Sync data on disk from time to time, otherwise at the end of the transfer
* we may suffer a big delay as the memory buffers are copied into the
* actual disk . */
if ( server . repl_transfer_read > =
server . repl_transfer_last_fsync_off + REPL_MAX_WRITTEN_BEFORE_FSYNC )
{
off_t sync_size = server . repl_transfer_read -
server . repl_transfer_last_fsync_off ;
rdb_fsync_range ( server . repl_transfer_fd ,
server . repl_transfer_last_fsync_off , sync_size ) ;
server . repl_transfer_last_fsync_off + = sync_size ;
}
2010-11-04 12:29:53 -04:00
/* Check if the transfer is now complete */
2014-10-16 11:09:29 -04:00
if ( ! usemark ) {
if ( server . repl_transfer_read = = server . repl_transfer_size )
eof_reached = 1 ;
}
if ( eof_reached ) {
2011-12-21 06:22:13 -05:00
if ( rename ( server . repl_transfer_tmpfile , server . rdb_filename ) = = - 1 ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Failed trying to rename the temp DB into dump.rdb in MASTER <-> SLAVE synchronization: %s " , strerror ( errno ) ) ;
2010-11-04 12:29:53 -04:00
replicationAbortSyncTransfer ( ) ;
return ;
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " MASTER <-> SLAVE sync: Flushing old data " ) ;
2013-02-08 04:26:19 -05:00
signalFlushedDb ( - 1 ) ;
2013-12-10 12:38:26 -05:00
emptyDb ( replicationEmptyDbCallback ) ;
2010-11-12 14:02:20 -05:00
/* Before loading the DB into memory we need to delete the readable
* handler , otherwise it will get called recursively since
* rdbLoad ( ) will call the event loop to process events from time to
* time for non blocking loading . */
aeDeleteFileEvent ( server . el , server . repl_transfer_s , AE_READABLE ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " MASTER <-> SLAVE sync: Loading DB in memory " ) ;
2015-07-26 17:17:55 -04:00
if ( rdbLoad ( server . rdb_filename ) ! = C_OK ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Failed trying to load the MASTER synchronization DB from disk " ) ;
2010-11-04 12:29:53 -04:00
replicationAbortSyncTransfer ( ) ;
return ;
}
/* Final setup of the connected slave <- master link */
zfree ( server . repl_transfer_tmpfile ) ;
close ( server . repl_transfer_fd ) ;
2015-02-04 05:24:46 -05:00
replicationCreateMasterClient ( server . repl_transfer_s ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " MASTER <-> SLAVE sync: Finished with success " ) ;
2011-12-15 10:07:49 -05:00
/* Restart the AOF subsystem now that we finished the sync. This
* will trigger an AOF rewrite , and when done will start appending
* to the new file . */
2015-07-27 03:41:48 -04:00
if ( server . aof_state ! = AOF_OFF ) {
2011-12-15 10:07:49 -05:00
int retry = 10 ;
stopAppendOnly ( ) ;
2015-07-26 17:17:55 -04:00
while ( retry - - & & startAppendOnly ( ) = = C_ERR ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Failed enabling the AOF after successful master synchronization! Trying it again in one second. " ) ;
2011-12-15 10:07:49 -05:00
sleep ( 1 ) ;
}
if ( ! retry ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " FATAL: this slave instance finished the synchronization with its master, but the AOF can't be turned on. Exiting now. " ) ;
2011-12-15 10:07:49 -05:00
exit ( 1 ) ;
}
}
2010-11-04 12:29:53 -04:00
}
2011-05-22 06:41:24 -04:00
return ;
error :
replicationAbortSyncTransfer ( ) ;
return ;
2010-11-04 12:29:53 -04:00
}
2012-06-26 03:47:47 -04:00
/* Send a synchronous command to the master. Used to send AUTH and
2012-06-27 05:26:37 -04:00
* REPLCONF commands before starting the replication with SYNC .
2012-06-26 03:47:47 -04:00
*
2013-01-30 12:33:16 -05:00
* The command returns an sds string representing the result of the
* operation . On error the first byte is a " - " .
2012-06-26 03:47:47 -04:00
*/
char * sendSynchronousCommand ( int fd , . . . ) {
va_list ap ;
sds cmd = sdsempty ( ) ;
char * arg , buf [ 256 ] ;
/* Create the command to send to the master, we use simple inline
* protocol for simplicity as currently we only send simple strings . */
va_start ( ap , fd ) ;
while ( 1 ) {
arg = va_arg ( ap , char * ) ;
if ( arg = = NULL ) break ;
if ( sdslen ( cmd ) ! = 0 ) cmd = sdscatlen ( cmd , " " , 1 ) ;
cmd = sdscat ( cmd , arg ) ;
}
cmd = sdscatlen ( cmd , " \r \n " , 2 ) ;
/* Transfer command to the server. */
if ( syncWrite ( fd , cmd , sdslen ( cmd ) , server . repl_syncio_timeout * 1000 ) = = - 1 ) {
sdsfree ( cmd ) ;
2013-01-30 12:33:16 -05:00
return sdscatprintf ( sdsempty ( ) , " -Writing to master: %s " ,
2012-06-26 03:47:47 -04:00
strerror ( errno ) ) ;
}
sdsfree ( cmd ) ;
/* Read the reply from the server. */
if ( syncReadLine ( fd , buf , sizeof ( buf ) , server . repl_syncio_timeout * 1000 ) = = - 1 )
{
2013-01-30 12:33:16 -05:00
return sdscatprintf ( sdsempty ( ) , " -Reading from master: %s " ,
2012-06-26 03:47:47 -04:00
strerror ( errno ) ) ;
}
2013-01-30 12:33:16 -05:00
return sdsnew ( buf ) ;
}
/* Try a partial resynchronization with the master if we are about to reconnect.
* If there is no cached master structure , at least try to issue a
* " PSYNC ? -1 " command in order to trigger a full resync using the PSYNC
* command in order to obtain the master run id and the master replication
* global offset .
*
* This function is designed to be called from syncWithMaster ( ) , so the
* following assumptions are made :
*
* 1 ) We pass the function an already connected socket " fd " .
* 2 ) This function does not close the file descriptor " fd " . However in case
* of successful partial resynchronization , the function will reuse
* ' fd ' as file descriptor of the server . master client structure .
*
* The function returns :
*
* PSYNC_CONTINUE : If the PSYNC command succeded and we can continue .
* PSYNC_FULLRESYNC : If PSYNC is supported but a full resync is needed .
* In this case the master run_id and global replication
* offset is saved .
* PSYNC_NOT_SUPPORTED : If the server does not understand PSYNC at all and
* the caller should fall back to SYNC .
*/
# define PSYNC_CONTINUE 0
# define PSYNC_FULLRESYNC 1
# define PSYNC_NOT_SUPPORTED 2
int slaveTryPartialResynchronization ( int fd ) {
char * psync_runid ;
char psync_offset [ 32 ] ;
sds reply ;
/* Initially set repl_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
* 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
* client structure representing the master into server . master . */
server . repl_master_initial_offset = - 1 ;
if ( server . cached_master ) {
psync_runid = server . cached_master - > replrunid ;
snprintf ( psync_offset , sizeof ( psync_offset ) , " %lld " , server . cached_master - > reploff + 1 ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Trying a partial resynchronization (request %s:%s). " , psync_runid , psync_offset ) ;
2013-01-30 12:33:16 -05:00
} else {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Partial resynchronization not possible (no cached master) " ) ;
2013-01-30 12:33:16 -05:00
psync_runid = " ? " ;
memcpy ( psync_offset , " -1 " , 3 ) ;
}
/* Issue the PSYNC command */
reply = sendSynchronousCommand ( fd , " PSYNC " , psync_runid , psync_offset , NULL ) ;
if ( ! strncmp ( reply , " +FULLRESYNC " , 11 ) ) {
2013-02-01 09:28:02 -05:00
char * runid = NULL , * offset = NULL ;
2013-01-30 12:33:16 -05:00
/* FULL RESYNC, parse the reply in order to extract the run id
* and the replication offset . */
runid = strchr ( reply , ' ' ) ;
if ( runid ) {
runid + + ;
offset = strchr ( runid , ' ' ) ;
if ( offset ) offset + + ;
}
2015-07-27 03:41:48 -04:00
if ( ! runid | | ! offset | | ( offset - runid - 1 ) ! = CONFIG_RUN_ID_SIZE ) {
serverLog ( LL_WARNING ,
2013-01-30 12:33:16 -05:00
" Master replied with wrong +FULLRESYNC syntax. " ) ;
2013-02-13 12:33:33 -05:00
/* This is an unexpected condition, actually the +FULLRESYNC
* reply means that the master supports PSYNC , but the reply
* format seems wrong . To stay safe we blank the master
2013-02-13 12:43:40 -05:00
* runid to make sure next PSYNCs will fail . */
2015-07-27 03:41:48 -04:00
memset ( server . repl_master_runid , 0 , CONFIG_RUN_ID_SIZE + 1 ) ;
2013-01-30 12:33:16 -05:00
} else {
memcpy ( server . repl_master_runid , runid , offset - runid - 1 ) ;
2015-07-27 03:41:48 -04:00
server . repl_master_runid [ CONFIG_RUN_ID_SIZE ] = ' \0 ' ;
2013-01-30 12:33:16 -05:00
server . repl_master_initial_offset = strtoll ( offset , NULL , 10 ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Full resync from master: %s:%lld " ,
2013-01-30 12:33:16 -05:00
server . repl_master_runid ,
server . repl_master_initial_offset ) ;
}
/* We are going to full resync, discard the cached master structure. */
replicationDiscardCachedMaster ( ) ;
sdsfree ( reply ) ;
return PSYNC_FULLRESYNC ;
}
2012-06-26 03:47:47 -04:00
2013-01-30 12:33:16 -05:00
if ( ! strncmp ( reply , " +CONTINUE " , 9 ) ) {
/* Partial resync was accepted, set the replication state accordingly */
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE ,
2013-01-30 12:33:16 -05:00
" Successful partial resynchronization with master. " ) ;
sdsfree ( reply ) ;
replicationResurrectCachedMaster ( fd ) ;
return PSYNC_CONTINUE ;
2012-06-26 03:47:47 -04:00
}
2013-01-30 12:33:16 -05:00
/* If we reach this point we receied either an error since the master does
* not understand PSYNC , or an unexpected reply from the master .
2014-01-08 08:11:02 -05:00
* Return PSYNC_NOT_SUPPORTED to the caller in both cases . */
2013-01-30 12:33:16 -05:00
if ( strncmp ( reply , " -ERR " , 4 ) ) {
/* If it's not an error, log the unexpected event. */
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2013-01-30 12:33:16 -05:00
" Unexpected reply to PSYNC from master: %s " , reply ) ;
} else {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE ,
2013-01-30 12:33:16 -05:00
" Master does not support PSYNC or is in "
" error state (reply: %s) " , reply ) ;
}
sdsfree ( reply ) ;
replicationDiscardCachedMaster ( ) ;
return PSYNC_NOT_SUPPORTED ;
2012-06-26 03:47:47 -04:00
}
2011-05-19 12:53:06 -04:00
void syncWithMaster ( aeEventLoop * el , int fd , void * privdata , int mask ) {
2012-06-26 03:47:47 -04:00
char tmpfile [ 256 ] , * err ;
2010-06-21 18:07:48 -04:00
int dfd , maxtries = 5 ;
2013-01-30 12:33:16 -05:00
int sockerr = 0 , psync_result ;
2012-08-31 09:32:57 -04:00
socklen_t errlen = sizeof ( sockerr ) ;
2015-07-27 03:41:48 -04:00
UNUSED ( el ) ;
UNUSED ( privdata ) ;
UNUSED ( mask ) ;
2010-06-21 18:07:48 -04:00
2011-10-18 05:09:32 -04:00
/* If this event fired after the user turned the instance into a master
* with SLAVEOF NO ONE we must just return ASAP . */
2015-07-27 03:41:48 -04:00
if ( server . repl_state = = REPL_STATE_NONE ) {
2011-10-18 05:09:32 -04:00
close ( fd ) ;
return ;
}
2012-08-31 09:32:57 -04:00
/* Check for errors in the socket. */
if ( getsockopt ( fd , SOL_SOCKET , SO_ERROR , & sockerr , & errlen ) = = - 1 )
sockerr = errno ;
if ( sockerr ) {
aeDeleteFileEvent ( server . el , fd , AE_READABLE | AE_WRITABLE ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Error condition on socket for SYNC: %s " ,
2012-08-31 09:32:57 -04:00
strerror ( sockerr ) ) ;
goto error ;
}
/* If we were connecting, it's time to send a non blocking PING, we want to
* make sure the master is able to reply before going into the actual
* replication process where we have long timeouts in the order of
* seconds ( in the meantime the slave would block ) . */
2015-07-27 03:41:48 -04:00
if ( server . repl_state = = REPL_STATE_CONNECTING ) {
serverLog ( LL_NOTICE , " Non blocking connect for SYNC fired the event. " ) ;
2012-08-31 09:32:57 -04:00
/* Delete the writable event so that the readable event remains
* registered and we can wait for the PONG reply . */
aeDeleteFileEvent ( server . el , fd , AE_WRITABLE ) ;
2015-07-27 03:41:48 -04:00
server . repl_state = REPL_STATE_RECEIVE_PONG ;
2012-08-31 09:32:57 -04:00
/* Send the PING, don't check for errors at all, we have the timeout
* that will take care about this . */
syncWrite ( fd , " PING \r \n " , 6 , 100 ) ;
return ;
}
/* Receive the PONG command. */
2015-07-27 03:41:48 -04:00
if ( server . repl_state = = REPL_STATE_RECEIVE_PONG ) {
2012-08-31 09:32:57 -04:00
char buf [ 1024 ] ;
/* Delete the readable event, we no longer need it now that there is
* the PING reply to read . */
aeDeleteFileEvent ( server . el , fd , AE_READABLE ) ;
/* Read the reply with explicit timeout. */
buf [ 0 ] = ' \0 ' ;
if ( syncReadLine ( fd , buf , sizeof ( buf ) ,
server . repl_syncio_timeout * 1000 ) = = - 1 )
{
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2012-08-31 09:32:57 -04:00
" I/O error reading PING reply from master: %s " ,
strerror ( errno ) ) ;
goto error ;
}
2013-02-12 10:53:27 -05:00
/* We accept only two replies as valid, a positive +PONG reply
* ( we just check for " + " ) or an authentication error .
* Note that older versions of Redis replied with " operation not
* permitted " instead of using a proper error code, so we test
* both . */
if ( buf [ 0 ] ! = ' + ' & &
strncmp ( buf , " -NOAUTH " , 7 ) ! = 0 & &
strncmp ( buf , " -ERR operation not permitted " , 28 ) ! = 0 )
{
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Error reply to PING from master: '%s' " , buf ) ;
2012-08-31 09:32:57 -04:00
goto error ;
} else {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE ,
2012-08-31 09:32:57 -04:00
" Master replied to PING, replication can continue... " ) ;
}
}
2010-06-21 18:07:48 -04:00
/* AUTH with the master if required. */
if ( server . masterauth ) {
2012-06-26 03:47:47 -04:00
err = sendSynchronousCommand ( fd , " AUTH " , server . masterauth , NULL ) ;
2013-01-30 12:33:16 -05:00
if ( err [ 0 ] = = ' - ' ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Unable to AUTH to MASTER: %s " , err ) ;
2012-06-26 03:47:47 -04:00
sdsfree ( err ) ;
2011-05-19 12:53:06 -04:00
goto error ;
}
2013-01-30 12:33:16 -05:00
sdsfree ( err ) ;
2012-06-26 03:47:47 -04:00
}
/* Set the slave port, so that Master's INFO command can list the
* slave listening port correctly . */
{
sds port = sdsfromlonglong ( server . port ) ;
err = sendSynchronousCommand ( fd , " REPLCONF " , " listening-port " , port ,
NULL ) ;
sdsfree ( port ) ;
/* Ignore the error if any, not all the Redis versions support
* REPLCONF listening - port . */
2013-01-30 12:33:16 -05:00
if ( err [ 0 ] = = ' - ' ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " (Non critical) Master does not understand REPLCONF listening-port: %s " , err ) ;
2010-06-21 18:07:48 -04:00
}
2013-01-30 12:33:16 -05:00
sdsfree ( err ) ;
2010-06-21 18:07:48 -04:00
}
2013-01-30 12:33:16 -05:00
/* Try a partial resynchonization. If we don't have a cached master
* slaveTryPartialResynchronization ( ) will at least try to use PSYNC
* to start a full resynchronization so that we get the master run id
* and the global offset , to try a partial resync at the next
* reconnection attempt . */
psync_result = slaveTryPartialResynchronization ( fd ) ;
if ( psync_result = = PSYNC_CONTINUE ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " MASTER <-> SLAVE sync: Master accepted a Partial Resynchronization. " ) ;
2013-01-30 12:33:16 -05:00
return ;
}
2015-07-28 10:14:52 -04:00
/* 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
* transfer us an entirely different data set and we have no way to
* incrementally feed our slaves after that . */
disconnectSlaves ( ) ; /* Force our slaves to resync with us as well. */
freeReplicationBacklog ( ) ; /* Don't allow our chained slaves to PSYNC. */
2013-01-30 12:33:16 -05:00
/* Fall back to SYNC if needed. Otherwise psync_result == PSYNC_FULLRESYNC
* and the server . repl_master_runid and repl_master_initial_offset are
* already populated . */
if ( psync_result = = PSYNC_NOT_SUPPORTED ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Retrying with SYNC... " ) ;
2013-01-30 12:33:16 -05:00
if ( syncWrite ( fd , " SYNC \r \n " , 6 , server . repl_syncio_timeout * 1000 ) = = - 1 ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " I/O error writing to MASTER: %s " ,
2013-01-30 12:33:16 -05:00
strerror ( errno ) ) ;
goto error ;
}
2010-06-21 18:07:48 -04:00
}
2010-11-04 13:09:35 -04:00
/* Prepare a suitable temp file for bulk transfer */
2010-06-21 18:07:48 -04:00
while ( maxtries - - ) {
snprintf ( tmpfile , 256 ,
2012-03-27 11:39:58 -04:00
" temp-%d.%ld.rdb " , ( int ) server . unixtime , ( long int ) getpid ( ) ) ;
2010-06-21 18:07:48 -04:00
dfd = open ( tmpfile , O_CREAT | O_WRONLY | O_EXCL , 0644 ) ;
if ( dfd ! = - 1 ) break ;
sleep ( 1 ) ;
}
if ( dfd = = - 1 ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Opening the temp file needed for MASTER <-> SLAVE synchronization: %s " , strerror ( errno ) ) ;
2011-05-19 12:53:06 -04:00
goto error ;
2010-06-21 18:07:48 -04:00
}
2010-11-04 12:29:53 -04:00
/* Setup the non blocking download of the bulk file. */
2011-05-19 12:53:06 -04:00
if ( aeCreateFileEvent ( server . el , fd , AE_READABLE , readSyncBulkPayload , NULL )
2010-11-04 12:35:03 -04:00
= = AE_ERR )
2010-11-04 12:29:53 -04:00
{
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2013-01-03 08:22:55 -05:00
" Can't create readable event for SYNC: %s (fd=%d) " ,
strerror ( errno ) , fd ) ;
2011-05-19 12:53:06 -04:00
goto error ;
2010-06-21 18:07:48 -04:00
}
2011-05-19 12:53:06 -04:00
2015-07-27 03:41:48 -04:00
server . repl_state = REPL_STATE_TRANSFER ;
2012-08-24 13:28:44 -04:00
server . repl_transfer_size = - 1 ;
server . repl_transfer_read = 0 ;
server . repl_transfer_last_fsync_off = 0 ;
2010-11-04 12:29:53 -04:00
server . repl_transfer_fd = dfd ;
2012-03-27 11:39:58 -04:00
server . repl_transfer_lastio = server . unixtime ;
2010-11-04 12:29:53 -04:00
server . repl_transfer_tmpfile = zstrdup ( tmpfile ) ;
2011-05-19 12:53:06 -04:00
return ;
error :
close ( fd ) ;
2012-08-31 09:32:57 -04:00
server . repl_transfer_s = - 1 ;
2015-07-27 03:41:48 -04:00
server . repl_state = REPL_STATE_CONNECT ;
2011-05-19 12:53:06 -04:00
return ;
}
int connectWithMaster ( void ) {
int fd ;
2015-06-11 06:57:53 -04:00
fd = anetTcpNonBlockBestEffortBindConnect ( NULL ,
2015-07-27 03:41:48 -04:00
server . masterhost , server . masterport , NET_FIRST_BIND_ADDR ) ;
2011-05-19 12:53:06 -04:00
if ( fd = = - 1 ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Unable to connect to MASTER: %s " ,
2011-05-19 12:53:06 -04:00
strerror ( errno ) ) ;
2015-07-26 17:17:55 -04:00
return C_ERR ;
2011-05-19 12:53:06 -04:00
}
2011-06-09 09:35:07 -04:00
if ( aeCreateFileEvent ( server . el , fd , AE_READABLE | AE_WRITABLE , syncWithMaster , NULL ) = =
2011-05-22 06:41:24 -04:00
AE_ERR )
2011-05-19 12:53:06 -04:00
{
close ( fd ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Can't create readable event for SYNC " ) ;
2015-07-26 17:17:55 -04:00
return C_ERR ;
2011-05-19 12:53:06 -04:00
}
2012-03-27 11:39:58 -04:00
server . repl_transfer_lastio = server . unixtime ;
2011-05-19 12:53:06 -04:00
server . repl_transfer_s = fd ;
2015-07-27 03:41:48 -04:00
server . repl_state = REPL_STATE_CONNECTING ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2010-06-21 18:07:48 -04:00
}
2011-11-30 09:35:16 -05:00
/* This function can be called when a non blocking connection is currently
* in progress to undo it . */
void undoConnectWithMaster ( void ) {
int fd = server . repl_transfer_s ;
2015-07-27 03:41:48 -04:00
serverAssert ( server . repl_state = = REPL_STATE_CONNECTING | |
server . repl_state = = REPL_STATE_RECEIVE_PONG ) ;
2011-11-30 09:35:16 -05:00
aeDeleteFileEvent ( server . el , fd , AE_READABLE | AE_WRITABLE ) ;
close ( fd ) ;
server . repl_transfer_s = - 1 ;
2015-07-27 03:41:48 -04:00
server . repl_state = REPL_STATE_CONNECT ;
2011-11-30 09:35:16 -05:00
}
2013-01-14 05:39:54 -05:00
/* This function aborts a non blocking replication attempt if there is one
* in progress , by canceling the non - blocking connect attempt or
* the initial bulk transfer .
*
* If there was a replication handshake in progress 1 is returned and
2015-07-27 03:41:48 -04:00
* the replication state ( server . repl_state ) set to REPL_STATE_CONNECT .
2013-01-14 05:39:54 -05:00
*
* Otherwise zero is returned and no operation is perforemd at all . */
int cancelReplicationHandshake ( void ) {
2015-07-27 03:41:48 -04:00
if ( server . repl_state = = REPL_STATE_TRANSFER ) {
2013-01-14 05:39:54 -05:00
replicationAbortSyncTransfer ( ) ;
2015-07-27 03:41:48 -04:00
} else if ( server . repl_state = = REPL_STATE_CONNECTING | |
server . repl_state = = REPL_STATE_RECEIVE_PONG )
2013-01-14 05:39:54 -05:00
{
undoConnectWithMaster ( ) ;
} else {
return 0 ;
}
return 1 ;
}
2013-03-04 07:22:21 -05:00
/* Set replication to the specified master address and port. */
void replicationSetMaster ( char * ip , int port ) {
sdsfree ( server . masterhost ) ;
2013-03-04 09:39:43 -05:00
server . masterhost = sdsnew ( ip ) ;
2013-03-04 07:22:21 -05:00
server . masterport = port ;
if ( server . master ) freeClient ( server . master ) ;
Replication: disconnect blocked clients when switching to slave role.
Bug as old as Redis and blocking operations. It's hard to trigger since
only happens on instance role switch, but the results are quite bad
since an inconsistency between master and slave is created.
How to trigger the bug is a good description of the bug itself.
1. Client does "BLPOP mylist 0" in master.
2. Master is turned into slave, that replicates from New-Master.
3. Client does "LPUSH mylist foo" in New-Master.
4. New-Master propagates write to slave.
5. Slave receives the LPUSH, the blocked client get served.
Now Master "mylist" key has "foo", Slave "mylist" key is empty.
Highlights:
* At step "2" above, the client remains attached, basically escaping any
check performed during command dispatch: read only slave, in that case.
* At step "5" the slave (that was the master), serves the blocked client
consuming a list element, which is not consumed on the master side.
This scenario is technically likely to happen during failovers, however
since Redis Sentinel already disconnects clients using the CLIENT
command when changing the role of the instance, the bug is avoided in
Sentinel deployments.
Closes #2473.
2015-03-24 11:00:09 -04:00
disconnectAllBlockedClients ( ) ; /* Clients blocked in master, now slave. */
2013-03-04 07:22:21 -05:00
disconnectSlaves ( ) ; /* Force our slaves to resync with us as well. */
replicationDiscardCachedMaster ( ) ; /* Don't try a PSYNC. */
freeReplicationBacklog ( ) ; /* Don't allow our chained slaves to PSYNC. */
cancelReplicationHandshake ( ) ;
2015-07-27 03:41:48 -04:00
server . repl_state = REPL_STATE_CONNECT ;
Make new masters inherit replication offsets.
Currently replication offsets could be used into a limited way in order
to understand, out of a set of slaves, what is the one with the most
updated data. For example this comparison is possible of N slaves
were replicating all with the same master.
However the replication offset was not transferred from master to slaves
(that are later promoted as masters) in any way, so for instance if
there were three instances A, B, C, with A master and B and C
replication from A, the following could happen:
C disconnects from A.
B is turned into master.
A is switched to master of B.
B receives some write.
In this context there was no way to compare the offset of A and C,
because B would use its own local master replication offset as
replication offset to initialize the replication with A.
With this commit what happens is that when B is turned into master it
inherits the replication offset from A, making A and C comparable.
In the above case assuming no inconsistencies are created during the
disconnection and failover process, A will show to have a replication
offset greater than C.
Note that this does not mean offsets are always comparable to understand
what is, in a set of instances, since in more complex examples the
replica with the higher replication offset could be partitioned away
when picking the instance to elect as new master. However this in
general improves the ability of a system to try to pick a good replica
to promote to master.
2013-12-22 05:43:25 -05:00
server . master_repl_offset = 0 ;
2014-01-17 12:20:31 -05:00
server . repl_down_since = 0 ;
2013-03-04 07:22:21 -05:00
}
/* Cancel replication, setting the instance as a master itself. */
void replicationUnsetMaster ( void ) {
if ( server . masterhost = = NULL ) return ; /* Nothing to do. */
sdsfree ( server . masterhost ) ;
server . masterhost = NULL ;
Make new masters inherit replication offsets.
Currently replication offsets could be used into a limited way in order
to understand, out of a set of slaves, what is the one with the most
updated data. For example this comparison is possible of N slaves
were replicating all with the same master.
However the replication offset was not transferred from master to slaves
(that are later promoted as masters) in any way, so for instance if
there were three instances A, B, C, with A master and B and C
replication from A, the following could happen:
C disconnects from A.
B is turned into master.
A is switched to master of B.
B receives some write.
In this context there was no way to compare the offset of A and C,
because B would use its own local master replication offset as
replication offset to initialize the replication with A.
With this commit what happens is that when B is turned into master it
inherits the replication offset from A, making A and C comparable.
In the above case assuming no inconsistencies are created during the
disconnection and failover process, A will show to have a replication
offset greater than C.
Note that this does not mean offsets are always comparable to understand
what is, in a set of instances, since in more complex examples the
replica with the higher replication offset could be partitioned away
when picking the instance to elect as new master. However this in
general improves the ability of a system to try to pick a good replica
to promote to master.
2013-12-22 05:43:25 -05:00
if ( server . master ) {
if ( listLength ( server . slaves ) = = 0 ) {
/* If this instance is turned into a master and there are no
* slaves , it inherits the replication offset from the master .
* Under certain conditions this makes replicas comparable by
* replication offset to understand what is the most updated . */
server . master_repl_offset = server . master - > reploff ;
freeReplicationBacklog ( ) ;
}
freeClient ( server . master ) ;
}
2013-03-04 07:22:21 -05:00
replicationDiscardCachedMaster ( ) ;
cancelReplicationHandshake ( ) ;
2015-07-27 03:41:48 -04:00
server . repl_state = REPL_STATE_NONE ;
2013-03-04 07:22:21 -05:00
}
2015-07-28 08:36:50 -04:00
/* This function is called when the slave lose the connection with the
* master into an unexpected way . */
void replicationHandleMasterDisconnection ( void ) {
server . master = NULL ;
server . repl_state = REPL_STATE_CONNECT ;
server . repl_down_since = server . unixtime ;
2015-07-28 10:14:52 -04:00
/* We lost connection with our master, don't disconnect slaves yet,
* maybe we ' ll be able to PSYNC with our master later . We ' ll disconnect
* the slaves only if we ' ll have to do a full resync with our master . */
2015-07-28 08:36:50 -04:00
}
2015-07-26 09:20:46 -04:00
void slaveofCommand ( client * c ) {
2013-03-05 06:39:11 -05:00
/* SLAVEOF is not allowed in cluster mode as replication is automatically
* configured using the current address of the master node . */
if ( server . cluster_enabled ) {
addReplyError ( c , " SLAVEOF not allowed in cluster mode. " ) ;
return ;
}
/* The special host/port combination "NO" "ONE" turns the instance
* into a master . Otherwise the new master address is set . */
2010-06-21 18:07:48 -04:00
if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " no " ) & &
! strcasecmp ( c - > argv [ 2 ] - > ptr , " one " ) ) {
if ( server . masterhost ) {
2013-03-04 07:22:21 -05:00
replicationUnsetMaster ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " MASTER MODE enabled (user request) " ) ;
2010-06-21 18:07:48 -04:00
}
} else {
2012-01-16 05:27:22 -05:00
long port ;
2015-07-26 17:17:55 -04:00
if ( ( getLongFromObjectOrReply ( c , c - > argv [ 2 ] , & port , NULL ) ! = C_OK ) )
2012-01-16 05:27:22 -05:00
return ;
/* Check if we are already attached to the specified slave */
if ( server . masterhost & & ! strcasecmp ( server . masterhost , c - > argv [ 1 ] - > ptr )
& & server . masterport = = port ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " SLAVE OF would result into synchronization with the master we are already connected with. No operation performed. " ) ;
2012-01-16 05:27:22 -05:00
addReplySds ( c , sdsnew ( " +OK Already connected to specified master \r \n " ) ) ;
return ;
}
/* There was no previous master or the user specified a different one,
* we can continue . */
2013-03-04 07:22:21 -05:00
replicationSetMaster ( c - > argv [ 1 ] - > ptr , port ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " SLAVE OF %s:%d enabled (user request) " ,
2010-06-21 18:07:48 -04:00
server . masterhost , server . masterport ) ;
}
addReply ( c , shared . ok ) ;
}
2010-11-04 12:29:53 -04:00
2014-06-07 11:27:49 -04:00
/* ROLE command: provide information about the role of the instance
* ( master or slave ) and additional information related to replication
* in an easy to process format . */
2015-07-26 09:20:46 -04:00
void roleCommand ( client * c ) {
2014-06-07 11:27:49 -04:00
if ( server . masterhost = = NULL ) {
listIter li ;
listNode * ln ;
void * mbcount ;
int slaves = 0 ;
addReplyMultiBulkLen ( c , 3 ) ;
addReplyBulkCBuffer ( c , " master " , 6 ) ;
addReplyLongLong ( c , server . master_repl_offset ) ;
mbcount = addDeferredMultiBulkLength ( c ) ;
listRewind ( server . slaves , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2015-07-27 03:41:48 -04:00
char ip [ NET_IP_STR_LEN ] ;
2014-06-07 11:27:49 -04:00
if ( anetPeerToString ( slave - > fd , ip , sizeof ( ip ) , NULL ) = = - 1 ) continue ;
2015-07-27 03:41:48 -04:00
if ( slave - > replstate ! = SLAVE_STATE_ONLINE ) continue ;
2014-06-07 11:27:49 -04:00
addReplyMultiBulkLen ( c , 3 ) ;
addReplyBulkCString ( c , ip ) ;
addReplyBulkLongLong ( c , slave - > slave_listening_port ) ;
addReplyBulkLongLong ( c , slave - > repl_ack_off ) ;
slaves + + ;
}
setDeferredMultiBulkLength ( c , mbcount , slaves ) ;
} else {
2014-06-07 11:38:16 -04:00
char * slavestate = NULL ;
2014-06-21 05:17:18 -04:00
addReplyMultiBulkLen ( c , 5 ) ;
2014-06-07 11:27:49 -04:00
addReplyBulkCBuffer ( c , " slave " , 5 ) ;
addReplyBulkCString ( c , server . masterhost ) ;
addReplyLongLong ( c , server . masterport ) ;
2014-06-07 11:38:16 -04:00
switch ( server . repl_state ) {
2015-07-27 03:41:48 -04:00
case REPL_STATE_NONE : slavestate = " none " ; break ;
case REPL_STATE_CONNECT : slavestate = " connect " ; break ;
case REPL_STATE_CONNECTING : slavestate = " connecting " ; break ;
case REPL_STATE_RECEIVE_PONG : /* see next */
case REPL_STATE_TRANSFER : slavestate = " sync " ; break ;
case REPL_STATE_CONNECTED : slavestate = " connected " ; break ;
2014-06-07 11:38:16 -04:00
default : slavestate = " unknown " ; break ;
}
addReplyBulkCString ( c , slavestate ) ;
addReplyLongLong ( c , server . master ? server . master - > reploff : - 1 ) ;
2014-06-07 11:27:49 -04:00
}
}
2013-05-27 04:41:53 -04:00
/* Send a REPLCONF ACK command to the master to inform it about the current
* processed offset . If we are not connected with a master , the command has
* no effects . */
void replicationSendAck ( void ) {
2015-07-26 09:20:46 -04:00
client * c = server . master ;
2013-05-27 04:41:53 -04:00
if ( c ! = NULL ) {
2015-07-27 03:41:48 -04:00
c - > flags | = CLIENT_MASTER_FORCE_REPLY ;
2013-05-27 04:41:53 -04:00
addReplyMultiBulkLen ( c , 3 ) ;
addReplyBulkCString ( c , " REPLCONF " ) ;
addReplyBulkCString ( c , " ACK " ) ;
addReplyBulkLongLong ( c , c - > reploff ) ;
2015-07-27 03:41:48 -04:00
c - > flags & = ~ CLIENT_MASTER_FORCE_REPLY ;
2013-05-27 04:41:53 -04:00
}
}
2013-01-30 12:33:16 -05:00
/* ---------------------- MASTER CACHING FOR PSYNC -------------------------- */
/* In order to implement partial synchronization we need to be able to cache
* our master ' s client structure after a transient disconnection .
* It is cached into server . cached_master and flushed away using the following
* functions . */
/* This function is called by freeClient() in order to cache the master
* client structure instead of destryoing it . freeClient ( ) will return
* ASAP after this function returns , so every action needed to avoid problems
* with a client that is really " suspended " has to be done by this function .
*
* The other functions that will deal with the cached master are :
*
* replicationDiscardCachedMaster ( ) that will make sure to kill the client
* as for some reason we don ' t want to use it in the future .
*
* replicationResurrectCachedMaster ( ) that is used after a successful PSYNC
* handshake in order to reactivate the cached master .
*/
2015-07-26 09:20:46 -04:00
void replicationCacheMaster ( client * c ) {
2013-01-30 12:33:16 -05:00
listNode * ln ;
2015-07-26 09:29:53 -04:00
serverAssert ( server . master ! = NULL & & server . cached_master = = NULL ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Caching the disconnected master state. " ) ;
2013-01-30 12:33:16 -05:00
/* Remove from the list of clients, we don't want this client to be
* listed by CLIENT LIST or processed in any way by batch operations . */
ln = listSearchKey ( server . clients , c ) ;
2015-07-26 09:29:53 -04:00
serverAssert ( ln ! = NULL ) ;
2013-01-30 12:33:16 -05:00
listDelNode ( server . clients , ln ) ;
/* Save the master. Server.master will be set to null later by
* replicationHandleMasterDisconnection ( ) . */
server . cached_master = server . master ;
/* Remove the event handlers and close the socket. We'll later reuse
* the socket of the new connection with the master during PSYNC . */
aeDeleteFileEvent ( server . el , c - > fd , AE_READABLE ) ;
aeDeleteFileEvent ( server . el , c - > fd , AE_WRITABLE ) ;
close ( c - > fd ) ;
/* Set fd to -1 so that we can safely call freeClient(c) later. */
c - > fd = - 1 ;
2014-04-28 11:36:57 -04:00
/* Invalidate the Peer ID cache. */
if ( c - > peerid ) {
sdsfree ( c - > peerid ) ;
c - > peerid = NULL ;
}
2013-01-30 12:33:16 -05:00
/* Caching the master happens instead of the actual freeClient() call,
* so make sure to adjust the replication state . This function will
* also set server . master to NULL . */
replicationHandleMasterDisconnection ( ) ;
}
/* Free a cached master, called when there are no longer the conditions for
* a partial resync on reconnection . */
void replicationDiscardCachedMaster ( void ) {
if ( server . cached_master = = NULL ) return ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " Discarding previously cached master state. " ) ;
server . cached_master - > flags & = ~ CLIENT_MASTER ;
2013-01-30 12:33:16 -05:00
freeClient ( server . cached_master ) ;
server . cached_master = NULL ;
}
/* Turn the cached master into the current master, using the file descriptor
* passed as argument as the socket for the new master .
*
2014-01-16 23:02:38 -05:00
* This function is called when successfully setup a partial resynchronization
2013-01-30 12:33:16 -05:00
* so the stream of data that we ' ll receive will start from were this
* master left . */
void replicationResurrectCachedMaster ( int newfd ) {
server . master = server . cached_master ;
server . cached_master = NULL ;
server . master - > fd = newfd ;
2015-07-27 03:41:48 -04:00
server . master - > flags & = ~ ( CLIENT_CLOSE_AFTER_REPLY | CLIENT_CLOSE_ASAP ) ;
2013-01-30 12:33:16 -05:00
server . master - > authenticated = 1 ;
server . master - > lastinteraction = server . unixtime ;
2015-07-27 03:41:48 -04:00
server . repl_state = REPL_STATE_CONNECTED ;
2013-01-30 12:33:16 -05:00
/* Re-add to the list of clients. */
listAddNodeTail ( server . clients , server . master ) ;
if ( aeCreateFileEvent ( server . el , newfd , AE_READABLE ,
readQueryFromClient , server . master ) ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Error resurrecting the cached master, impossible to add the readable handler: %s " , strerror ( errno ) ) ;
2013-01-30 12:33:16 -05:00
freeClientAsync ( server . master ) ; /* Close ASAP. */
}
2013-10-04 10:12:25 -04:00
/* We may also need to install the write handler as well if there is
* pending data in the write buffers . */
if ( server . master - > bufpos | | listLength ( server . master - > reply ) ) {
if ( aeCreateFileEvent ( server . el , newfd , AE_WRITABLE ,
sendReplyToClient , server . master ) ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Error resurrecting the cached master, impossible to add the writable handler: %s " , strerror ( errno ) ) ;
2013-10-04 10:12:25 -04:00
freeClientAsync ( server . master ) ; /* Close ASAP. */
}
}
2013-01-30 12:33:16 -05:00
}
2013-05-29 05:36:44 -04:00
/* ------------------------- MIN-SLAVES-TO-WRITE --------------------------- */
/* This function counts the number of slaves with lag <= min-slaves-max-lag.
* If the option is active , the server will prevent writes if there are not
* enough connected slaves with the specified lag ( or less ) . */
void refreshGoodSlavesCount ( void ) {
listIter li ;
listNode * ln ;
int good = 0 ;
if ( ! server . repl_min_slaves_to_write | |
! server . repl_min_slaves_max_lag ) return ;
listRewind ( server . slaves , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2013-05-29 05:36:44 -04:00
time_t lag = server . unixtime - slave - > repl_ack_time ;
2015-07-27 03:41:48 -04:00
if ( slave - > replstate = = SLAVE_STATE_ONLINE & &
2013-05-29 05:36:44 -04:00
lag < = server . repl_min_slaves_max_lag ) good + + ;
}
server . repl_good_slaves_count = good ;
}
2013-06-24 04:26:04 -04:00
/* ----------------------- REPLICATION SCRIPT CACHE --------------------------
* The goal of this code is to keep track of scripts already sent to every
* connected slave , in order to be able to replicate EVALSHA as it is without
* translating it to EVAL every time it is possible .
*
2013-12-05 10:35:32 -05:00
* We use a capped collection implemented by a hash table for fast lookup
2013-06-24 04:26:04 -04:00
* of scripts we can send as EVALSHA , plus a linked list that is used for
* eviction of the oldest entry when the max number of items is reached .
*
* We don ' t care about taking a different cache for every different slave
* since to fill the cache again is not very costly , the goal of this code
* is to avoid that the same big script is trasmitted a big number of times
* per second wasting bandwidth and processor speed , but it is not a problem
* if we need to rebuild the cache from scratch from time to time , every used
* script will need to be transmitted a single time to reappear in the cache .
*
* This is how the system works :
*
* 1 ) Every time a new slave connects , we flush the whole script cache .
* 2 ) We only send as EVALSHA what was sent to the master as EVALSHA , without
* trying to convert EVAL into EVALSHA specifically for slaves .
* 3 ) Every time we trasmit a script as EVAL to the slaves , we also add the
* corresponding SHA1 of the script into the cache as we are sure every
* slave knows about the script starting from now .
* 4 ) On SCRIPT FLUSH command , we replicate the command to all the slaves
* and at the same time flush the script cache .
* 5 ) When the last slave disconnects , flush the cache .
* 6 ) We handle SCRIPT LOAD as well since that ' s how scripts are loaded
* in the master sometimes .
*/
/* Initialize the script cache, only called at startup. */
void replicationScriptCacheInit ( void ) {
server . repl_scriptcache_size = 10000 ;
server . repl_scriptcache_dict = dictCreate ( & replScriptCacheDictType , NULL ) ;
server . repl_scriptcache_fifo = listCreate ( ) ;
}
/* Empty the script cache. Should be called every time we are no longer sure
2013-06-24 12:57:31 -04:00
* that every slave knows about all the scripts in our set , or when the
* current AOF " context " is no longer aware of the script . In general we
* should flush the cache :
*
* 1 ) Every time a new slave reconnects to this master and performs a
* full SYNC ( PSYNC does not require flushing ) .
* 2 ) Every time an AOF rewrite is performed .
* 3 ) Every time we are left without slaves at all , and AOF is off , in order
* to reclaim otherwise unused memory .
*/
2013-06-24 04:26:04 -04:00
void replicationScriptCacheFlush ( void ) {
2013-12-10 12:18:24 -05:00
dictEmpty ( server . repl_scriptcache_dict , NULL ) ;
2013-06-24 04:26:04 -04:00
listRelease ( server . repl_scriptcache_fifo ) ;
server . repl_scriptcache_fifo = listCreate ( ) ;
}
/* Add an entry into the script cache, if we reach max number of entries the
* oldest is removed from the list . */
void replicationScriptCacheAdd ( sds sha1 ) {
int retval ;
sds key = sdsdup ( sha1 ) ;
/* Evict oldest. */
if ( listLength ( server . repl_scriptcache_fifo ) = = server . repl_scriptcache_size )
{
listNode * ln = listLast ( server . repl_scriptcache_fifo ) ;
sds oldest = listNodeValue ( ln ) ;
retval = dictDelete ( server . repl_scriptcache_dict , oldest ) ;
2015-07-26 09:29:53 -04:00
serverAssert ( retval = = DICT_OK ) ;
2013-06-24 04:26:04 -04:00
listDelNode ( server . repl_scriptcache_fifo , ln ) ;
}
/* Add current. */
retval = dictAdd ( server . repl_scriptcache_dict , key , NULL ) ;
listAddNodeHead ( server . repl_scriptcache_fifo , key ) ;
2015-07-26 09:29:53 -04:00
serverAssert ( retval = = DICT_OK ) ;
2013-06-24 04:26:04 -04:00
}
/* Returns non-zero if the specified entry exists inside the cache, that is,
* if all the slaves are aware of this script SHA1 . */
int replicationScriptCacheExists ( sds sha1 ) {
2013-06-24 12:57:31 -04:00
return dictFind ( server . repl_scriptcache_dict , sha1 ) ! = NULL ;
2013-06-24 04:26:04 -04:00
}
2013-12-04 09:52:20 -05:00
/* ----------------------- SYNCHRONOUS REPLICATION --------------------------
* Redis synchronous replication design can be summarized in points :
*
* - Redis masters have a global replication offset , used by PSYNC .
* - Master increment the offset every time new commands are sent to slaves .
* - Slaves ping back masters with the offset processed so far .
*
* So synchronous replication adds a new WAIT command in the form :
*
* WAIT < num_replicas > < milliseconds_timeout >
*
* That returns the number of replicas that processed the query when
* we finally have at least num_replicas , or when the timeout was
* reached .
*
* The command is implemented in this way :
*
* - Every time a client processes a command , we remember the replication
* offset after sending that command to the slaves .
* - When WAIT is called , we ask slaves to send an acknowledgement ASAP .
* The client is blocked at the same time ( see blocked . c ) .
* - Once we receive enough ACKs for a given offset or when the timeout
* is reached , the WAIT command is unblocked and the reply sent to the
* client .
*/
/* This just set a flag so that we broadcast a REPLCONF GETACK command
* to all the slaves in the beforeSleep ( ) function . Note that this way
* we " group " all the clients that want to wait for synchronouns replication
* in a given event loop iteration , and send a single GETACK for them all . */
void replicationRequestAckFromSlaves ( void ) {
server . get_ack_from_slaves = 1 ;
}
/* Return the number of slaves that already acknowledged the specified
* replication offset . */
int replicationCountAcksByOffset ( long long offset ) {
listIter li ;
listNode * ln ;
int count = 0 ;
listRewind ( server . slaves , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2013-12-04 09:52:20 -05:00
2015-07-27 03:41:48 -04:00
if ( slave - > replstate ! = SLAVE_STATE_ONLINE ) continue ;
2013-12-04 09:52:20 -05:00
if ( slave - > repl_ack_off > = offset ) count + + ;
}
return count ;
}
/* WAIT for N replicas to acknowledge the processing of our latest
* write command ( and all the previous commands ) . */
2015-07-26 09:20:46 -04:00
void waitCommand ( client * c ) {
2013-12-04 09:52:20 -05:00
mstime_t timeout ;
long numreplicas , ackreplicas ;
long long offset = c - > woff ;
/* Argument parsing. */
2015-07-26 17:17:55 -04:00
if ( getLongFromObjectOrReply ( c , c - > argv [ 1 ] , & numreplicas , NULL ) ! = C_OK )
2013-12-04 09:52:20 -05:00
return ;
if ( getTimeoutFromObjectOrReply ( c , c - > argv [ 2 ] , & timeout , UNIT_MILLISECONDS )
2015-07-26 17:17:55 -04:00
! = C_OK ) return ;
2013-12-04 09:52:20 -05:00
/* First try without blocking at all. */
ackreplicas = replicationCountAcksByOffset ( c - > woff ) ;
2015-07-27 03:41:48 -04:00
if ( ackreplicas > = numreplicas | | c - > flags & CLIENT_MULTI ) {
2013-12-04 09:52:20 -05:00
addReplyLongLong ( c , ackreplicas ) ;
return ;
}
/* Otherwise block the client and put it into our list of clients
* waiting for ack from slaves . */
c - > bpop . timeout = timeout ;
c - > bpop . reploffset = offset ;
c - > bpop . numreplicas = numreplicas ;
listAddNodeTail ( server . clients_waiting_acks , c ) ;
2015-07-27 03:41:48 -04:00
blockClient ( c , BLOCKED_WAIT ) ;
2013-12-04 09:52:20 -05:00
/* Make sure that the server will send an ACK request to all the slaves
* before returning to the event loop . */
replicationRequestAckFromSlaves ( ) ;
}
/* This is called by unblockClient() to perform the blocking op type
* specific cleanup . We just remove the client from the list of clients
* waiting for replica acks . Never call it directly , call unblockClient ( )
* instead . */
2015-07-26 09:20:46 -04:00
void unblockClientWaitingReplicas ( client * c ) {
2013-12-04 09:52:20 -05:00
listNode * ln = listSearchKey ( server . clients_waiting_acks , c ) ;
2015-07-26 09:29:53 -04:00
serverAssert ( ln ! = NULL ) ;
2013-12-04 09:52:20 -05:00
listDelNode ( server . clients_waiting_acks , ln ) ;
}
/* Check if there are clients blocked in WAIT that can be unblocked since
* we received enough ACKs from slaves . */
void processClientsWaitingReplicas ( void ) {
long long last_offset = 0 ;
int last_numreplicas = 0 ;
listIter li ;
listNode * ln ;
listRewind ( server . clients_waiting_acks , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * c = ln - > value ;
2013-12-04 09:52:20 -05:00
/* Every time we find a client that is satisfied for a given
* offset and number of replicas , we remember it so the next client
* may be unblocked without calling replicationCountAcksByOffset ( )
* if the requested offset / replicas were equal or less . */
if ( last_offset & & last_offset > c - > bpop . reploffset & &
last_numreplicas > c - > bpop . numreplicas )
{
unblockClient ( c ) ;
addReplyLongLong ( c , last_numreplicas ) ;
} else {
int numreplicas = replicationCountAcksByOffset ( c - > bpop . reploffset ) ;
if ( numreplicas > = c - > bpop . numreplicas ) {
last_offset = c - > bpop . reploffset ;
last_numreplicas = numreplicas ;
unblockClient ( c ) ;
addReplyLongLong ( c , numreplicas ) ;
}
}
}
}
2014-01-29 10:39:04 -05:00
/* Return the slave replication offset for this instance, that is
* the offset for which we already processed the master replication stream . */
long long replicationGetSlaveOffset ( void ) {
long long offset = 0 ;
if ( server . masterhost ! = NULL ) {
if ( server . master ) {
offset = server . master - > reploff ;
} else if ( server . cached_master ) {
offset = server . cached_master - > reploff ;
}
}
/* offset may be -1 when the master does not support it at all, however
* this function is designed to return an offset that can express the
* amount of data processed by the master , so we return a positive
* integer . */
if ( offset < 0 ) offset = 0 ;
return offset ;
}
2013-12-04 09:52:20 -05:00
/* --------------------------- REPLICATION CRON ---------------------------- */
2010-11-04 12:29:53 -04:00
2014-01-16 23:02:38 -05:00
/* Replication cron function, called 1 time per second. */
2010-11-04 12:29:53 -04:00
void replicationCron ( void ) {
2011-11-30 09:35:16 -05:00
/* Non blocking connection timeout? */
2012-08-31 09:32:57 -04:00
if ( server . masterhost & &
2015-07-27 03:41:48 -04:00
( server . repl_state = = REPL_STATE_CONNECTING | |
server . repl_state = = REPL_STATE_RECEIVE_PONG ) & &
2011-11-30 09:35:16 -05:00
( time ( NULL ) - server . repl_transfer_lastio ) > server . repl_timeout )
{
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Timeout connecting to the MASTER... " ) ;
2011-11-30 09:35:16 -05:00
undoConnectWithMaster ( ) ;
}
2010-11-04 12:29:53 -04:00
/* Bulk transfer I/O timeout? */
2015-07-27 03:41:48 -04:00
if ( server . masterhost & & server . repl_state = = REPL_STATE_TRANSFER & &
2011-10-31 06:13:28 -04:00
( time ( NULL ) - server . repl_transfer_lastio ) > server . repl_timeout )
2010-11-04 12:29:53 -04:00
{
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Timeout receiving bulk data from MASTER... If the problem persists try to set the 'repl-timeout' parameter in redis.conf to a larger value. " ) ;
2010-11-04 12:29:53 -04:00
replicationAbortSyncTransfer ( ) ;
}
2011-01-20 07:18:23 -05:00
/* Timed out master when we are an already connected slave? */
2015-07-27 03:41:48 -04:00
if ( server . masterhost & & server . repl_state = = REPL_STATE_CONNECTED & &
2011-10-31 06:13:28 -04:00
( time ( NULL ) - server . master - > lastinteraction ) > server . repl_timeout )
2011-01-20 07:18:23 -05:00
{
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " MASTER timeout: no data nor PING received... " ) ;
2011-01-20 07:18:23 -05:00
freeClient ( server . master ) ;
}
2010-11-04 12:29:53 -04:00
/* Check if we should connect to a MASTER */
2015-07-27 03:41:48 -04:00
if ( server . repl_state = = REPL_STATE_CONNECT ) {
serverLog ( LL_NOTICE , " Connecting to MASTER %s:%d " ,
2013-11-11 03:25:36 -05:00
server . masterhost , server . masterport ) ;
2015-07-26 17:17:55 -04:00
if ( connectWithMaster ( ) = = C_OK ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE , " MASTER <-> SLAVE sync started " ) ;
2010-11-04 12:29:53 -04:00
}
}
2013-05-27 04:45:37 -04:00
2014-01-08 08:25:06 -05:00
/* Send ACK to master from time to time.
* Note that we do not send periodic acks to masters that don ' t
* support PSYNC and replication offsets . */
if ( server . masterhost & & server . master & &
2015-07-27 03:41:48 -04:00
! ( server . master - > flags & CLIENT_PRE_PSYNC ) )
2013-05-27 04:45:37 -04:00
replicationSendAck ( ) ;
2014-06-26 12:48:40 -04:00
2011-01-20 07:18:23 -05:00
/* If we have attached slaves, PING them from time to time.
* So slaves can implement an explicit timeout to masters , and will
* be able to detect a link disconnection even if the TCP connection
* will not actually go down . */
2012-12-14 11:10:40 -05:00
if ( ! ( server . cronloops % ( server . repl_ping_slave_period * server . hz ) ) ) {
2011-01-20 07:18:23 -05:00
listIter li ;
listNode * ln ;
2012-11-03 06:56:28 -04:00
robj * ping_argv [ 1 ] ;
2011-01-20 07:18:23 -05:00
2012-11-03 06:56:28 -04:00
/* First, send PING */
ping_argv [ 0 ] = createStringObject ( " PING " , 4 ) ;
replicationFeedSlaves ( server . slaves , server . slaveseldb , ping_argv , 1 ) ;
decrRefCount ( ping_argv [ 0 ] ) ;
2013-01-30 12:33:16 -05:00
/* Second, send a newline to all the slaves in pre-synchronization
* stage , that is , slaves waiting for the master to create the RDB file .
2012-11-03 06:56:28 -04:00
* The newline will be ignored by the slave but will refresh the
* last - io timer preventing a timeout . */
2011-01-20 07:18:23 -05:00
listRewind ( server . slaves , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2011-01-20 07:18:23 -05:00
2015-07-27 03:41:48 -04:00
if ( slave - > replstate = = SLAVE_STATE_WAIT_BGSAVE_START | |
( slave - > replstate = = SLAVE_STATE_WAIT_BGSAVE_END & &
server . rdb_child_type ! = RDB_CHILD_TYPE_SOCKET ) )
2014-10-17 04:23:44 -04:00
{
2011-02-21 11:50:54 -05:00
if ( write ( slave - > fd , " \n " , 1 ) = = - 1 ) {
/* Don't worry, it's just a ping. */
}
2011-01-20 07:18:23 -05:00
}
}
}
2013-01-30 12:33:16 -05:00
2013-05-27 05:17:17 -04:00
/* Disconnect timedout slaves. */
if ( listLength ( server . slaves ) ) {
listIter li ;
listNode * ln ;
listRewind ( server . slaves , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2013-05-27 05:17:17 -04:00
2015-07-27 03:41:48 -04:00
if ( slave - > replstate ! = SLAVE_STATE_ONLINE ) continue ;
if ( slave - > flags & CLIENT_PRE_PSYNC ) continue ;
2013-05-27 05:17:17 -04:00
if ( ( server . unixtime - slave - > repl_ack_time ) > server . repl_timeout )
{
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Disconnecting timedout slave: %s " ,
2014-11-11 09:10:58 -05:00
replicationGetSlaveName ( slave ) ) ;
freeClient ( slave ) ;
2013-05-27 05:17:17 -04:00
}
}
}
2013-01-30 12:33:16 -05:00
/* If we have no attached slaves and there is a replication backlog
* using memory , free it after some ( configured ) time . */
if ( listLength ( server . slaves ) = = 0 & & server . repl_backlog_time_limit & &
server . repl_backlog )
{
time_t idle = server . unixtime - server . repl_no_slaves_since ;
if ( idle > server . repl_backlog_time_limit ) {
freeReplicationBacklog ( ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_NOTICE ,
2013-01-30 12:33:16 -05:00
" Replication backlog freed after %d seconds "
2013-02-27 06:27:15 -05:00
" without connected slaves. " ,
( int ) server . repl_backlog_time_limit ) ;
2013-01-30 12:33:16 -05:00
}
}
2013-05-29 05:36:44 -04:00
2013-06-24 12:57:31 -04:00
/* If AOF is disabled and we no longer have attached slaves, we can
* free our Replication Script Cache as there is no need to propagate
* EVALSHA at all . */
if ( listLength ( server . slaves ) = = 0 & &
2015-07-27 03:41:48 -04:00
server . aof_state = = AOF_OFF & &
2013-06-24 12:57:31 -04:00
listLength ( server . repl_scriptcache_fifo ) ! = 0 )
{
replicationScriptCacheFlush ( ) ;
}
2014-10-16 03:03:52 -04:00
/* If we are using diskless replication and there are slaves waiting
* in WAIT_BGSAVE_START state , check if enough seconds elapsed and
2014-10-16 04:15:18 -04:00
* start a BGSAVE .
*
* This code is also useful to trigger a BGSAVE if the diskless
* replication was turned off with CONFIG SET , while there were already
* slaves in WAIT_BGSAVE_START state . */
if ( server . rdb_child_pid = = - 1 & & server . aof_child_pid = = - 1 ) {
2014-10-16 03:03:52 -04:00
time_t idle , max_idle = 0 ;
int slaves_waiting = 0 ;
listNode * ln ;
listIter li ;
listRewind ( server . slaves , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2015-07-27 03:41:48 -04:00
if ( slave - > replstate = = SLAVE_STATE_WAIT_BGSAVE_START ) {
2014-10-16 03:03:52 -04:00
idle = server . unixtime - slave - > lastinteraction ;
if ( idle > max_idle ) max_idle = idle ;
slaves_waiting + + ;
}
}
2014-10-27 05:36:30 -04:00
if ( slaves_waiting & & max_idle > server . repl_diskless_sync_delay ) {
/* Start a BGSAVE. Usually with socket target, or with disk target
* if there was a recent socket - > disk config change . */
2015-07-26 17:17:55 -04:00
if ( startBgsaveForReplication ( ) = = C_OK ) {
2014-10-16 03:03:52 -04:00
/* It started! We need to change the state of slaves
2014-10-29 07:48:22 -04:00
* from WAIT_BGSAVE_START to WAIT_BGSAVE_END in case
* the current target is disk . Otherwise it was already done
* by rdbSaveToSlavesSockets ( ) which is called by
* startBgsaveForReplication ( ) . */
listRewind ( server . slaves , & li ) ;
2014-10-16 03:03:52 -04:00
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = ln - > value ;
2015-08-04 10:56:00 -04:00
if ( slave - > replstate = = SLAVE_STATE_WAIT_BGSAVE_START ) {
2015-07-27 03:41:48 -04:00
slave - > replstate = SLAVE_STATE_WAIT_BGSAVE_END ;
2015-08-04 10:56:00 -04:00
replicationSendFullresyncReply ( slave ,
getPsyncInitialOffset ( ) ) ;
}
2014-10-16 03:03:52 -04:00
}
}
}
}
2013-05-29 05:36:44 -04:00
/* Refresh the number of slaves with lag <= min-slaves-max-lag. */
refreshGoodSlavesCount ( ) ;
2010-11-04 12:29:53 -04:00
}