2012-11-08 12:25:23 -05:00
/*
* 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"
2017-05-09 05:57:09 -04:00
# include "atomicvar.h"
2019-03-28 02:38:16 -04:00
# include <sys/socket.h>
2010-06-21 18:07:48 -04:00
# include <sys/uio.h>
2013-07-17 09:04:22 -04:00
# include <math.h>
2016-11-25 04:55:16 -05:00
# include <ctype.h>
2010-06-21 18:07:48 -04:00
2018-08-23 00:21:23 -04:00
static void setProtocolError ( const char * errstr , client * c ) ;
2019-03-30 06:26:58 -04:00
int postponeClientRead ( client * c ) ;
2011-12-31 10:09:46 -05:00
2015-07-25 11:25:44 -04:00
/* Return the size consumed from the allocator, for the specified SDS string,
* including internal fragmentation . This function is used in order to compute
* the client output buffer size . */
size_t sdsZmallocSize ( sds s ) {
void * sh = sdsAllocPtr ( s ) ;
return zmalloc_size ( sh ) ;
}
2012-02-07 11:41:31 -05:00
2013-07-23 05:50:17 -04:00
/* Return the amount of memory used by the sds string at object->ptr
* for a string object . */
size_t getStringObjectSdsUsedMemory ( robj * o ) {
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( NULL , o , o - > type = = OBJ_STRING ) ;
2013-07-23 05:50:17 -04:00
switch ( o - > encoding ) {
2015-07-26 09:28:00 -04:00
case OBJ_ENCODING_RAW : return sdsZmallocSize ( o - > ptr ) ;
case OBJ_ENCODING_EMBSTR : return zmalloc_size ( o ) - sizeof ( robj ) ;
2013-07-23 05:50:17 -04:00
default : return 0 ; /* Just integer encoding for now. */
}
}
2015-07-31 08:59:54 -04:00
/* Client.reply list dup and free methods. */
2010-06-21 18:07:48 -04:00
void * dupClientReplyValue ( void * o ) {
2018-02-21 13:18:34 -05:00
clientReplyBlock * old = o ;
clientReplyBlock * buf = zmalloc ( sizeof ( clientReplyBlock ) + old - > size ) ;
memcpy ( buf , o , sizeof ( clientReplyBlock ) + old - > size ) ;
return buf ;
2015-07-31 08:59:54 -04:00
}
void freeClientReplyValue ( void * o ) {
2018-02-21 13:18:34 -05:00
zfree ( o ) ;
2010-06-21 18:07:48 -04:00
}
int listMatchObjects ( void * a , void * b ) {
return equalStringObjects ( a , b ) ;
}
2017-12-05 09:59:56 -05:00
/* This function links the client to the global linked list of clients.
* unlinkClient ( ) does the opposite , among other things . */
void linkClient ( client * c ) {
listAddNodeTail ( server . clients , c ) ;
/* Note that we remember the linked list node where the client is stored,
* this way removing the client in unlinkClient ( ) will not require
* a linear scan , but just a constant time operation . */
c - > client_list_node = listLast ( server . clients ) ;
2018-06-27 07:26:01 -04:00
uint64_t id = htonu64 ( c - > id ) ;
raxInsert ( server . clients_index , ( unsigned char * ) & id , sizeof ( id ) , c , NULL ) ;
2017-12-05 09:59:56 -05:00
}
2019-09-12 03:56:54 -04:00
client * createClient ( connection * conn ) {
2015-07-26 09:20:46 -04:00
client * c = zmalloc ( sizeof ( client ) ) ;
2010-06-21 18:07:48 -04:00
2019-09-12 03:56:54 -04:00
/* passing NULL as conn it is possible to create a non connected client.
2015-07-27 03:41:48 -04:00
* This is useful since all the commands needs to be executed
2011-04-30 16:29:21 -04:00
* in the context of a client . When commands are executed in other
* contexts ( for instance a Lua script ) we need a non connected client . */
2019-09-12 03:56:54 -04:00
if ( conn ) {
connNonBlock ( conn ) ;
connEnableTcpNoDelay ( conn ) ;
2013-02-08 10:40:59 -05:00
if ( server . tcpkeepalive )
2019-09-12 03:56:54 -04:00
connKeepAlive ( conn , server . tcpkeepalive ) ;
connSetReadHandler ( conn , readQueryFromClient ) ;
connSetPrivateData ( conn , c ) ;
2010-09-06 05:27:22 -04:00
}
2010-06-21 18:07:48 -04:00
selectDb ( c , 0 ) ;
2019-03-30 06:26:58 -04:00
uint64_t client_id = + + server . next_client_id ;
2017-05-09 05:57:09 -04:00
c - > id = client_id ;
2018-11-21 05:53:18 -05:00
c - > resp = 2 ;
2019-09-12 03:56:54 -04:00
c - > conn = conn ;
2013-01-11 12:43:28 -05:00
c - > name = NULL ;
2012-03-13 07:59:27 -04:00
c - > bufpos = 0 ;
2018-08-13 12:43:36 -04:00
c - > qb_pos = 0 ;
2010-06-21 18:07:48 -04:00
c - > querybuf = sdsempty ( ) ;
Fix PSYNC2 incomplete command bug as described in #3899.
This bug was discovered by @kevinmcgehee and constituted a major hidden
bug in the PSYNC2 implementation, caused by the propagation from the
master of incomplete commands to slaves.
The bug had several results:
1. Borrowing from Kevin text in the issue: "Given that slaves blindly
copy over their master's input into their own replication backlog over
successive read syscalls, it's possible that with large commands or
small TCP buffers, partial commands are present in this buffer. If the
master were to fail before successfully propagating the entire command
to a slave, the slaves will never execute the partial command (since the
client is invalidated) but will copy it to replication backlog which may
relay those invalid bytes to its slaves on PSYNC2, corrupting the
backlog and possibly other valid commands that follow the failover.
Simple command boundaries aren't sufficient to capture this, either,
because in the case of a MULTI/EXEC block, if the master successfully
propagates a subset of the commands but not the EXEC, then the
transaction in the backlog becomes corrupt and could corrupt other
slaves that consume this data."
2. As identified by @yangsiran later, there is another effect of the
bug. For the same mechanism of the first problem, a slave having another
slave, could receive a full resynchronization request with an already
half-applied command in the backlog. Once the RDB is ready, it will be
sent to the slave, and the replication will continue sending to the
sub-slave the other half of the command, which is not valid.
The fix, designed by @yangsiran and @antirez, and implemented by
@antirez, uses a secondary buffer in order to feed the sub-masters and
update the replication backlog and offsets, only when a given part of
the query buffer is actually *applied* to the state of the instance,
that is, when the command gets processed and the command is not pending
in the Redis transaction buffer because of CLIENT_MULTI state.
Given that now the backlog and offsets representation are in agreement
with the actual processed commands, both issue 1 and 2 should no longer
be possible.
Thanks to @kevinmcgehee, @yangsiran and @oranagra for their work in
identifying and designing a fix for this problem.
2017-04-19 04:25:45 -04:00
c - > pending_querybuf = sdsempty ( ) ;
2012-03-14 10:32:30 -04:00
c - > querybuf_peak = 0 ;
2010-10-15 09:40:25 -04:00
c - > reqtype = 0 ;
2010-06-21 18:07:48 -04:00
c - > argc = 0 ;
c - > argv = NULL ;
2011-11-24 08:56:34 -05:00
c - > cmd = c - > lastcmd = NULL ;
2019-01-11 05:32:41 -05:00
c - > user = DefaultUser ;
2010-10-15 09:40:25 -04:00
c - > multibulklen = 0 ;
2010-06-21 18:07:48 -04:00
c - > bulklen = - 1 ;
c - > sentlen = 0 ;
c - > flags = 0 ;
2012-03-27 11:39:58 -04:00
c - > ctime = c - > lastinteraction = server . unixtime ;
2019-01-15 11:57:49 -05:00
/* If the default user does not require authentication, the user is
* directly authenticated . */
c - > authenticated = ( c - > user - > flags & USER_FLAG_NOPASS ) ! = 0 ;
2015-07-27 03:41:48 -04:00
c - > replstate = REPL_STATE_NONE ;
2014-11-11 11:12:12 -05:00
c - > repl_put_online_on_ack = 0 ;
2013-01-30 12:33:16 -05:00
c - > reploff = 0 ;
Fix PSYNC2 incomplete command bug as described in #3899.
This bug was discovered by @kevinmcgehee and constituted a major hidden
bug in the PSYNC2 implementation, caused by the propagation from the
master of incomplete commands to slaves.
The bug had several results:
1. Borrowing from Kevin text in the issue: "Given that slaves blindly
copy over their master's input into their own replication backlog over
successive read syscalls, it's possible that with large commands or
small TCP buffers, partial commands are present in this buffer. If the
master were to fail before successfully propagating the entire command
to a slave, the slaves will never execute the partial command (since the
client is invalidated) but will copy it to replication backlog which may
relay those invalid bytes to its slaves on PSYNC2, corrupting the
backlog and possibly other valid commands that follow the failover.
Simple command boundaries aren't sufficient to capture this, either,
because in the case of a MULTI/EXEC block, if the master successfully
propagates a subset of the commands but not the EXEC, then the
transaction in the backlog becomes corrupt and could corrupt other
slaves that consume this data."
2. As identified by @yangsiran later, there is another effect of the
bug. For the same mechanism of the first problem, a slave having another
slave, could receive a full resynchronization request with an already
half-applied command in the backlog. Once the RDB is ready, it will be
sent to the slave, and the replication will continue sending to the
sub-slave the other half of the command, which is not valid.
The fix, designed by @yangsiran and @antirez, and implemented by
@antirez, uses a secondary buffer in order to feed the sub-masters and
update the replication backlog and offsets, only when a given part of
the query buffer is actually *applied* to the state of the instance,
that is, when the command gets processed and the command is not pending
in the Redis transaction buffer because of CLIENT_MULTI state.
Given that now the backlog and offsets representation are in agreement
with the actual processed commands, both issue 1 and 2 should no longer
be possible.
Thanks to @kevinmcgehee, @yangsiran and @oranagra for their work in
identifying and designing a fix for this problem.
2017-04-19 04:25:45 -04:00
c - > read_reploff = 0 ;
2013-05-24 18:37:56 -04:00
c - > repl_ack_off = 0 ;
c - > repl_ack_time = 0 ;
2012-06-26 03:47:47 -04:00
c - > slave_listening_port = 0 ;
2016-07-27 10:41:20 -04:00
c - > slave_ip [ 0 ] = ' \0 ' ;
2015-08-06 03:23:23 -04:00
c - > slave_capa = SLAVE_CAPA_NONE ;
2010-06-21 18:07:48 -04:00
c - > reply = listCreate ( ) ;
2011-12-25 10:32:54 -05:00
c - > reply_bytes = 0 ;
2012-01-23 10:12:37 -05:00
c - > obuf_soft_limit_reached_time = 0 ;
2015-07-31 08:59:54 -04:00
listSetFreeMethod ( c - > reply , freeClientReplyValue ) ;
2010-06-21 18:07:48 -04:00
listSetDupMethod ( c - > reply , dupClientReplyValue ) ;
2015-07-27 03:41:48 -04:00
c - > btype = BLOCKED_NONE ;
2010-11-09 13:06:25 -05:00
c - > bpop . timeout = 0 ;
2017-09-06 09:43:28 -04:00
c - > bpop . keys = dictCreate ( & objectKeyHeapPointerValueDictType , NULL ) ;
2010-11-09 13:06:25 -05:00
c - > bpop . target = NULL ;
2017-09-07 03:30:50 -04:00
c - > bpop . xread_group = NULL ;
2018-01-19 10:39:09 -05:00
c - > bpop . xread_consumer = NULL ;
2018-07-09 07:26:40 -04:00
c - > bpop . xread_group_noack = 0 ;
2013-12-03 11:43:53 -05:00
c - > bpop . numreplicas = 0 ;
c - > bpop . reploffset = 0 ;
2013-12-04 09:52:20 -05:00
c - > woff = 0 ;
2010-06-21 18:07:48 -04:00
c - > watched_keys = listCreate ( ) ;
2015-07-31 12:01:23 -04:00
c - > pubsub_channels = dictCreate ( & objectKeyPointerValueDictType , NULL ) ;
2010-06-21 18:07:48 -04:00
c - > pubsub_patterns = listCreate ( ) ;
2014-04-28 11:36:57 -04:00
c - > peerid = NULL ;
2017-12-05 09:59:56 -05:00
c - > client_list_node = NULL ;
2019-06-29 20:08:41 -04:00
c - > client_tracking_redirection = 0 ;
2013-01-24 05:27:10 -05:00
listSetFreeMethod ( c - > pubsub_patterns , decrRefCountVoid ) ;
2010-06-21 18:07:48 -04:00
listSetMatchMethod ( c - > pubsub_patterns , listMatchObjects ) ;
2019-09-12 03:56:54 -04:00
if ( conn ) linkClient ( c ) ;
2010-06-21 18:07:48 -04:00
initClientMultiState ( c ) ;
return c ;
}
2018-10-09 07:15:41 -04:00
/* This funciton puts the client in the queue of clients that should write
* their output buffers to the socket . Note that it does not * yet * install
* the write handler , to start clients are put in a queue of clients that need
* to write , so we try to do that before returning in the event loop ( see the
* handleClientsWithPendingWrites ( ) function ) .
* If we fail and there is more data to write , compared to what the socket
* buffers can hold , then we ' ll really install the handler . */
void clientInstallWriteHandler ( client * c ) {
/* Schedule the client to write the output buffers to the socket only
* if not already done and , for slaves , if the slave can actually receive
* writes at this stage . */
if ( ! ( c - > flags & CLIENT_PENDING_WRITE ) & &
( c - > replstate = = REPL_STATE_NONE | |
( c - > replstate = = SLAVE_STATE_ONLINE & & ! c - > repl_put_online_on_ack ) ) )
{
/* Here instead of installing the write handler, we just flag the
* client and put it into a list of clients that have something
* to write to the socket . This way before re - entering the event
* loop , we can try to directly write to the client sockets avoiding
* a system call . We ' ll only really install the write handler if
* we ' ll not be able to write the whole reply at once . */
c - > flags | = CLIENT_PENDING_WRITE ;
listAddNodeHead ( server . clients_pending_write , c ) ;
}
}
2012-01-23 11:15:49 -05:00
/* This function is called every time we are going to transmit new data
* to the client . The behavior is the following :
*
* If the client should receive new data ( normal clients will ) the function
2015-07-26 17:17:55 -04:00
* returns C_OK , and make sure to install the write handler in our event
2012-01-23 11:15:49 -05:00
* loop so that when the socket is writable new data gets written .
*
2015-04-01 04:07:08 -04:00
* If the client should not receive new data , because it is a fake client
* ( used to load AOF in memory ) , a master or because the setup of the write
2015-07-26 17:17:55 -04:00
* handler failed , the function returns C_ERR .
2015-04-01 04:07:08 -04:00
*
2015-07-26 17:17:55 -04:00
* The function may return C_OK without actually installing the write
2015-04-01 04:07:08 -04:00
* event handler in the following cases :
*
* 1 ) The event handler should already be installed since the output buffer
2015-07-31 08:59:54 -04:00
* already contains something .
2015-04-01 04:07:08 -04:00
* 2 ) The client is a slave but not yet online , so we want to just accumulate
* writes in the buffer but not actually sending them yet .
2012-01-23 11:15:49 -05:00
*
* Typically gets called every time a reply is built , before adding more
2015-07-26 17:17:55 -04:00
* data to the clients output buffers . If the function returns C_ERR no
2012-01-23 11:15:49 -05:00
* data should be appended to the output buffers . */
2015-07-26 09:20:46 -04:00
int prepareClientToWrite ( client * c ) {
2015-04-01 04:07:08 -04:00
/* If it's the Lua client we always return ok without installing any
* handler since there is no socket at all . */
2016-03-06 07:44:24 -05:00
if ( c - > flags & ( CLIENT_LUA | CLIENT_MODULE ) ) return C_OK ;
2015-04-01 04:07:08 -04:00
CLIENT REPLY command implemented: ON, OFF and SKIP modes.
Sometimes it can be useful for clients to completely disable replies
from the Redis server. For example when the client sends fire and forget
commands or performs a mass loading of data, or in caching contexts
where new data is streamed constantly. In such contexts to use server
time and bandwidth in order to send back replies to clients, which are
going to be ignored, is a shame.
Multiple mechanisms are possible to implement such a feature. For
example it could be a feature of MULTI/EXEC, or a command prefix
such as "NOREPLY SADD myset foo", or a different mechanism that allows
to switch on/off requests using the CLIENT command.
The MULTI/EXEC approach has the problem that transactions are not
strictly part of the no-reply semantics, and if we want to insert a lot
of data in a bulk way, creating a huge MULTI/EXEC transaction in the
server memory is bad.
The prefix is the best in this specific use case since it does not allow
desynchronizations, and is pretty clear semantically. However Redis
internals and client libraries are not prepared to handle this
currently.
So the implementation uses the CLIENT command, providing a new REPLY
subcommand with three options:
CLIENT REPLY OFF disables the replies, and does not reply itself.
CLIENT REPLY ON re-enables the replies, replying +OK.
CLIENT REPLY SKIP only discards the reply of the next command, and
like OFF does not reply anything itself.
The reason to add the SKIP command is that it allows to have an easy
way to send conceptually "single" commands that don't need a reply
as the sum of two pipelined commands:
CLIENT REPLY SKIP
SET key value
Note that CLIENT REPLY ON replies with +OK so it should be used when
sending multiple commands that don't need a reply. However since it
replies with +OK the client can check that the connection is still
active and all the previous commands were received.
This is currently just into Redis "unstable" so the proposal can be
modified or abandoned based on users inputs.
2015-10-21 14:43:37 -04:00
/* CLIENT REPLY OFF / SKIP handling: don't send replies. */
if ( c - > flags & ( CLIENT_REPLY_OFF | CLIENT_REPLY_SKIP ) ) return C_ERR ;
2015-07-27 03:41:48 -04:00
/* Masters don't receive replies, unless CLIENT_MASTER_FORCE_REPLY flag
2015-04-01 04:07:08 -04:00
* is set . */
2015-07-27 03:41:48 -04:00
if ( ( c - > flags & CLIENT_MASTER ) & &
! ( c - > flags & CLIENT_MASTER_FORCE_REPLY ) ) return C_ERR ;
2015-04-01 04:07:08 -04:00
2019-09-12 03:56:54 -04:00
if ( ! c - > conn ) return C_ERR ; /* Fake client for AOF loading. */
2015-04-01 04:07:08 -04:00
2018-10-09 07:15:41 -04:00
/* Schedule the client to write the output buffers to the socket, unless
* it should already be setup to do so ( it has already pending data ) . */
if ( ! clientHasPendingReplies ( c ) ) clientInstallWriteHandler ( c ) ;
2015-04-01 04:07:08 -04:00
/* Authorize the caller to queue in the output buffer of this client. */
2015-07-26 17:17:55 -04:00
return C_OK ;
2010-08-30 08:44:34 -04:00
}
2011-03-31 10:44:43 -04:00
/* -----------------------------------------------------------------------------
* Low level functions to add more data to output buffers .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2015-07-26 09:20:46 -04:00
int _addReplyToBuffer ( client * c , const char * s , size_t len ) {
2010-09-16 05:59:53 -04:00
size_t available = sizeof ( c - > buf ) - c - > bufpos ;
2010-09-02 13:18:55 -04:00
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_CLOSE_AFTER_REPLY ) return C_OK ;
2011-03-31 10:44:43 -04:00
2010-09-02 13:18:55 -04:00
/* If there already are entries in the reply list, we cannot
* add anything more to the static buffer . */
2015-07-26 17:17:55 -04:00
if ( listLength ( c - > reply ) > 0 ) return C_ERR ;
2010-09-02 13:18:55 -04:00
/* Check that the buffer has enough space available for this string. */
2015-07-26 17:17:55 -04:00
if ( len > available ) return C_ERR ;
2010-06-21 18:07:48 -04:00
2010-09-02 13:18:55 -04:00
memcpy ( c - > buf + c - > bufpos , s , len ) ;
c - > bufpos + = len ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2010-08-30 08:44:34 -04:00
}
2018-12-17 10:59:19 -05:00
void _addReplyProtoToList ( client * c , const char * s , size_t len ) {
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_CLOSE_AFTER_REPLY ) return ;
2011-03-31 10:44:43 -04:00
2018-02-21 13:18:34 -05:00
listNode * ln = listLast ( c - > reply ) ;
clientReplyBlock * tail = ln ? listNodeValue ( ln ) : NULL ;
2018-07-16 11:56:54 -04:00
/* Note that 'tail' may be NULL even if we have a tail node, becuase when
* addDeferredMultiBulkLength ( ) is used , it sets a dummy node to NULL just
* fo fill it later , when the size of the bulk length is set . */
2018-02-21 13:18:34 -05:00
/* Append to tail string when possible. */
if ( tail ) {
2018-07-16 11:56:54 -04:00
/* Copy the part we can fit into the tail, and leave the rest for a
* new node */
2018-02-21 13:18:34 -05:00
size_t avail = tail - > size - tail - > used ;
size_t copy = avail > = len ? len : avail ;
memcpy ( tail - > buf + tail - > used , s , copy ) ;
tail - > used + = copy ;
s + = copy ;
len - = copy ;
}
if ( len ) {
2018-07-16 11:56:54 -04:00
/* Create a new node, make sure it is allocated to at
* least PROTO_REPLY_CHUNK_BYTES */
2018-02-21 13:18:34 -05:00
size_t size = len < PROTO_REPLY_CHUNK_BYTES ? PROTO_REPLY_CHUNK_BYTES : len ;
tail = zmalloc ( size + sizeof ( clientReplyBlock ) ) ;
/* take over the allocation's internal fragmentation */
tail - > size = zmalloc_usable ( tail ) - sizeof ( clientReplyBlock ) ;
tail - > used = len ;
memcpy ( tail - > buf , s , len ) ;
listAddNodeTail ( c - > reply , tail ) ;
c - > reply_bytes + = tail - > size ;
2010-08-30 08:44:34 -04:00
}
2012-01-23 10:12:37 -05:00
asyncCloseClientOnOutputBufferLimitReached ( c ) ;
2010-08-30 08:44:34 -04:00
}
2010-06-21 18:07:48 -04:00
2011-03-31 10:44:43 -04:00
/* -----------------------------------------------------------------------------
* Higher level functions to queue data on the client output buffer .
* The following functions are the ones that commands implementations will call .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2018-03-22 06:45:04 -04:00
/* Add the object 'obj' string representation to the client output buffer. */
2015-07-26 09:20:46 -04:00
void addReply ( client * c , robj * obj ) {
2015-07-26 17:17:55 -04:00
if ( prepareClientToWrite ( c ) ! = C_OK ) return ;
2010-09-16 07:08:40 -04:00
2012-06-05 15:50:10 -04:00
if ( sdsEncodedObject ( obj ) ) {
2015-07-26 17:17:55 -04:00
if ( _addReplyToBuffer ( c , obj - > ptr , sdslen ( obj - > ptr ) ) ! = C_OK )
2018-12-17 10:59:19 -05:00
_addReplyProtoToList ( c , obj - > ptr , sdslen ( obj - > ptr ) ) ;
2015-07-26 09:28:00 -04:00
} else if ( obj - > encoding = = OBJ_ENCODING_INT ) {
2018-03-22 06:42:50 -04:00
/* For integer encoded strings we just convert it into a string
* using our optimized function , and attach the resulting string
* to the output buffer . */
char buf [ 32 ] ;
size_t len = ll2string ( buf , sizeof ( buf ) , ( long ) obj - > ptr ) ;
if ( _addReplyToBuffer ( c , buf , len ) ! = C_OK )
2018-12-17 10:59:19 -05:00
_addReplyProtoToList ( c , buf , len ) ;
2012-01-23 11:15:49 -05:00
} else {
2015-07-27 03:41:48 -04:00
serverPanic ( " Wrong obj->encoding in addReply() " ) ;
2010-06-21 18:07:48 -04:00
}
}
2018-03-22 06:45:04 -04:00
/* Add the SDS 's' string to the client output buffer, as a side effect
* the SDS string is freed . */
2015-07-26 09:20:46 -04:00
void addReplySds ( client * c , sds s ) {
2015-07-26 17:17:55 -04:00
if ( prepareClientToWrite ( c ) ! = C_OK ) {
2010-09-02 08:19:15 -04:00
/* The caller expects the sds to be free'd. */
sdsfree ( s ) ;
return ;
}
2018-03-22 06:42:50 -04:00
if ( _addReplyToBuffer ( c , s , sdslen ( s ) ) ! = C_OK )
2018-12-17 10:59:19 -05:00
_addReplyProtoToList ( c , s , sdslen ( s ) ) ;
2018-03-22 06:42:50 -04:00
sdsfree ( s ) ;
2010-06-21 18:07:48 -04:00
}
PSYNC2: different improvements to Redis replication.
The gist of the changes is that now, partial resynchronizations between
slaves and masters (without the need of a full resync with RDB transfer
and so forth), work in a number of cases when it was impossible
in the past. For instance:
1. When a slave is promoted to mastrer, the slaves of the old master can
partially resynchronize with the new master.
2. Chained slalves (slaves of slaves) can be moved to replicate to other
slaves or the master itsef, without requiring a full resync.
3. The master itself, after being turned into a slave, is able to
partially resynchronize with the new master, when it joins replication
again.
In order to obtain this, the following main changes were operated:
* Slaves also take a replication backlog, not just masters.
* Same stream replication for all the slaves and sub slaves. The
replication stream is identical from the top level master to its slaves
and is also the same from the slaves to their sub-slaves and so forth.
This means that if a slave is later promoted to master, it has the
same replication backlong, and can partially resynchronize with its
slaves (that were previously slaves of the old master).
* A given replication history is no longer identified by the `runid` of
a Redis node. There is instead a `replication ID` which changes every
time the instance has a new history no longer coherent with the past
one. So, for example, slaves publish the same replication history of
their master, however when they are turned into masters, they publish
a new replication ID, but still remember the old ID, so that they are
able to partially resynchronize with slaves of the old master (up to a
given offset).
* The replication protocol was slightly modified so that a new extended
+CONTINUE reply from the master is able to inform the slave of a
replication ID change.
* REPLCONF CAPA is used in order to notify masters that a slave is able
to understand the new +CONTINUE reply.
* The RDB file was extended with an auxiliary field that is able to
select a given DB after loading in the slave, so that the slave can
continue receiving the replication stream from the point it was
disconnected without requiring the master to insert "SELECT" statements.
This is useful in order to guarantee the "same stream" property, because
the slave must be able to accumulate an identical backlog.
* Slave pings to sub-slaves are now sent in a special form, when the
top-level master is disconnected, in order to don't interfer with the
replication stream. We just use out of band "\n" bytes as in other parts
of the Redis protocol.
An old design document is available here:
https://gist.github.com/antirez/ae068f95c0d084891305
However the implementation is not identical to the description because
during the work to implement it, different changes were needed in order
to make things working well.
2016-11-09 05:31:06 -05:00
/* This low level function just adds whatever protocol you send it to the
* client buffer , trying the static buffer initially , and using the string
* of objects if not possible .
*
* It is efficient because does not create an SDS object nor an Redis object
* if not needed . The object will only be created by calling
2018-12-17 10:59:19 -05:00
* _addReplyProtoToList ( ) if we fail to extend the existing tail object
PSYNC2: different improvements to Redis replication.
The gist of the changes is that now, partial resynchronizations between
slaves and masters (without the need of a full resync with RDB transfer
and so forth), work in a number of cases when it was impossible
in the past. For instance:
1. When a slave is promoted to mastrer, the slaves of the old master can
partially resynchronize with the new master.
2. Chained slalves (slaves of slaves) can be moved to replicate to other
slaves or the master itsef, without requiring a full resync.
3. The master itself, after being turned into a slave, is able to
partially resynchronize with the new master, when it joins replication
again.
In order to obtain this, the following main changes were operated:
* Slaves also take a replication backlog, not just masters.
* Same stream replication for all the slaves and sub slaves. The
replication stream is identical from the top level master to its slaves
and is also the same from the slaves to their sub-slaves and so forth.
This means that if a slave is later promoted to master, it has the
same replication backlong, and can partially resynchronize with its
slaves (that were previously slaves of the old master).
* A given replication history is no longer identified by the `runid` of
a Redis node. There is instead a `replication ID` which changes every
time the instance has a new history no longer coherent with the past
one. So, for example, slaves publish the same replication history of
their master, however when they are turned into masters, they publish
a new replication ID, but still remember the old ID, so that they are
able to partially resynchronize with slaves of the old master (up to a
given offset).
* The replication protocol was slightly modified so that a new extended
+CONTINUE reply from the master is able to inform the slave of a
replication ID change.
* REPLCONF CAPA is used in order to notify masters that a slave is able
to understand the new +CONTINUE reply.
* The RDB file was extended with an auxiliary field that is able to
select a given DB after loading in the slave, so that the slave can
continue receiving the replication stream from the point it was
disconnected without requiring the master to insert "SELECT" statements.
This is useful in order to guarantee the "same stream" property, because
the slave must be able to accumulate an identical backlog.
* Slave pings to sub-slaves are now sent in a special form, when the
top-level master is disconnected, in order to don't interfer with the
replication stream. We just use out of band "\n" bytes as in other parts
of the Redis protocol.
An old design document is available here:
https://gist.github.com/antirez/ae068f95c0d084891305
However the implementation is not identical to the description because
during the work to implement it, different changes were needed in order
to make things working well.
2016-11-09 05:31:06 -05:00
* in the list of objects . */
2018-12-17 10:59:19 -05:00
void addReplyProto ( client * c , const char * s , size_t len ) {
2015-07-26 17:17:55 -04:00
if ( prepareClientToWrite ( c ) ! = C_OK ) return ;
if ( _addReplyToBuffer ( c , s , len ) ! = C_OK )
2018-12-17 10:59:19 -05:00
_addReplyProtoToList ( c , s , len ) ;
2010-08-30 08:44:34 -04:00
}
2010-06-21 18:07:48 -04:00
2018-02-16 10:18:01 -05:00
/* Low level function called by the addReplyError...() functions.
* It emits the protocol for a Redis error , in the form :
*
* - ERRORCODE Error Message < CR > < LF >
*
* If the error code is already passed in the string ' s ' , the error
* code provided is used , otherwise the string " -ERR " for the generic
* error code is automatically added . */
2015-07-26 09:20:46 -04:00
void addReplyErrorLength ( client * c , const char * s , size_t len ) {
2018-02-16 10:18:01 -05:00
/* If the string already starts with "-..." then the error code
* is provided by the caller . Otherwise we use " -ERR " . */
2018-12-17 10:59:19 -05:00
if ( ! len | | s [ 0 ] ! = ' - ' ) addReplyProto ( c , " -ERR " , 5 ) ;
addReplyProto ( c , s , len ) ;
addReplyProto ( c , " \r \n " , 2 ) ;
2018-07-18 11:40:07 -04:00
/* Sometimes it could be normal that a slave replies to a master with
* an error and this function gets called . Actually the error will never
* be sent because addReply * ( ) against master clients has no effect . . .
* A notable example is :
*
* EVAL ' redis . call ( " incr " , KEYS [ 1 ] ) ; redis . call ( " nonexisting " ) ' 1 x
*
* Where the master must propagate the first change even if the second
* will produce an error . However it is useful to log such events since
* they are rare and may hint at errors in a script or a bug in Redis . */
2018-12-04 02:17:17 -05:00
if ( c - > flags & ( CLIENT_MASTER | CLIENT_SLAVE ) & & ! ( c - > flags & CLIENT_MONITOR ) ) {
2018-09-10 11:02:44 -04:00
char * to = c - > flags & CLIENT_MASTER ? " master " : " replica " ;
char * from = c - > flags & CLIENT_MASTER ? " replica " : " master " ;
2018-02-13 10:01:31 -05:00
char * cmdname = c - > lastcmd ? c - > lastcmd - > name : " <unknown> " ;
fix rare replication stream corruption with disk-based replication
The slave sends \n keepalive messages to the master while parsing the rdb,
and later sends REPLCONF ACK once a second. rarely, the master recives both
a linefeed char and a REPLCONF in the same read, \n*3\r\n$8\r\nREPLCONF\r\n...
and it tries to trim two chars (\r\n) from the query buffer,
trimming the '*' from *3\r\n$8\r\nREPLCONF\r\n...
then the master tries to process a command starting with '3' and replies to
the slave a bunch of -ERR and one +OK.
although the slave silently ignores these (prints a log message), this corrupts
the replication offset at the slave since the slave increases the replication
offset, and the master did not.
other than the fix in processInlineBuffer, i did several other improvments
while hunting this very rare bug.
- when redis replies with "unknown command" it includes a portion of the
arguments, not just the command name. so it would be easier to understand
what was recived, in my case, on the slave side, it was -ERR, but
the "arguments" were the interesting part (containing info on the error).
- about a year ago i added code in addReplyErrorLength to print the error to
the log in case of a reply to master (since this string isn't actually
trasmitted to the master), now changed that block to print a similar log
message to indicate an error being sent from the master to the slave.
note that the slave is marked as CLIENT_SLAVE only after PSYNC was received,
so this will not cause any harm for REPLCONF, and will only indicate problems
that are gonna corrupt the replication stream anyway.
- two places were c->reply was emptied, and i wanted to reset sentlen
this is a precaution (i did not actually see such a problem), since a
non-zero sentlen will cause corruption to be transmitted on the socket.
2018-07-14 09:49:15 -04:00
serverLog ( LL_WARNING , " == CRITICAL == This %s is sending an error "
" to its %s: '%s' after processing the command "
" '%s' " , from , to , s , cmdname ) ;
2018-02-13 10:01:31 -05:00
}
2010-06-21 18:07:48 -04:00
}
2015-07-26 09:20:46 -04:00
void addReplyError ( client * c , const char * err ) {
2012-01-23 11:15:49 -05:00
addReplyErrorLength ( c , err , strlen ( err ) ) ;
2010-09-02 13:52:24 -04:00
}
2010-06-21 18:07:48 -04:00
2015-07-26 09:20:46 -04:00
void addReplyErrorFormat ( client * c , const char * fmt , . . . ) {
2011-05-24 13:43:11 -04:00
size_t l , j ;
2010-09-02 13:52:24 -04:00
va_list ap ;
va_start ( ap , fmt ) ;
sds s = sdscatvprintf ( sdsempty ( ) , fmt , ap ) ;
va_end ( ap ) ;
2011-05-24 13:43:11 -04:00
/* Make sure there are no newlines in the string, otherwise invalid protocol
* is emitted . */
l = sdslen ( s ) ;
for ( j = 0 ; j < l ; j + + ) {
if ( s [ j ] = = ' \r ' | | s [ j ] = = ' \n ' ) s [ j ] = ' ' ;
}
2012-01-23 11:15:49 -05:00
addReplyErrorLength ( c , s , sdslen ( s ) ) ;
2010-09-02 13:52:24 -04:00
sdsfree ( s ) ;
}
2015-07-26 09:20:46 -04:00
void addReplyStatusLength ( client * c , const char * s , size_t len ) {
2018-12-17 10:59:19 -05:00
addReplyProto ( c , " + " , 1 ) ;
addReplyProto ( c , s , len ) ;
addReplyProto ( c , " \r \n " , 2 ) ;
2010-09-02 13:52:24 -04:00
}
2015-07-26 09:20:46 -04:00
void addReplyStatus ( client * c , const char * status ) {
2012-01-23 11:15:49 -05:00
addReplyStatusLength ( c , status , strlen ( status ) ) ;
2010-09-02 13:52:24 -04:00
}
2015-07-26 09:20:46 -04:00
void addReplyStatusFormat ( client * c , const char * fmt , . . . ) {
2010-09-02 13:52:24 -04:00
va_list ap ;
va_start ( ap , fmt ) ;
sds s = sdscatvprintf ( sdsempty ( ) , fmt , ap ) ;
va_end ( ap ) ;
2012-01-23 11:15:49 -05:00
addReplyStatusLength ( c , s , sdslen ( s ) ) ;
2010-09-02 13:52:24 -04:00
sdsfree ( s ) ;
}
2010-08-30 10:02:06 -04:00
/* Adds an empty object to the reply list that will contain the multi bulk
* length , which is not known when this function is called . */
2018-11-08 06:28:56 -05:00
void * addReplyDeferredLen ( client * c ) {
2010-09-16 07:08:40 -04:00
/* Note that we install the write event here even if the object is not
* ready to be sent , since we are sure that before returning to the
2018-11-08 06:28:56 -05:00
* event loop setDeferredAggregateLen ( ) will be called . */
2015-07-26 17:17:55 -04:00
if ( prepareClientToWrite ( c ) ! = C_OK ) return NULL ;
2015-07-31 08:59:54 -04:00
listAddNodeTail ( c - > reply , NULL ) ; /* NULL is our placeholder. */
2010-08-30 10:02:06 -04:00
return listLast ( c - > reply ) ;
}
2013-01-16 12:00:20 -05:00
/* Populate the length object and try gluing it to the next chunk. */
2018-11-08 06:28:56 -05:00
void setDeferredAggregateLen ( client * c , void * node , long length , char prefix ) {
2010-08-30 10:02:06 -04:00
listNode * ln = ( listNode * ) node ;
2018-02-21 13:18:34 -05:00
clientReplyBlock * next ;
char lenstr [ 128 ] ;
2018-11-08 06:28:56 -05:00
size_t lenstr_len = sprintf ( lenstr , " %c%ld \r \n " , prefix , length ) ;
2010-08-30 10:02:06 -04:00
2015-07-31 08:59:54 -04:00
/* Abort when *node is NULL: when the client should not accept writes
2018-11-08 06:28:56 -05:00
* we return NULL in addReplyDeferredLen ( ) */
2010-08-30 10:02:06 -04:00
if ( node = = NULL ) return ;
2018-02-21 13:18:34 -05:00
serverAssert ( ! listNodeValue ( ln ) ) ;
2018-11-08 06:28:56 -05:00
/* Normally we fill this dummy NULL node, added by addReplyDeferredLen(),
2018-07-16 11:56:54 -04:00
* with a new buffer structure containing the protocol needed to specify
* the length of the array following . However sometimes when there is
* little memory to move , we may instead remove this NULL node , and prefix
* our protocol in the node immediately after to it , in order to save a
* write ( 2 ) syscall later . Conditions needed to do it :
*
* - The next node is non - NULL ,
* - It has enough room already allocated
* - And not too large ( avoid large memmove ) */
if ( ln - > next ! = NULL & & ( next = listNodeValue ( ln - > next ) ) & &
2018-02-21 13:18:34 -05:00
next - > size - next - > used > = lenstr_len & &
next - > used < PROTO_REPLY_CHUNK_BYTES * 4 ) {
memmove ( next - > buf + lenstr_len , next - > buf , next - > used ) ;
memcpy ( next - > buf , lenstr , lenstr_len ) ;
next - > used + = lenstr_len ;
listDelNode ( c - > reply , ln ) ;
} else {
/* Create a new node */
clientReplyBlock * buf = zmalloc ( lenstr_len + sizeof ( clientReplyBlock ) ) ;
2018-07-16 11:56:54 -04:00
/* Take over the allocation's internal fragmentation */
2018-02-21 13:18:34 -05:00
buf - > size = zmalloc_usable ( buf ) - sizeof ( clientReplyBlock ) ;
buf - > used = lenstr_len ;
memcpy ( buf - > buf , lenstr , lenstr_len ) ;
listNodeValue ( ln ) = buf ;
c - > reply_bytes + = buf - > size ;
2010-06-21 18:07:48 -04:00
}
2012-01-23 10:12:37 -05:00
asyncCloseClientOnOutputBufferLimitReached ( c ) ;
2010-08-30 10:02:06 -04:00
}
2018-11-08 06:28:56 -05:00
void setDeferredArrayLen ( client * c , void * node , long length ) {
setDeferredAggregateLen ( c , node , length , ' * ' ) ;
}
void setDeferredMapLen ( client * c , void * node , long length ) {
2018-11-21 10:20:17 -05:00
int prefix = c - > resp = = 2 ? ' * ' : ' % ' ;
if ( c - > resp = = 2 ) length * = 2 ;
setDeferredAggregateLen ( c , node , length , prefix ) ;
2018-11-08 06:28:56 -05:00
}
void setDeferredSetLen ( client * c , void * node , long length ) {
2018-11-21 10:20:17 -05:00
int prefix = c - > resp = = 2 ? ' * ' : ' ~ ' ;
setDeferredAggregateLen ( c , node , length , prefix ) ;
2018-11-08 06:28:56 -05:00
}
void setDeferredAttributeLen ( client * c , void * node , long length ) {
2018-11-21 10:20:17 -05:00
int prefix = c - > resp = = 2 ? ' * ' : ' | ' ;
if ( c - > resp = = 2 ) length * = 2 ;
setDeferredAggregateLen ( c , node , length , prefix ) ;
2018-11-08 06:28:56 -05:00
}
void setDeferredPushLen ( client * c , void * node , long length ) {
2018-11-21 10:20:17 -05:00
int prefix = c - > resp = = 2 ? ' * ' : ' > ' ;
setDeferredAggregateLen ( c , node , length , prefix ) ;
2018-11-08 06:28:56 -05:00
}
2013-01-16 12:00:20 -05:00
/* Add a double as a bulk reply */
2015-07-26 09:20:46 -04:00
void addReplyDouble ( client * c , double d ) {
2013-07-17 09:04:22 -04:00
if ( isinf ( d ) ) {
/* Libc in odd systems (Hi Solaris!) will format infinite in a
* different way , so better to handle it in an explicit way . */
2018-11-21 10:20:17 -05:00
if ( c - > resp = = 2 ) {
addReplyBulkCString ( c , d > 0 ? " inf " : " -inf " ) ;
} else {
2019-07-02 09:28:48 -04:00
addReplyProto ( c , d > 0 ? " ,inf \r \n " : " ,-inf \r \n " ,
2018-11-21 10:20:17 -05:00
d > 0 ? 6 : 7 ) ;
}
2013-07-17 09:04:22 -04:00
} else {
2018-11-21 10:20:17 -05:00
char dbuf [ MAX_LONG_DOUBLE_CHARS + 3 ] ,
sbuf [ MAX_LONG_DOUBLE_CHARS + 32 ] ;
int dlen , slen ;
if ( c - > resp = = 2 ) {
dlen = snprintf ( dbuf , sizeof ( dbuf ) , " %.17g " , d ) ;
slen = snprintf ( sbuf , sizeof ( sbuf ) , " $%d \r \n %s \r \n " , dlen , dbuf ) ;
2018-12-17 10:59:19 -05:00
addReplyProto ( c , sbuf , slen ) ;
2018-11-21 10:20:17 -05:00
} else {
dlen = snprintf ( dbuf , sizeof ( dbuf ) , " ,%.17g \r \n " , d ) ;
2018-12-17 10:59:19 -05:00
addReplyProto ( c , dbuf , dlen ) ;
2018-11-21 10:20:17 -05:00
}
2013-07-17 09:04:22 -04:00
}
2010-06-21 18:07:48 -04:00
}
2016-02-18 16:08:47 -05:00
/* Add a long double as a bulk reply, but uses a human readable formatting
* of the double instead of exposing the crude behavior of doubles to the
* dear user . */
void addReplyHumanLongDouble ( client * c , long double d ) {
2018-11-21 10:20:17 -05:00
if ( c - > resp = = 2 ) {
robj * o = createStringObjectFromLongDouble ( d , 1 ) ;
addReplyBulk ( c , o ) ;
decrRefCount ( o ) ;
} else {
char buf [ MAX_LONG_DOUBLE_CHARS ] ;
2019-11-03 09:42:31 -05:00
int len = ld2string ( buf , sizeof ( buf ) , d , LD_STR_HUMAN ) ;
2018-12-17 10:59:19 -05:00
addReplyProto ( c , " , " , 1 ) ;
addReplyProto ( c , buf , len ) ;
addReplyProto ( c , " \r \n " , 2 ) ;
2018-11-21 10:20:17 -05:00
}
2016-02-18 16:08:47 -05:00
}
2010-12-07 10:33:13 -05:00
/* Add a long long as integer reply or bulk len / multi bulk count.
* Basically this is used to output < prefix > < long long > < crlf > . */
2015-07-26 09:20:46 -04:00
void addReplyLongLongWithPrefix ( client * c , long long ll , char prefix ) {
2010-06-21 18:07:48 -04:00
char buf [ 128 ] ;
2010-08-30 08:44:34 -04:00
int len ;
2012-02-04 02:58:37 -05:00
/* Things like $3\r\n or *2\r\n are emitted very often by the protocol
* so we have a few shared objects to use if the integer is small
* like it is most of the times . */
2015-07-27 03:41:48 -04:00
if ( prefix = = ' * ' & & ll < OBJ_SHARED_BULKHDR_LEN & & ll > = 0 ) {
2012-02-04 02:58:37 -05:00
addReply ( c , shared . mbulkhdr [ ll ] ) ;
return ;
2015-07-27 03:41:48 -04:00
} else if ( prefix = = ' $ ' & & ll < OBJ_SHARED_BULKHDR_LEN & & ll > = 0 ) {
2012-02-04 02:58:37 -05:00
addReply ( c , shared . bulkhdr [ ll ] ) ;
return ;
}
2010-08-30 08:44:34 -04:00
buf [ 0 ] = prefix ;
2010-06-21 18:07:48 -04:00
len = ll2string ( buf + 1 , sizeof ( buf ) - 1 , ll ) ;
buf [ len + 1 ] = ' \r ' ;
buf [ len + 2 ] = ' \n ' ;
2018-12-17 10:59:19 -05:00
addReplyProto ( c , buf , len + 3 ) ;
2010-06-21 18:07:48 -04:00
}
2015-07-26 09:20:46 -04:00
void addReplyLongLong ( client * c , long long ll ) {
2011-04-15 12:08:24 -04:00
if ( ll = = 0 )
addReply ( c , shared . czero ) ;
else if ( ll = = 1 )
addReply ( c , shared . cone ) ;
else
2012-01-23 11:15:49 -05:00
addReplyLongLongWithPrefix ( c , ll , ' : ' ) ;
2010-08-30 08:44:34 -04:00
}
2010-06-21 18:07:48 -04:00
2018-11-07 11:40:35 -05:00
void addReplyAggregateLen ( client * c , long length , int prefix ) {
if ( prefix = = ' * ' & & length < OBJ_SHARED_BULKHDR_LEN )
2013-08-12 06:43:26 -04:00
addReply ( c , shared . mbulkhdr [ length ] ) ;
else
2018-11-07 11:40:35 -05:00
addReplyLongLongWithPrefix ( c , length , prefix ) ;
}
void addReplyArrayLen ( client * c , long length ) {
addReplyAggregateLen ( c , length , ' * ' ) ;
}
void addReplyMapLen ( client * c , long length ) {
2018-11-21 06:49:39 -05:00
int prefix = c - > resp = = 2 ? ' * ' : ' % ' ;
2018-11-21 10:20:17 -05:00
if ( c - > resp = = 2 ) length * = 2 ;
2018-11-21 06:49:39 -05:00
addReplyAggregateLen ( c , length , prefix ) ;
2018-11-07 11:40:35 -05:00
}
void addReplySetLen ( client * c , long length ) {
2018-11-21 06:49:39 -05:00
int prefix = c - > resp = = 2 ? ' * ' : ' ~ ' ;
addReplyAggregateLen ( c , length , prefix ) ;
2018-11-07 11:40:35 -05:00
}
void addReplyAttributeLen ( client * c , long length ) {
2018-11-21 06:49:39 -05:00
int prefix = c - > resp = = 2 ? ' * ' : ' | ' ;
2018-11-21 10:20:17 -05:00
if ( c - > resp = = 2 ) length * = 2 ;
2018-11-21 06:49:39 -05:00
addReplyAggregateLen ( c , length , prefix ) ;
2018-11-07 11:40:35 -05:00
}
void addReplyPushLen ( client * c , long length ) {
2018-11-21 06:49:39 -05:00
int prefix = c - > resp = = 2 ? ' * ' : ' > ' ;
addReplyAggregateLen ( c , length , prefix ) ;
2010-06-21 18:07:48 -04:00
}
2018-11-27 05:58:55 -05:00
void addReplyNull ( client * c ) {
if ( c - > resp = = 2 ) {
2018-12-17 10:59:19 -05:00
addReplyProto ( c , " $-1 \r \n " , 5 ) ;
2018-11-27 05:58:55 -05:00
} else {
2018-12-17 10:59:19 -05:00
addReplyProto ( c , " _ \r \n " , 3 ) ;
2018-11-27 05:58:55 -05:00
}
}
2018-12-04 12:00:35 -05:00
void addReplyBool ( client * c , int b ) {
2018-12-10 06:35:28 -05:00
if ( c - > resp = = 2 ) {
2018-12-04 12:00:35 -05:00
addReply ( c , b ? shared . cone : shared . czero ) ;
} else {
2018-12-17 10:59:19 -05:00
addReplyProto ( c , b ? " #t \r \n " : " #f \r \n " , 4 ) ;
2018-12-04 12:00:35 -05:00
}
}
2018-11-30 10:31:02 -05:00
/* A null array is a concept that no longer exists in RESP3. However
* RESP2 had it , so API - wise we have this call , that will emit the correct
* RESP2 protocol , however for RESP3 the reply will always be just the
* Null type " _ \r \n " . */
void addReplyNullArray ( client * c ) {
if ( c - > resp = = 2 ) {
2018-12-17 10:59:19 -05:00
addReplyProto ( c , " *-1 \r \n " , 5 ) ;
2018-11-30 10:31:02 -05:00
} else {
2018-12-17 10:59:19 -05:00
addReplyProto ( c , " _ \r \n " , 3 ) ;
2018-11-30 10:31:02 -05:00
}
}
2010-12-07 10:33:13 -05:00
/* Create the length prefix of a bulk reply, example: $2234 */
2015-07-26 09:20:46 -04:00
void addReplyBulkLen ( client * c , robj * obj ) {
2019-02-19 22:52:57 -05:00
size_t len = stringObjectLen ( obj ) ;
2013-08-12 06:43:26 -04:00
2015-07-27 03:41:48 -04:00
if ( len < OBJ_SHARED_BULKHDR_LEN )
2013-08-12 06:43:26 -04:00
addReply ( c , shared . bulkhdr [ len ] ) ;
else
addReplyLongLongWithPrefix ( c , len , ' $ ' ) ;
2010-06-21 18:07:48 -04:00
}
2010-12-07 10:33:13 -05:00
/* Add a Redis Object as a bulk reply */
2015-07-26 09:20:46 -04:00
void addReplyBulk ( client * c , robj * obj ) {
2010-06-21 18:07:48 -04:00
addReplyBulkLen ( c , obj ) ;
addReply ( c , obj ) ;
addReply ( c , shared . crlf ) ;
}
2010-12-07 10:33:13 -05:00
/* Add a C buffer as bulk reply */
2015-07-26 09:20:46 -04:00
void addReplyBulkCBuffer ( client * c , const void * p , size_t len ) {
2012-01-23 11:15:49 -05:00
addReplyLongLongWithPrefix ( c , len , ' $ ' ) ;
2018-12-17 10:59:19 -05:00
addReplyProto ( c , p , len ) ;
2010-12-07 10:33:13 -05:00
addReply ( c , shared . crlf ) ;
}
2014-11-16 13:03:54 -05:00
/* Add sds to reply (takes ownership of sds and frees it) */
2015-07-26 09:20:46 -04:00
void addReplyBulkSds ( client * c , sds s ) {
2017-07-05 10:25:05 -04:00
addReplyLongLongWithPrefix ( c , sdslen ( s ) , ' $ ' ) ;
2014-11-16 13:03:54 -05:00
addReplySds ( c , s ) ;
addReply ( c , shared . crlf ) ;
}
2017-12-06 06:05:11 -05:00
/* Add a C null term string as bulk reply */
2015-07-26 09:20:46 -04:00
void addReplyBulkCString ( client * c , const char * s ) {
2010-06-21 18:07:48 -04:00
if ( s = = NULL ) {
2018-11-30 03:41:54 -05:00
addReplyNull ( c ) ;
2010-06-21 18:07:48 -04:00
} else {
2010-12-07 10:33:13 -05:00
addReplyBulkCBuffer ( c , s , strlen ( s ) ) ;
2010-06-21 18:07:48 -04:00
}
}
2010-12-07 10:33:13 -05:00
/* Add a long long as a bulk reply */
2015-07-26 09:20:46 -04:00
void addReplyBulkLongLong ( client * c , long long ll ) {
2010-12-07 10:33:13 -05:00
char buf [ 64 ] ;
int len ;
len = ll2string ( buf , 64 , ll ) ;
addReplyBulkCBuffer ( c , buf , len ) ;
}
2018-12-10 10:55:20 -05:00
/* Reply with a verbatim type having the specified extension.
*
* The ' ext ' is the " extension " of the file , actually just a three
* character type that describes the format of the verbatim string .
* For instance " txt " means it should be interpreted as a text only
* file by the receiver , " md " as markdown , and so forth . Only the
* three first characters of the extension are used , and if the
* provided one is shorter than that , the remaining is filled with
* spaces . */
void addReplyVerbatim ( client * c , const char * s , size_t len , const char * ext ) {
if ( c - > resp = = 2 ) {
addReplyBulkCBuffer ( c , s , len ) ;
} else {
char buf [ 32 ] ;
size_t preflen = snprintf ( buf , sizeof ( buf ) , " =%zu \r \n xxx: " , len + 4 ) ;
char * p = buf + preflen - 4 ;
for ( int i = 0 ; i < 3 ; i + + ) {
if ( * ext = = ' \0 ' ) {
p [ i ] = ' ' ;
} else {
p [ i ] = * ext + + ;
}
}
2018-12-17 10:59:19 -05:00
addReplyProto ( c , buf , preflen ) ;
addReplyProto ( c , s , len ) ;
addReplyProto ( c , " \r \n " , 2 ) ;
2018-12-10 10:55:20 -05:00
}
}
2017-12-06 06:05:11 -05:00
/* Add an array of C strings as status replies with a heading.
2017-11-27 10:57:44 -05:00
* This function is typically invoked by from commands that support
* subcommands in response to the ' help ' subcommand . The help array
* is terminated by NULL sentinel . */
void addReplyHelp ( client * c , const char * * help ) {
sds cmd = sdsnew ( ( char * ) c - > argv [ 0 ] - > ptr ) ;
2018-11-08 06:28:56 -05:00
void * blenp = addReplyDeferredLen ( c ) ;
2017-11-27 10:57:44 -05:00
int blen = 0 ;
sdstoupper ( cmd ) ;
addReplyStatusFormat ( c ,
" %s <subcommand> arg arg ... arg. Subcommands are: " , cmd ) ;
sdsfree ( cmd ) ;
2017-12-06 06:05:11 -05:00
2017-12-05 12:09:19 -05:00
while ( help [ blen ] ) addReplyStatus ( c , help [ blen + + ] ) ;
2017-11-27 10:57:44 -05:00
2017-12-05 12:09:19 -05:00
blen + + ; /* Account for the header line(s). */
2018-11-08 06:28:56 -05:00
setDeferredArrayLen ( c , blenp , blen ) ;
2017-11-27 10:57:44 -05:00
}
2018-06-07 11:34:58 -04:00
/* Add a suggestive error reply.
* This function is typically invoked by from commands that support
* subcommands in response to an unknown subcommand or argument error . */
2018-07-02 12:49:34 -04:00
void addReplySubcommandSyntaxError ( client * c ) {
2018-06-07 11:34:58 -04:00
sds cmd = sdsnew ( ( char * ) c - > argv [ 0 ] - > ptr ) ;
sdstoupper ( cmd ) ;
addReplyErrorFormat ( c ,
" Unknown subcommand or wrong number of arguments for '%s'. Try %s HELP. " ,
2018-07-06 20:44:41 -04:00
( char * ) c - > argv [ 1 ] - > ptr , cmd ) ;
2018-07-02 12:49:34 -04:00
sdsfree ( cmd ) ;
2018-06-07 11:34:58 -04:00
}
2019-03-24 07:10:55 -04:00
/* Append 'src' client output buffers into 'dst' client output buffers.
* This function clears the output buffers of ' src ' */
void AddReplyFromClient ( client * dst , client * src ) {
if ( prepareClientToWrite ( dst ) ! = C_OK )
return ;
addReplyProto ( dst , src - > buf , src - > bufpos ) ;
if ( listLength ( src - > reply ) )
listJoin ( dst - > reply , src - > reply ) ;
dst - > reply_bytes + = src - > reply_bytes ;
src - > reply_bytes = 0 ;
src - > bufpos = 0 ;
}
2011-12-30 13:34:40 -05:00
/* Copy 'src' client output buffers into 'dst' client output buffers.
* The function takes care of freeing the old output buffers of the
* destination client . */
2015-07-26 09:20:46 -04:00
void copyClientOutputBuffer ( client * dst , client * src ) {
2011-12-30 13:34:40 -05:00
listRelease ( dst - > reply ) ;
fix rare replication stream corruption with disk-based replication
The slave sends \n keepalive messages to the master while parsing the rdb,
and later sends REPLCONF ACK once a second. rarely, the master recives both
a linefeed char and a REPLCONF in the same read, \n*3\r\n$8\r\nREPLCONF\r\n...
and it tries to trim two chars (\r\n) from the query buffer,
trimming the '*' from *3\r\n$8\r\nREPLCONF\r\n...
then the master tries to process a command starting with '3' and replies to
the slave a bunch of -ERR and one +OK.
although the slave silently ignores these (prints a log message), this corrupts
the replication offset at the slave since the slave increases the replication
offset, and the master did not.
other than the fix in processInlineBuffer, i did several other improvments
while hunting this very rare bug.
- when redis replies with "unknown command" it includes a portion of the
arguments, not just the command name. so it would be easier to understand
what was recived, in my case, on the slave side, it was -ERR, but
the "arguments" were the interesting part (containing info on the error).
- about a year ago i added code in addReplyErrorLength to print the error to
the log in case of a reply to master (since this string isn't actually
trasmitted to the master), now changed that block to print a similar log
message to indicate an error being sent from the master to the slave.
note that the slave is marked as CLIENT_SLAVE only after PSYNC was received,
so this will not cause any harm for REPLCONF, and will only indicate problems
that are gonna corrupt the replication stream anyway.
- two places were c->reply was emptied, and i wanted to reset sentlen
this is a precaution (i did not actually see such a problem), since a
non-zero sentlen will cause corruption to be transmitted on the socket.
2018-07-14 09:49:15 -04:00
dst - > sentlen = 0 ;
2011-12-30 13:34:40 -05:00
dst - > reply = listDup ( src - > reply ) ;
memcpy ( dst - > buf , src - > buf , src - > bufpos ) ;
dst - > bufpos = src - > bufpos ;
2011-12-25 10:32:54 -05:00
dst - > reply_bytes = src - > reply_bytes ;
2011-12-30 13:34:40 -05:00
}
2015-09-30 10:41:48 -04:00
/* Return true if the specified client has pending reply buffers to write to
* the socket . */
int clientHasPendingReplies ( client * c ) {
return c - > bufpos | | listLength ( c - > reply ) ;
}
2019-09-12 03:56:54 -04:00
void clientAcceptHandler ( connection * conn ) {
client * c = connGetPrivateData ( conn ) ;
2010-06-21 18:07:48 -04:00
2019-09-12 03:56:54 -04:00
if ( connGetState ( conn ) ! = CONN_STATE_CONNECTED ) {
serverLog ( LL_WARNING ,
" Error accepting a client connection: %s " ,
connGetLastError ( conn ) ) ;
2010-06-21 18:07:48 -04:00
freeClient ( c ) ;
return ;
}
New security feature: Redis protected mode.
An exposed Redis instance on the internet can be cause of serious
issues. Since Redis, by default, binds to all the interfaces, it is easy
to forget an instance without any protection layer, for error.
Protected mode try to address this feature in a soft way, providing a
layer of protection, but giving clues to Redis users about why the
server is not accepting connections.
When protected mode is enabeld (the default), and if there are no
minumum hints about the fact the server is properly configured (no
"bind" directive is used in order to restrict the server to certain
interfaces, nor a password is set), clients connecting from external
intefaces are refused with an error explaining what to do in order to
fix the issue.
Clients connecting from the IPv4 and IPv6 lookback interfaces are still
accepted normally, similarly Unix domain socket connections are not
restricted in any way.
2016-01-07 07:00:08 -05:00
/* If the server is running in protected mode (the default) and there
* is no password set , nor a specific interface is bound , we don ' t accept
* requests from non loopback interfaces . Instead we try to explain the
* user what to do to fix it if needed . */
if ( server . protected_mode & &
server . bindaddr_count = = 0 & &
2019-01-18 05:49:30 -05:00
DefaultUser - > flags & USER_FLAG_NOPASS & &
2019-09-12 03:56:54 -04:00
! ( c - > flags & CLIENT_UNIX_SOCKET ) )
New security feature: Redis protected mode.
An exposed Redis instance on the internet can be cause of serious
issues. Since Redis, by default, binds to all the interfaces, it is easy
to forget an instance without any protection layer, for error.
Protected mode try to address this feature in a soft way, providing a
layer of protection, but giving clues to Redis users about why the
server is not accepting connections.
When protected mode is enabeld (the default), and if there are no
minumum hints about the fact the server is properly configured (no
"bind" directive is used in order to restrict the server to certain
interfaces, nor a password is set), clients connecting from external
intefaces are refused with an error explaining what to do in order to
fix the issue.
Clients connecting from the IPv4 and IPv6 lookback interfaces are still
accepted normally, similarly Unix domain socket connections are not
restricted in any way.
2016-01-07 07:00:08 -05:00
{
2019-09-12 03:56:54 -04:00
char cip [ NET_IP_STR_LEN + 1 ] = { 0 } ;
connPeerToString ( conn , cip , sizeof ( cip ) - 1 , NULL ) ;
if ( strcmp ( cip , " 127.0.0.1 " ) & & strcmp ( cip , " ::1 " ) ) {
New security feature: Redis protected mode.
An exposed Redis instance on the internet can be cause of serious
issues. Since Redis, by default, binds to all the interfaces, it is easy
to forget an instance without any protection layer, for error.
Protected mode try to address this feature in a soft way, providing a
layer of protection, but giving clues to Redis users about why the
server is not accepting connections.
When protected mode is enabeld (the default), and if there are no
minumum hints about the fact the server is properly configured (no
"bind" directive is used in order to restrict the server to certain
interfaces, nor a password is set), clients connecting from external
intefaces are refused with an error explaining what to do in order to
fix the issue.
Clients connecting from the IPv4 and IPv6 lookback interfaces are still
accepted normally, similarly Unix domain socket connections are not
restricted in any way.
2016-01-07 07:00:08 -05:00
char * err =
" -DENIED Redis is running in protected mode because protected "
" mode is enabled, no bind address was specified, no "
" authentication password is requested to clients. In this mode "
2016-01-07 08:35:07 -05:00
" connections are only accepted from the loopback interface. "
New security feature: Redis protected mode.
An exposed Redis instance on the internet can be cause of serious
issues. Since Redis, by default, binds to all the interfaces, it is easy
to forget an instance without any protection layer, for error.
Protected mode try to address this feature in a soft way, providing a
layer of protection, but giving clues to Redis users about why the
server is not accepting connections.
When protected mode is enabeld (the default), and if there are no
minumum hints about the fact the server is properly configured (no
"bind" directive is used in order to restrict the server to certain
interfaces, nor a password is set), clients connecting from external
intefaces are refused with an error explaining what to do in order to
fix the issue.
Clients connecting from the IPv4 and IPv6 lookback interfaces are still
accepted normally, similarly Unix domain socket connections are not
restricted in any way.
2016-01-07 07:00:08 -05:00
" If you want to connect from external computers to Redis you "
" may adopt one of the following solutions: "
" 1) Just disable protected mode sending the command "
" 'CONFIG SET protected-mode no' from the loopback interface "
" by connecting to Redis from the same host the server is "
2016-01-20 10:08:28 -05:00
" running, however MAKE SURE Redis is not publicly accessible "
New security feature: Redis protected mode.
An exposed Redis instance on the internet can be cause of serious
issues. Since Redis, by default, binds to all the interfaces, it is easy
to forget an instance without any protection layer, for error.
Protected mode try to address this feature in a soft way, providing a
layer of protection, but giving clues to Redis users about why the
server is not accepting connections.
When protected mode is enabeld (the default), and if there are no
minumum hints about the fact the server is properly configured (no
"bind" directive is used in order to restrict the server to certain
interfaces, nor a password is set), clients connecting from external
intefaces are refused with an error explaining what to do in order to
fix the issue.
Clients connecting from the IPv4 and IPv6 lookback interfaces are still
accepted normally, similarly Unix domain socket connections are not
restricted in any way.
2016-01-07 07:00:08 -05:00
" from internet if you do so. Use CONFIG REWRITE to make this "
" change permanent. "
" 2) Alternatively you can just disable the protected mode by "
" editing the Redis configuration file, and setting the protected "
" mode option to 'no', and then restarting the server. "
" 3) If you started the server manually just for testing, restart "
2016-01-07 16:42:43 -05:00
" it with the '--protected-mode no' option. "
New security feature: Redis protected mode.
An exposed Redis instance on the internet can be cause of serious
issues. Since Redis, by default, binds to all the interfaces, it is easy
to forget an instance without any protection layer, for error.
Protected mode try to address this feature in a soft way, providing a
layer of protection, but giving clues to Redis users about why the
server is not accepting connections.
When protected mode is enabeld (the default), and if there are no
minumum hints about the fact the server is properly configured (no
"bind" directive is used in order to restrict the server to certain
interfaces, nor a password is set), clients connecting from external
intefaces are refused with an error explaining what to do in order to
fix the issue.
Clients connecting from the IPv4 and IPv6 lookback interfaces are still
accepted normally, similarly Unix domain socket connections are not
restricted in any way.
2016-01-07 07:00:08 -05:00
" 4) Setup a bind address or an authentication password. "
" NOTE: You only need to do one of the above things in order for "
" the server to start accepting connections from the outside. \r \n " ;
2019-09-12 03:56:54 -04:00
if ( connWrite ( c - > conn , err , strlen ( err ) ) = = - 1 ) {
New security feature: Redis protected mode.
An exposed Redis instance on the internet can be cause of serious
issues. Since Redis, by default, binds to all the interfaces, it is easy
to forget an instance without any protection layer, for error.
Protected mode try to address this feature in a soft way, providing a
layer of protection, but giving clues to Redis users about why the
server is not accepting connections.
When protected mode is enabeld (the default), and if there are no
minumum hints about the fact the server is properly configured (no
"bind" directive is used in order to restrict the server to certain
interfaces, nor a password is set), clients connecting from external
intefaces are refused with an error explaining what to do in order to
fix the issue.
Clients connecting from the IPv4 and IPv6 lookback interfaces are still
accepted normally, similarly Unix domain socket connections are not
restricted in any way.
2016-01-07 07:00:08 -05:00
/* Nothing to do, Just to avoid the warning... */
}
server . stat_rejected_conn + + ;
freeClient ( c ) ;
return ;
}
}
2010-06-21 18:07:48 -04:00
server . stat_numconnections + + ;
2019-10-22 04:44:18 -04:00
moduleFireServerEvent ( REDISMODULE_EVENT_CLIENT_CHANGE ,
2019-10-22 06:48:07 -04:00
REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED ,
2019-10-22 04:44:18 -04:00
c ) ;
2019-09-12 03:56:54 -04:00
}
# define MAX_ACCEPTS_PER_CALL 1000
static void acceptCommonHandler ( connection * conn , int flags , char * ip ) {
client * c ;
UNUSED ( ip ) ;
/* Admission control will happen before a client is created and connAccept()
* called , because we don ' t want to even start transport - level negotiation
* if rejected .
*/
if ( listLength ( server . clients ) > = server . maxclients ) {
char * err = " -ERR max number of clients reached \r \n " ;
/* That's a best effort error message, don't check write errors.
* Note that for TLS connections , no handshake was done yet so nothing is written
* and the connection will just drop .
*/
if ( connWrite ( conn , err , strlen ( err ) ) = = - 1 ) {
/* Nothing to do, Just to avoid the warning... */
}
server . stat_rejected_conn + + ;
connClose ( conn ) ;
return ;
}
/* Create connection and client */
if ( ( c = createClient ( conn ) ) = = NULL ) {
char conninfo [ 100 ] ;
serverLog ( LL_WARNING ,
" Error registering fd event for the new client: %s (conn: %s) " ,
connGetLastError ( conn ) ,
connGetInfo ( conn , conninfo , sizeof ( conninfo ) ) ) ;
connClose ( conn ) ; /* May be already closed, just ignore errors */
return ;
}
/* Last chance to keep flags */
2012-11-01 17:10:45 -04:00
c - > flags | = flags ;
2019-09-12 03:56:54 -04:00
/* Initiate accept.
*
* Note that connAccept ( ) is free to do two things here :
* 1. Call clientAcceptHandler ( ) immediately ;
* 2. Schedule a future call to clientAcceptHandler ( ) .
*
* Because of that , we must do nothing else afterwards .
*/
if ( connAccept ( conn , clientAcceptHandler ) = = C_ERR ) {
char conninfo [ 100 ] ;
serverLog ( LL_WARNING ,
" Error accepting a client connection: %s (conn: %s) " ,
connGetLastError ( conn ) , connGetInfo ( conn , conninfo , sizeof ( conninfo ) ) ) ;
2019-08-11 09:07:53 -04:00
freeClient ( connGetPrivateData ( conn ) ) ;
2019-09-12 03:56:54 -04:00
return ;
}
2010-06-21 18:07:48 -04:00
}
2010-10-13 12:34:24 -04:00
void acceptTcpHandler ( aeEventLoop * el , int fd , void * privdata , int mask ) {
2014-04-24 11:33:57 -04:00
int cport , cfd , max = MAX_ACCEPTS_PER_CALL ;
2015-07-27 03:41:48 -04:00
char cip [ NET_IP_STR_LEN ] ;
UNUSED ( el ) ;
UNUSED ( mask ) ;
UNUSED ( privdata ) ;
2010-10-13 12:34:24 -04:00
2014-04-24 11:33:57 -04:00
while ( max - - ) {
cfd = anetTcpAccept ( server . neterr , fd , cip , sizeof ( cip ) , & cport ) ;
if ( cfd = = ANET_ERR ) {
if ( errno ! = EWOULDBLOCK )
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2014-04-24 11:33:57 -04:00
" Accepting client connection: %s " , server . neterr ) ;
return ;
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_VERBOSE , " Accepted %s:%d " , cip , cport ) ;
2019-09-12 03:56:54 -04:00
acceptCommonHandler ( connCreateAcceptedSocket ( cfd ) , 0 , cip ) ;
}
}
void acceptTLSHandler ( aeEventLoop * el , int fd , void * privdata , int mask ) {
int cport , cfd , max = MAX_ACCEPTS_PER_CALL ;
char cip [ NET_IP_STR_LEN ] ;
UNUSED ( el ) ;
UNUSED ( mask ) ;
UNUSED ( privdata ) ;
while ( max - - ) {
cfd = anetTcpAccept ( server . neterr , fd , cip , sizeof ( cip ) , & cport ) ;
if ( cfd = = ANET_ERR ) {
if ( errno ! = EWOULDBLOCK )
serverLog ( LL_WARNING ,
" Accepting client connection: %s " , server . neterr ) ;
return ;
}
serverLog ( LL_VERBOSE , " Accepted %s:%d " , cip , cport ) ;
acceptCommonHandler ( connCreateAcceptedTLS ( cfd , server . tls_auth_clients ) , 0 , cip ) ;
2010-10-13 12:34:24 -04:00
}
}
void acceptUnixHandler ( aeEventLoop * el , int fd , void * privdata , int mask ) {
2014-04-24 11:33:57 -04:00
int cfd , max = MAX_ACCEPTS_PER_CALL ;
2015-07-27 03:41:48 -04:00
UNUSED ( el ) ;
UNUSED ( mask ) ;
UNUSED ( privdata ) ;
2010-10-13 12:34:24 -04:00
2014-04-24 11:33:57 -04:00
while ( max - - ) {
cfd = anetUnixAccept ( server . neterr , fd ) ;
if ( cfd = = ANET_ERR ) {
if ( errno ! = EWOULDBLOCK )
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING ,
2014-04-24 11:33:57 -04:00
" Accepting client connection: %s " , server . neterr ) ;
return ;
}
2015-07-27 03:41:48 -04:00
serverLog ( LL_VERBOSE , " Accepted connection to %s " , server . unixsocket ) ;
2019-09-12 03:56:54 -04:00
acceptCommonHandler ( connCreateAcceptedSocket ( cfd ) , CLIENT_UNIX_SOCKET , NULL ) ;
2010-10-13 12:34:24 -04:00
}
}
2015-07-26 09:20:46 -04:00
static void freeClientArgv ( client * c ) {
2010-06-21 18:07:48 -04:00
int j ;
for ( j = 0 ; j < c - > argc ; j + + )
decrRefCount ( c - > argv [ j ] ) ;
c - > argc = 0 ;
2011-07-08 06:59:30 -04:00
c - > cmd = NULL ;
2010-06-21 18:07:48 -04:00
}
2012-03-29 03:24:02 -04:00
/* Close all the slaves connections. This is useful in chained replication
* when we resync with our own master and want to force all our slaves to
* resync with us as well . */
void disconnectSlaves ( void ) {
while ( listLength ( server . slaves ) ) {
listNode * ln = listFirst ( server . slaves ) ;
2015-07-26 09:20:46 -04:00
freeClient ( ( client * ) ln - > value ) ;
2012-03-29 03:24:02 -04:00
}
}
2015-09-30 10:56:02 -04:00
/* Remove the specified client from global lists where the client could
* be referenced , not including the Pub / Sub channels .
* This is used by freeClient ( ) and replicationCacheMaster ( ) . */
void unlinkClient ( client * c ) {
2010-06-21 18:07:48 -04:00
listNode * ln ;
2015-09-30 10:56:02 -04:00
/* If this is marked as current client unset it. */
2012-01-12 10:02:57 -05:00
if ( server . current_client = = c ) server . current_client = NULL ;
2019-09-12 03:56:54 -04:00
/* Certain operations must be done only if the client has an active connection.
2015-09-30 10:56:02 -04:00
* If the client was already unlinked or if it ' s a " fake client " the
2019-09-12 03:56:54 -04:00
* conn is already set to NULL . */
if ( c - > conn ) {
2015-09-30 10:56:02 -04:00
/* Remove from the list of active clients. */
2017-11-30 05:11:05 -05:00
if ( c - > client_list_node ) {
2018-06-27 07:26:01 -04:00
uint64_t id = htonu64 ( c - > id ) ;
raxRemove ( server . clients_index , ( unsigned char * ) & id , sizeof ( id ) , NULL ) ;
2017-11-30 05:11:05 -05:00
listDelNode ( server . clients , c - > client_list_node ) ;
c - > client_list_node = NULL ;
}
2015-09-30 10:56:02 -04:00
2019-08-11 09:07:53 -04:00
/* Check if this is a replica waiting for diskless replication (rdb pipe),
* in which case it needs to be cleaned from that list */
if ( c - > flags & CLIENT_SLAVE & &
c - > replstate = = SLAVE_STATE_WAIT_BGSAVE_END & &
server . rdb_pipe_conns )
{
int i ;
for ( i = 0 ; i < server . rdb_pipe_numconns ; i + + ) {
if ( server . rdb_pipe_conns [ i ] = = c - > conn ) {
rdbPipeWriteHandlerConnRemoved ( c - > conn ) ;
server . rdb_pipe_conns [ i ] = NULL ;
break ;
}
}
}
2019-09-12 03:56:54 -04:00
connClose ( c - > conn ) ;
c - > conn = NULL ;
2015-09-30 10:56:02 -04:00
}
/* Remove from the list of pending writes if needed. */
if ( c - > flags & CLIENT_PENDING_WRITE ) {
ln = listSearchKey ( server . clients_pending_write , c ) ;
serverAssert ( ln ! = NULL ) ;
listDelNode ( server . clients_pending_write , ln ) ;
2015-12-09 17:06:44 -05:00
c - > flags & = ~ CLIENT_PENDING_WRITE ;
2015-09-30 10:56:02 -04:00
}
2019-03-30 06:26:58 -04:00
/* Remove from the list of pending reads if needed. */
if ( c - > flags & CLIENT_PENDING_READ ) {
ln = listSearchKey ( server . clients_pending_read , c ) ;
serverAssert ( ln ! = NULL ) ;
listDelNode ( server . clients_pending_read , ln ) ;
c - > flags & = ~ CLIENT_PENDING_READ ;
}
2015-09-30 10:56:02 -04:00
/* When client was just unblocked because of a blocking operation,
* remove it from the list of unblocked clients . */
if ( c - > flags & CLIENT_UNBLOCKED ) {
ln = listSearchKey ( server . unblocked_clients , c ) ;
serverAssert ( ln ! = NULL ) ;
listDelNode ( server . unblocked_clients , ln ) ;
2015-12-09 17:06:44 -05:00
c - > flags & = ~ CLIENT_UNBLOCKED ;
2015-09-30 10:56:02 -04:00
}
2019-06-29 20:08:41 -04:00
/* Clear the tracking status. */
if ( c - > flags & CLIENT_TRACKING ) disableTracking ( c ) ;
2015-09-30 10:56:02 -04:00
}
void freeClient ( client * c ) {
listNode * ln ;
2018-10-09 07:25:53 -04:00
/* If a client is protected, yet we need to free it right now, make sure
* to at least use asynchronous freeing . */
if ( c - > flags & CLIENT_PROTECTED ) {
freeClientAsync ( c ) ;
return ;
}
2019-10-23 04:52:25 -04:00
/* For connected clients, call the disconnection event of modules hooks. */
if ( c - > conn ) {
moduleFireServerEvent ( REDISMODULE_EVENT_CLIENT_CHANGE ,
REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED ,
c ) ;
}
2018-10-09 07:25:53 -04:00
2013-01-30 12:33:16 -05:00
/* If it is our master that's beging disconnected we should make sure
* to cache the state to try a partial resynchronization later .
*
* Note that before doing this we make sure that the client is not in
* some unexpected state , by checking its flags . */
2015-07-27 03:41:48 -04:00
if ( server . master & & c - > flags & CLIENT_MASTER ) {
serverLog ( LL_WARNING , " Connection with master lost. " ) ;
if ( ! ( c - > flags & ( CLIENT_CLOSE_AFTER_REPLY |
CLIENT_CLOSE_ASAP |
2018-08-31 10:07:03 -04:00
CLIENT_BLOCKED ) ) )
2013-12-20 18:23:37 -05:00
{
replicationCacheMaster ( c ) ;
return ;
}
2013-01-30 12:33:16 -05:00
}
2013-12-22 04:15:35 -05:00
/* Log link disconnection with slave */
2015-07-27 03:41:48 -04:00
if ( ( c - > flags & CLIENT_SLAVE ) & & ! ( c - > flags & CLIENT_MONITOR ) ) {
2018-09-10 11:02:44 -04:00
serverLog ( LL_WARNING , " Connection with replica %s lost. " ,
2014-10-27 06:58:20 -04:00
replicationGetSlaveName ( c ) ) ;
2013-12-22 04:15:35 -05:00
}
2013-12-03 07:54:06 -05:00
/* Free the query buffer */
2010-06-21 18:07:48 -04:00
sdsfree ( c - > querybuf ) ;
Fix PSYNC2 incomplete command bug as described in #3899.
This bug was discovered by @kevinmcgehee and constituted a major hidden
bug in the PSYNC2 implementation, caused by the propagation from the
master of incomplete commands to slaves.
The bug had several results:
1. Borrowing from Kevin text in the issue: "Given that slaves blindly
copy over their master's input into their own replication backlog over
successive read syscalls, it's possible that with large commands or
small TCP buffers, partial commands are present in this buffer. If the
master were to fail before successfully propagating the entire command
to a slave, the slaves will never execute the partial command (since the
client is invalidated) but will copy it to replication backlog which may
relay those invalid bytes to its slaves on PSYNC2, corrupting the
backlog and possibly other valid commands that follow the failover.
Simple command boundaries aren't sufficient to capture this, either,
because in the case of a MULTI/EXEC block, if the master successfully
propagates a subset of the commands but not the EXEC, then the
transaction in the backlog becomes corrupt and could corrupt other
slaves that consume this data."
2. As identified by @yangsiran later, there is another effect of the
bug. For the same mechanism of the first problem, a slave having another
slave, could receive a full resynchronization request with an already
half-applied command in the backlog. Once the RDB is ready, it will be
sent to the slave, and the replication will continue sending to the
sub-slave the other half of the command, which is not valid.
The fix, designed by @yangsiran and @antirez, and implemented by
@antirez, uses a secondary buffer in order to feed the sub-masters and
update the replication backlog and offsets, only when a given part of
the query buffer is actually *applied* to the state of the instance,
that is, when the command gets processed and the command is not pending
in the Redis transaction buffer because of CLIENT_MULTI state.
Given that now the backlog and offsets representation are in agreement
with the actual processed commands, both issue 1 and 2 should no longer
be possible.
Thanks to @kevinmcgehee, @yangsiran and @oranagra for their work in
identifying and designing a fix for this problem.
2017-04-19 04:25:45 -04:00
sdsfree ( c - > pending_querybuf ) ;
2010-06-21 18:07:48 -04:00
c - > querybuf = NULL ;
2013-12-03 07:54:06 -05:00
/* Deallocate structures used to block on blocking ops. */
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_BLOCKED ) unblockClient ( c ) ;
2012-12-03 06:12:53 -05:00
dictRelease ( c - > bpop . keys ) ;
2010-06-21 18:07:48 -04:00
/* UNWATCH all the keys */
unwatchAllKeys ( c ) ;
listRelease ( c - > watched_keys ) ;
2013-12-03 07:54:06 -05:00
2010-06-21 18:07:48 -04:00
/* Unsubscribe from all the pubsub channels */
pubsubUnsubscribeAllChannels ( c , 0 ) ;
pubsubUnsubscribeAllPatterns ( c , 0 ) ;
dictRelease ( c - > pubsub_channels ) ;
listRelease ( c - > pubsub_patterns ) ;
2013-12-03 07:54:06 -05:00
2015-09-30 10:56:02 -04:00
/* Free data structures. */
2010-06-21 18:07:48 -04:00
listRelease ( c - > reply ) ;
freeClientArgv ( c ) ;
2013-12-03 07:54:06 -05:00
2015-09-30 10:56:02 -04:00
/* Unlink the client: this will close the socket, remove the I/O
* handlers , and remove references of the client from different
* places where active clients may be referenced . */
unlinkClient ( c ) ;
2013-12-03 07:54:06 -05:00
/* Master/slave cleanup Case 1:
* we lost the connection with a slave . */
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_SLAVE ) {
if ( c - > replstate = = SLAVE_STATE_SEND_BULK ) {
2013-08-12 04:29:14 -04:00
if ( c - > repldbfd ! = - 1 ) close ( c - > repldbfd ) ;
if ( c - > replpreamble ) sdsfree ( c - > replpreamble ) ;
}
2015-07-27 03:41:48 -04:00
list * l = ( c - > flags & CLIENT_MONITOR ) ? server . monitors : server . slaves ;
2010-06-21 18:07:48 -04:00
ln = listSearchKey ( l , c ) ;
2015-07-26 09:29:53 -04:00
serverAssert ( ln ! = NULL ) ;
2010-06-21 18:07:48 -04:00
listDelNode ( l , ln ) ;
2013-01-30 12:33:16 -05:00
/* We need to remember the time when we started to have zero
* attached slaves , as after some time we ' ll free the replication
* backlog . */
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_SLAVE & & listLength ( server . slaves ) = = 0 )
2013-01-30 12:33:16 -05:00
server . repl_no_slaves_since = server . unixtime ;
2013-05-29 05:36:44 -04:00
refreshGoodSlavesCount ( ) ;
2019-10-29 11:59:09 -04:00
/* Fire the replica change modules event. */
if ( c - > replstate = = SLAVE_STATE_ONLINE )
moduleFireServerEvent ( REDISMODULE_EVENT_REPLICA_CHANGE ,
REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE ,
NULL ) ;
2010-06-21 18:07:48 -04:00
}
2010-08-24 10:04:13 -04:00
2013-12-03 07:54:06 -05:00
/* Master/slave cleanup Case 2:
* we lost the connection with the master . */
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_MASTER ) replicationHandleMasterDisconnection ( ) ;
2012-01-23 10:12:37 -05:00
/* If this client was scheduled for async freeing we need to remove it
* from the queue . */
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_CLOSE_ASAP ) {
2012-01-23 10:12:37 -05:00
ln = listSearchKey ( server . clients_to_close , c ) ;
2015-07-26 09:29:53 -04:00
serverAssert ( ln ! = NULL ) ;
2012-01-23 10:12:37 -05:00
listDelNode ( server . clients_to_close , ln ) ;
}
2013-12-03 07:54:06 -05:00
/* Release other dynamically allocated client structure fields,
* and finally release the client structure itself . */
2013-01-11 12:43:28 -05:00
if ( c - > name ) decrRefCount ( c - > name ) ;
2010-06-21 18:07:48 -04:00
zfree ( c - > argv ) ;
freeClientMultiState ( c ) ;
2014-04-28 11:36:57 -04:00
sdsfree ( c - > peerid ) ;
2010-06-21 18:07:48 -04:00
zfree ( c ) ;
}
2012-01-23 10:12:37 -05:00
/* Schedule a client to free it at a safe time in the serverCron() function.
* This function is useful when we need to terminate a client but we are in
* a context where calling freeClient ( ) is not possible , because the client
* should be valid for the continuation of the flow of the program . */
2015-07-26 09:20:46 -04:00
void freeClientAsync ( client * c ) {
2017-10-24 02:35:05 -04:00
/* We need to handle concurrent access to the server.clients_to_close list
* only in the freeClientAsync ( ) function , since it ' s the only function that
* may access the list while Redis uses I / O threads . All the other accesses
* are in the context of the main thread while the other threads are
* idle . */
static pthread_mutex_t async_free_queue_mutex = PTHREAD_MUTEX_INITIALIZER ;
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_CLOSE_ASAP | | c - > flags & CLIENT_LUA ) return ;
c - > flags | = CLIENT_CLOSE_ASAP ;
2017-10-24 02:35:05 -04:00
pthread_mutex_lock ( & async_free_queue_mutex ) ;
2012-01-23 10:12:37 -05:00
listAddNodeTail ( server . clients_to_close , c ) ;
2017-10-24 02:35:05 -04:00
pthread_mutex_unlock ( & async_free_queue_mutex ) ;
2012-01-23 10:12:37 -05:00
}
void freeClientsInAsyncFreeQueue ( void ) {
while ( listLength ( server . clients_to_close ) ) {
listNode * ln = listFirst ( server . clients_to_close ) ;
2015-07-26 09:20:46 -04:00
client * c = listNodeValue ( ln ) ;
2012-01-23 10:12:37 -05:00
2015-07-27 03:41:48 -04:00
c - > flags & = ~ CLIENT_CLOSE_ASAP ;
2012-01-23 10:12:37 -05:00
freeClient ( c ) ;
listDelNode ( server . clients_to_close , ln ) ;
}
}
2018-06-27 07:52:46 -04:00
/* Return a client by ID, or NULL if the client ID is not in the set
* of registered clients . Note that " fake clients " , created with - 1 as FD ,
* are not registered clients . */
client * lookupClientByID ( uint64_t id ) {
id = htonu64 ( id ) ;
client * c = raxFind ( server . clients_index , ( unsigned char * ) & id , sizeof ( id ) ) ;
return ( c = = raxNotFound ) ? NULL : c ;
}
2015-09-28 13:27:51 -04:00
/* Write data in output buffers to client. Return C_OK if the client
2017-10-24 02:35:05 -04:00
* is still valid after the call , C_ERR if it was freed because of some
2019-09-12 03:56:54 -04:00
* error . If handler_installed is set , it will attempt to clear the
* write event .
2017-10-24 02:35:05 -04:00
*
* This function is called by threads , but always with handler_installed
* set to 0. So when handler_installed is set to 0 the function must be
* thread safe . */
2019-09-12 03:56:54 -04:00
int writeToClient ( client * c , int handler_installed ) {
2015-01-18 16:46:25 -05:00
ssize_t nwritten = 0 , totwritten = 0 ;
size_t objlen ;
2018-02-21 13:18:34 -05:00
clientReplyBlock * o ;
2010-06-21 18:07:48 -04:00
2015-09-30 10:41:48 -04:00
while ( clientHasPendingReplies ( c ) ) {
2010-08-30 08:44:34 -04:00
if ( c - > bufpos > 0 ) {
2019-09-12 03:56:54 -04:00
nwritten = connWrite ( c - > conn , c - > buf + c - > sentlen , c - > bufpos - c - > sentlen ) ;
2013-05-24 12:58:57 -04:00
if ( nwritten < = 0 ) break ;
2010-08-30 08:44:34 -04:00
c - > sentlen + = nwritten ;
totwritten + = nwritten ;
/* If the buffer was sent, set bufpos to zero to continue with
* the remainder of the reply . */
2015-02-25 04:33:37 -05:00
if ( ( int ) c - > sentlen = = c - > bufpos ) {
2010-08-30 08:44:34 -04:00
c - > bufpos = 0 ;
c - > sentlen = 0 ;
}
} else {
o = listNodeValue ( listFirst ( c - > reply ) ) ;
2018-02-21 13:18:34 -05:00
objlen = o - > used ;
2010-06-21 18:07:48 -04:00
2010-08-30 08:44:34 -04:00
if ( objlen = = 0 ) {
2018-02-21 13:18:34 -05:00
c - > reply_bytes - = o - > size ;
2010-08-30 08:44:34 -04:00
listDelNode ( c - > reply , listFirst ( c - > reply ) ) ;
continue ;
}
2010-06-21 18:07:48 -04:00
2019-09-12 03:56:54 -04:00
nwritten = connWrite ( c - > conn , o - > buf + c - > sentlen , objlen - c - > sentlen ) ;
2013-05-24 12:58:57 -04:00
if ( nwritten < = 0 ) break ;
2010-08-30 08:44:34 -04:00
c - > sentlen + = nwritten ;
totwritten + = nwritten ;
2010-06-21 18:07:48 -04:00
2010-08-30 08:44:34 -04:00
/* If we fully sent the object on head go to the next one */
if ( c - > sentlen = = objlen ) {
2018-02-21 13:18:34 -05:00
c - > reply_bytes - = o - > size ;
2010-08-30 08:44:34 -04:00
listDelNode ( c - > reply , listFirst ( c - > reply ) ) ;
c - > sentlen = 0 ;
2017-07-04 05:55:05 -04:00
/* If there are no longer objects in the list, we expect
* the count of reply bytes to be exactly zero . */
if ( listLength ( c - > reply ) = = 0 )
serverAssert ( c - > reply_bytes = = 0 ) ;
2010-08-30 08:44:34 -04:00
}
2010-06-21 18:07:48 -04:00
}
2015-07-27 03:41:48 -04:00
/* Note that we avoid to send more than NET_MAX_WRITES_PER_EVENT
2010-06-21 18:07:48 -04:00
* bytes , in a single threaded server it ' s a good idea to serve
* other clients as well , even if a very large request comes from
* super fast link that is always able to accept data ( in real world
2012-02-04 08:05:54 -05:00
* scenario think about ' KEYS * ' against the loopback interface ) .
*
* However if we are over the maxmemory limit we ignore that and
2017-03-06 11:42:52 -05:00
* just deliver as much data as it is possible to deliver .
*
* Moreover , we also send as much as possible if the client is
* a slave ( otherwise , on high - speed traffic , the replication
* buffer will grow indefinitely ) */
2015-07-27 03:41:48 -04:00
if ( totwritten > NET_MAX_WRITES_PER_EVENT & &
2012-02-04 08:05:54 -05:00
( server . maxmemory = = 0 | |
2017-03-06 11:42:52 -05:00
zmalloc_used_memory ( ) < server . maxmemory ) & &
! ( c - > flags & CLIENT_SLAVE ) ) break ;
2010-06-21 18:07:48 -04:00
}
2017-02-10 07:13:01 -05:00
server . stat_net_output_bytes + = totwritten ;
2010-06-21 18:07:48 -04:00
if ( nwritten = = - 1 ) {
2019-09-12 03:56:54 -04:00
if ( connGetState ( c - > conn ) = = CONN_STATE_CONNECTED ) {
2010-06-21 18:07:48 -04:00
nwritten = 0 ;
} else {
2019-04-08 07:12:10 -04:00
serverLog ( LL_VERBOSE ,
2019-09-12 03:56:54 -04:00
" Error writing to client: %s " , connGetLastError ( c - > conn ) ) ;
2017-10-24 02:35:05 -04:00
freeClientAsync ( c ) ;
2015-09-28 13:27:51 -04:00
return C_ERR ;
2010-06-21 18:07:48 -04:00
}
}
2013-10-04 06:59:24 -04:00
if ( totwritten > 0 ) {
/* For clients representing masters we don't count sending data
* as an interaction , since we always send REPLCONF ACK commands
* that take some time to just fill the socket output buffer .
* We just rely on data / pings received for timeout detection . */
2015-07-27 03:41:48 -04:00
if ( ! ( c - > flags & CLIENT_MASTER ) ) c - > lastinteraction = server . unixtime ;
2013-10-04 06:59:24 -04:00
}
2015-09-30 10:41:48 -04:00
if ( ! clientHasPendingReplies ( c ) ) {
2010-06-21 18:07:48 -04:00
c - > sentlen = 0 ;
2017-10-24 02:35:05 -04:00
/* Note that writeToClient() is called in a threaded way, but
* adDeleteFileEvent ( ) is not thread safe : however writeToClient ( )
* is always called with handler_installed set to 0 from threads
* so we are fine . */
2019-09-12 03:56:54 -04:00
if ( handler_installed ) connSetWriteHandler ( c - > conn , NULL ) ;
2010-10-13 05:25:40 -04:00
/* Close connection after entire reply has been sent. */
2015-09-28 13:27:51 -04:00
if ( c - > flags & CLIENT_CLOSE_AFTER_REPLY ) {
2017-10-24 02:35:05 -04:00
freeClientAsync ( c ) ;
2015-09-28 13:27:51 -04:00
return C_ERR ;
}
2010-06-21 18:07:48 -04:00
}
2015-09-28 13:27:51 -04:00
return C_OK ;
}
/* Write event handler. Just send data to the client. */
2019-09-12 03:56:54 -04:00
void sendReplyToClient ( connection * conn ) {
client * c = connGetPrivateData ( conn ) ;
writeToClient ( c , 1 ) ;
2010-06-21 18:07:48 -04:00
}
2015-09-28 13:06:36 -04:00
/* This function is called just before entering the event loop, in the hope
* we can just write the replies to the client output buffer without any
* need to use a syscall in order to install the writable event handler ,
* get it called , and so forth . */
2015-09-30 11:23:34 -04:00
int handleClientsWithPendingWrites ( void ) {
2015-09-28 13:06:36 -04:00
listIter li ;
listNode * ln ;
2015-09-30 11:23:34 -04:00
int processed = listLength ( server . clients_pending_write ) ;
2015-09-28 13:06:36 -04:00
listRewind ( server . clients_pending_write , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
client * c = listNodeValue ( ln ) ;
c - > flags & = ~ CLIENT_PENDING_WRITE ;
listDelNode ( server . clients_pending_write , ln ) ;
2018-10-09 08:34:11 -04:00
/* If a client is protected, don't do anything,
* that may trigger write error or recreate handler . */
if ( c - > flags & CLIENT_PROTECTED ) continue ;
2015-09-28 13:06:36 -04:00
/* Try to write buffers to the client socket. */
2019-09-12 03:56:54 -04:00
if ( writeToClient ( c , 0 ) = = C_ERR ) continue ;
2015-09-28 13:06:36 -04:00
2018-02-27 04:40:40 -05:00
/* If after the synchronous writes above we still have data to
* output to the client , we need to install the writable handler . */
if ( clientHasPendingReplies ( c ) ) {
2019-08-19 05:18:25 -04:00
int ae_barrier = 0 ;
2018-02-27 04:40:40 -05:00
/* For the fsync=always policy, we want that a given FD is never
* served for reading and writing in the same event loop iteration ,
* so that in the middle of receiving the query , and serving it
* to the client , we ' ll call beforeSleep ( ) that will do the
2019-08-19 05:18:25 -04:00
* actual fsync of AOF to disk . the write barrier ensures that . */
2018-02-27 04:40:40 -05:00
if ( server . aof_state = = AOF_ON & &
server . aof_fsync = = AOF_FSYNC_ALWAYS )
{
2019-08-19 05:18:25 -04:00
ae_barrier = 1 ;
2018-02-27 04:40:40 -05:00
}
2019-08-19 05:18:25 -04:00
if ( connSetWriteHandlerWithBarrier ( c - > conn , sendReplyToClient , ae_barrier ) = = C_ERR ) {
2019-09-12 03:56:54 -04:00
freeClientAsync ( c ) ;
2018-02-27 04:40:40 -05:00
}
2015-09-28 13:06:36 -04:00
}
}
2015-09-30 11:23:34 -04:00
return processed ;
2015-09-28 13:06:36 -04:00
}
2010-06-21 18:07:48 -04:00
/* resetClient prepare the client to process the next command */
2015-07-26 09:20:46 -04:00
void resetClient ( client * c ) {
2013-02-20 11:07:52 -05:00
redisCommandProc * prevcmd = c - > cmd ? c - > cmd - > proc : NULL ;
2010-06-21 18:07:48 -04:00
freeClientArgv ( c ) ;
2010-10-15 09:40:25 -04:00
c - > reqtype = 0 ;
c - > multibulklen = 0 ;
2010-06-21 18:07:48 -04:00
c - > bulklen = - 1 ;
CLIENT REPLY command implemented: ON, OFF and SKIP modes.
Sometimes it can be useful for clients to completely disable replies
from the Redis server. For example when the client sends fire and forget
commands or performs a mass loading of data, or in caching contexts
where new data is streamed constantly. In such contexts to use server
time and bandwidth in order to send back replies to clients, which are
going to be ignored, is a shame.
Multiple mechanisms are possible to implement such a feature. For
example it could be a feature of MULTI/EXEC, or a command prefix
such as "NOREPLY SADD myset foo", or a different mechanism that allows
to switch on/off requests using the CLIENT command.
The MULTI/EXEC approach has the problem that transactions are not
strictly part of the no-reply semantics, and if we want to insert a lot
of data in a bulk way, creating a huge MULTI/EXEC transaction in the
server memory is bad.
The prefix is the best in this specific use case since it does not allow
desynchronizations, and is pretty clear semantically. However Redis
internals and client libraries are not prepared to handle this
currently.
So the implementation uses the CLIENT command, providing a new REPLY
subcommand with three options:
CLIENT REPLY OFF disables the replies, and does not reply itself.
CLIENT REPLY ON re-enables the replies, replying +OK.
CLIENT REPLY SKIP only discards the reply of the next command, and
like OFF does not reply anything itself.
The reason to add the SKIP command is that it allows to have an easy
way to send conceptually "single" commands that don't need a reply
as the sum of two pipelined commands:
CLIENT REPLY SKIP
SET key value
Note that CLIENT REPLY ON replies with +OK so it should be used when
sending multiple commands that don't need a reply. However since it
replies with +OK the client can check that the connection is still
active and all the previous commands were received.
This is currently just into Redis "unstable" so the proposal can be
modified or abandoned based on users inputs.
2015-10-21 14:43:37 -04:00
2013-02-20 11:07:52 -05:00
/* We clear the ASKING flag as well if we are not inside a MULTI, and
* if what we just executed is not the ASKING command itself . */
2015-07-27 03:41:48 -04:00
if ( ! ( c - > flags & CLIENT_MULTI ) & & prevcmd ! = askingCommand )
CLIENT REPLY command implemented: ON, OFF and SKIP modes.
Sometimes it can be useful for clients to completely disable replies
from the Redis server. For example when the client sends fire and forget
commands or performs a mass loading of data, or in caching contexts
where new data is streamed constantly. In such contexts to use server
time and bandwidth in order to send back replies to clients, which are
going to be ignored, is a shame.
Multiple mechanisms are possible to implement such a feature. For
example it could be a feature of MULTI/EXEC, or a command prefix
such as "NOREPLY SADD myset foo", or a different mechanism that allows
to switch on/off requests using the CLIENT command.
The MULTI/EXEC approach has the problem that transactions are not
strictly part of the no-reply semantics, and if we want to insert a lot
of data in a bulk way, creating a huge MULTI/EXEC transaction in the
server memory is bad.
The prefix is the best in this specific use case since it does not allow
desynchronizations, and is pretty clear semantically. However Redis
internals and client libraries are not prepared to handle this
currently.
So the implementation uses the CLIENT command, providing a new REPLY
subcommand with three options:
CLIENT REPLY OFF disables the replies, and does not reply itself.
CLIENT REPLY ON re-enables the replies, replying +OK.
CLIENT REPLY SKIP only discards the reply of the next command, and
like OFF does not reply anything itself.
The reason to add the SKIP command is that it allows to have an easy
way to send conceptually "single" commands that don't need a reply
as the sum of two pipelined commands:
CLIENT REPLY SKIP
SET key value
Note that CLIENT REPLY ON replies with +OK so it should be used when
sending multiple commands that don't need a reply. However since it
replies with +OK the client can check that the connection is still
active and all the previous commands were received.
This is currently just into Redis "unstable" so the proposal can be
modified or abandoned based on users inputs.
2015-10-21 14:43:37 -04:00
c - > flags & = ~ CLIENT_ASKING ;
/* Remove the CLIENT_REPLY_SKIP flag if any so that the reply
* to the next command will be sent , but set the flag if the command
* we just processed was " CLIENT REPLY SKIP " . */
c - > flags & = ~ CLIENT_REPLY_SKIP ;
if ( c - > flags & CLIENT_REPLY_SKIP_NEXT ) {
c - > flags | = CLIENT_REPLY_SKIP ;
c - > flags & = ~ CLIENT_REPLY_SKIP_NEXT ;
}
2010-06-21 18:07:48 -04:00
}
2018-10-09 07:15:41 -04:00
/* This funciton is used when we want to re-enter the event loop but there
* is the risk that the client we are dealing with will be freed in some
* way . This happens for instance in :
*
* * DEBUG RELOAD and similar .
* * When a Lua script is in - BUSY state .
*
* So the function will protect the client by doing two things :
*
* 1 ) It removes the file events . This way it is not possible that an
* error is signaled on the socket , freeing the client .
* 2 ) Moreover it makes sure that if the client is freed in a different code
* path , it is not really released , but only marked for later release . */
void protectClient ( client * c ) {
c - > flags | = CLIENT_PROTECTED ;
2019-09-12 03:56:54 -04:00
connSetReadHandler ( c - > conn , NULL ) ;
connSetWriteHandler ( c - > conn , NULL ) ;
2018-10-09 07:15:41 -04:00
}
/* This will undo the client protection done by protectClient() */
void unprotectClient ( client * c ) {
if ( c - > flags & CLIENT_PROTECTED ) {
c - > flags & = ~ CLIENT_PROTECTED ;
2019-09-12 03:56:54 -04:00
connSetReadHandler ( c - > conn , readQueryFromClient ) ;
2018-10-09 07:15:41 -04:00
if ( clientHasPendingReplies ( c ) ) clientInstallWriteHandler ( c ) ;
}
}
2017-04-12 04:12:27 -04:00
/* Like processMultibulkBuffer(), but for the inline protocol instead of RESP,
* this function consumes the client query buffer and creates a command ready
* to be executed inside the client structure . Returns C_OK if the command
* is ready to be executed , or C_ERR if there is still protocol to read to
* have a well formed command . The function also returns C_ERR when there is
* a protocol error : in such a case the client structure is setup to reply
* with the error and close the connection . */
2015-07-26 09:20:46 -04:00
int processInlineBuffer ( client * c ) {
2013-12-09 07:28:39 -05:00
char * newline ;
fix rare replication stream corruption with disk-based replication
The slave sends \n keepalive messages to the master while parsing the rdb,
and later sends REPLCONF ACK once a second. rarely, the master recives both
a linefeed char and a REPLCONF in the same read, \n*3\r\n$8\r\nREPLCONF\r\n...
and it tries to trim two chars (\r\n) from the query buffer,
trimming the '*' from *3\r\n$8\r\nREPLCONF\r\n...
then the master tries to process a command starting with '3' and replies to
the slave a bunch of -ERR and one +OK.
although the slave silently ignores these (prints a log message), this corrupts
the replication offset at the slave since the slave increases the replication
offset, and the master did not.
other than the fix in processInlineBuffer, i did several other improvments
while hunting this very rare bug.
- when redis replies with "unknown command" it includes a portion of the
arguments, not just the command name. so it would be easier to understand
what was recived, in my case, on the slave side, it was -ERR, but
the "arguments" were the interesting part (containing info on the error).
- about a year ago i added code in addReplyErrorLength to print the error to
the log in case of a reply to master (since this string isn't actually
trasmitted to the master), now changed that block to print a similar log
message to indicate an error being sent from the master to the slave.
note that the slave is marked as CLIENT_SLAVE only after PSYNC was received,
so this will not cause any harm for REPLCONF, and will only indicate problems
that are gonna corrupt the replication stream anyway.
- two places were c->reply was emptied, and i wanted to reset sentlen
this is a precaution (i did not actually see such a problem), since a
non-zero sentlen will cause corruption to be transmitted on the socket.
2018-07-14 09:49:15 -04:00
int argc , j , linefeed_chars = 1 ;
2013-07-24 04:37:55 -04:00
sds * argv , aux ;
2016-11-28 11:54:04 -05:00
size_t querylen ;
2010-10-15 09:40:25 -04:00
2013-12-09 07:28:39 -05:00
/* Search for end of line */
2018-08-13 12:43:36 -04:00
newline = strchr ( c - > querybuf + c - > qb_pos , ' \n ' ) ;
2013-12-09 07:28:39 -05:00
2010-10-15 09:40:25 -04:00
/* Nothing to do without a \r\n */
2011-12-31 10:09:46 -05:00
if ( newline = = NULL ) {
2018-08-13 12:43:36 -04:00
if ( sdslen ( c - > querybuf ) - c - > qb_pos > PROTO_INLINE_MAX_SIZE ) {
2011-12-31 10:09:46 -05:00
addReplyError ( c , " Protocol error: too big inline request " ) ;
2018-08-23 00:21:23 -04:00
setProtocolError ( " too big inline request " , c ) ;
2011-12-31 10:09:46 -05:00
}
2015-07-26 17:17:55 -04:00
return C_ERR ;
2011-12-31 10:09:46 -05:00
}
2010-10-15 09:40:25 -04:00
2013-12-09 07:28:39 -05:00
/* Handle the \r\n case. */
2018-08-13 12:43:36 -04:00
if ( newline & & newline ! = c - > querybuf + c - > qb_pos & & * ( newline - 1 ) = = ' \r ' )
fix rare replication stream corruption with disk-based replication
The slave sends \n keepalive messages to the master while parsing the rdb,
and later sends REPLCONF ACK once a second. rarely, the master recives both
a linefeed char and a REPLCONF in the same read, \n*3\r\n$8\r\nREPLCONF\r\n...
and it tries to trim two chars (\r\n) from the query buffer,
trimming the '*' from *3\r\n$8\r\nREPLCONF\r\n...
then the master tries to process a command starting with '3' and replies to
the slave a bunch of -ERR and one +OK.
although the slave silently ignores these (prints a log message), this corrupts
the replication offset at the slave since the slave increases the replication
offset, and the master did not.
other than the fix in processInlineBuffer, i did several other improvments
while hunting this very rare bug.
- when redis replies with "unknown command" it includes a portion of the
arguments, not just the command name. so it would be easier to understand
what was recived, in my case, on the slave side, it was -ERR, but
the "arguments" were the interesting part (containing info on the error).
- about a year ago i added code in addReplyErrorLength to print the error to
the log in case of a reply to master (since this string isn't actually
trasmitted to the master), now changed that block to print a similar log
message to indicate an error being sent from the master to the slave.
note that the slave is marked as CLIENT_SLAVE only after PSYNC was received,
so this will not cause any harm for REPLCONF, and will only indicate problems
that are gonna corrupt the replication stream anyway.
- two places were c->reply was emptied, and i wanted to reset sentlen
this is a precaution (i did not actually see such a problem), since a
non-zero sentlen will cause corruption to be transmitted on the socket.
2018-07-14 09:49:15 -04:00
newline - - , linefeed_chars + + ;
2013-12-09 07:28:39 -05:00
2010-10-15 09:40:25 -04:00
/* Split the input buffer up to the \r\n */
2018-08-13 12:43:36 -04:00
querylen = newline - ( c - > querybuf + c - > qb_pos ) ;
aux = sdsnewlen ( c - > querybuf + c - > qb_pos , querylen ) ;
2013-07-24 04:37:55 -04:00
argv = sdssplitargs ( aux , & argc ) ;
sdsfree ( aux ) ;
2013-12-08 05:57:03 -05:00
if ( argv = = NULL ) {
addReplyError ( c , " Protocol error: unbalanced quotes in request " ) ;
2018-08-23 00:21:23 -04:00
setProtocolError ( " unbalanced quotes in inline request " , c ) ;
2015-07-26 17:17:55 -04:00
return C_ERR ;
2013-12-08 05:57:03 -05:00
}
2010-10-15 09:40:25 -04:00
2013-12-09 07:32:44 -05:00
/* Newline from slaves can be used to refresh the last ACK time.
* This is useful for a slave to ping back while loading a big
* RDB file . */
2015-07-27 03:41:48 -04:00
if ( querylen = = 0 & & c - > flags & CLIENT_SLAVE )
2013-12-09 07:32:44 -05:00
c - > repl_ack_time = server . unixtime ;
2018-08-14 02:50:37 -04:00
/* Move querybuffer position to the next query in the buffer. */
c - > qb_pos + = querylen + linefeed_chars ;
2010-10-15 09:40:25 -04:00
/* Setup argv array on client structure */
2014-11-25 08:48:30 -05:00
if ( argc ) {
if ( c - > argv ) zfree ( c - > argv ) ;
c - > argv = zmalloc ( sizeof ( robj * ) * argc ) ;
}
2010-10-15 09:40:25 -04:00
/* Create redis objects for all arguments. */
for ( c - > argc = 0 , j = 0 ; j < argc ; j + + ) {
if ( sdslen ( argv [ j ] ) ) {
2015-07-26 09:28:00 -04:00
c - > argv [ c - > argc ] = createObject ( OBJ_STRING , argv [ j ] ) ;
2010-10-15 09:40:25 -04:00
c - > argc + + ;
} else {
sdsfree ( argv [ j ] ) ;
}
}
zfree ( argv ) ;
2015-07-26 17:17:55 -04:00
return C_OK ;
2010-10-15 09:40:25 -04:00
}
2018-08-23 00:21:23 -04:00
/* Helper function. Record protocol erro details in server log,
* and set the client as CLIENT_CLOSE_AFTER_REPLY . */
2016-11-25 04:55:16 -05:00
# define PROTO_DUMP_LEN 128
2018-08-23 00:21:23 -04:00
static void setProtocolError ( const char * errstr , client * c ) {
2015-07-27 03:41:48 -04:00
if ( server . verbosity < = LL_VERBOSE ) {
2014-04-28 11:36:57 -04:00
sds client = catClientInfoString ( sdsempty ( ) , c ) ;
2016-11-25 04:55:16 -05:00
/* Sample some protocol to given an idea about what was inside. */
char buf [ 256 ] ;
2018-08-13 12:43:36 -04:00
if ( sdslen ( c - > querybuf ) - c - > qb_pos < PROTO_DUMP_LEN ) {
snprintf ( buf , sizeof ( buf ) , " Query buffer during protocol error: '%s' " , c - > querybuf + c - > qb_pos ) ;
2016-11-25 04:55:16 -05:00
} else {
2018-08-13 12:43:36 -04:00
snprintf ( buf , sizeof ( buf ) , " Query buffer during protocol error: '%.*s' (... more %zu bytes ...) '%.*s' " , PROTO_DUMP_LEN / 2 , c - > querybuf + c - > qb_pos , sdslen ( c - > querybuf ) - c - > qb_pos - PROTO_DUMP_LEN , PROTO_DUMP_LEN / 2 , c - > querybuf + sdslen ( c - > querybuf ) - PROTO_DUMP_LEN / 2 ) ;
2016-11-25 04:55:16 -05:00
}
/* Remove non printable chars. */
char * p = buf ;
while ( * p ! = ' \0 ' ) {
if ( ! isprint ( * p ) ) * p = ' . ' ;
p + + ;
}
/* Log all the client and protocol info. */
2015-07-27 03:41:48 -04:00
serverLog ( LL_VERBOSE ,
2016-11-25 04:55:16 -05:00
" Protocol error (%s) from client: %s. %s " , errstr , client , buf ) ;
2011-11-25 10:09:16 -05:00
sdsfree ( client ) ;
}
2015-07-27 03:41:48 -04:00
c - > flags | = CLIENT_CLOSE_AFTER_REPLY ;
2010-10-15 09:40:25 -04:00
}
2017-04-12 04:12:27 -04:00
/* Process the query buffer for client 'c', setting up the client argument
* vector for command execution . Returns C_OK if after running the function
* the client has a well - formed ready to be processed command , otherwise
* C_ERR if there is still to read more buffer to get the full command .
* The function also returns C_ERR when there is a protocol error : in such a
* case the client structure is setup to reply with the error and close
* the connection .
*
* This function is called if processInputBuffer ( ) detects that the next
* command is in RESP format , so the first byte in the command is found
* to be ' * ' . Otherwise for inline commands processInlineBuffer ( ) is called . */
2015-07-26 09:20:46 -04:00
int processMultibulkBuffer ( client * c ) {
2010-10-15 09:40:25 -04:00
char * newline = NULL ;
2017-12-21 04:10:48 -05:00
int ok ;
2011-04-18 15:09:12 -04:00
long long ll ;
2010-10-15 09:40:25 -04:00
if ( c - > multibulklen = = 0 ) {
/* The client should have been reset */
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( c , NULL , c - > argc = = 0 ) ;
2010-10-15 09:40:25 -04:00
/* Multi bulk length cannot be read without a \r\n */
2018-08-13 12:43:36 -04:00
newline = strchr ( c - > querybuf + c - > qb_pos , ' \r ' ) ;
2011-12-31 10:09:46 -05:00
if ( newline = = NULL ) {
2018-08-13 12:43:36 -04:00
if ( sdslen ( c - > querybuf ) - c - > qb_pos > PROTO_INLINE_MAX_SIZE ) {
2011-12-31 10:09:46 -05:00
addReplyError ( c , " Protocol error: too big mbulk count string " ) ;
2018-08-23 00:21:23 -04:00
setProtocolError ( " too big mbulk count string " , c ) ;
2011-12-31 10:09:46 -05:00
}
2015-07-26 17:17:55 -04:00
return C_ERR ;
2011-12-31 10:09:46 -05:00
}
2010-10-15 09:40:25 -04:00
2011-04-27 08:29:27 -04:00
/* Buffer should also contain \n */
2018-08-13 12:43:36 -04:00
if ( newline - ( c - > querybuf + c - > qb_pos ) > ( ssize_t ) ( sdslen ( c - > querybuf ) - c - > qb_pos - 2 ) )
2015-07-26 17:17:55 -04:00
return C_ERR ;
2011-04-27 08:29:27 -04:00
2010-10-15 09:40:25 -04:00
/* We know for sure there is a whole line since newline != NULL,
* so go ahead and find out the multi bulk length . */
2018-08-13 12:43:36 -04:00
serverAssertWithInfo ( c , NULL , c - > querybuf [ c - > qb_pos ] = = ' * ' ) ;
ok = string2ll ( c - > querybuf + 1 + c - > qb_pos , newline - ( c - > querybuf + 1 + c - > qb_pos ) , & ll ) ;
2011-04-18 15:09:12 -04:00
if ( ! ok | | ll > 1024 * 1024 ) {
2010-10-15 13:15:38 -04:00
addReplyError ( c , " Protocol error: invalid multibulk length " ) ;
2018-08-23 00:21:23 -04:00
setProtocolError ( " invalid mbulk count " , c ) ;
2015-07-26 17:17:55 -04:00
return C_ERR ;
2010-10-15 09:40:25 -04:00
}
2011-04-27 07:16:23 -04:00
2018-08-13 12:43:36 -04:00
c - > qb_pos = ( newline - c - > querybuf ) + 2 ;
2018-08-14 01:55:30 -04:00
if ( ll < = 0 ) return C_OK ;
2011-04-27 07:16:23 -04:00
2011-04-18 15:09:12 -04:00
c - > multibulklen = ll ;
2010-10-15 09:40:25 -04:00
/* Setup argv array on client structure */
if ( c - > argv ) zfree ( c - > argv ) ;
c - > argv = zmalloc ( sizeof ( robj * ) * c - > multibulklen ) ;
}
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( c , NULL , c - > multibulklen > 0 ) ;
2010-10-15 09:40:25 -04:00
while ( c - > multibulklen ) {
/* Read bulk length if unknown */
if ( c - > bulklen = = - 1 ) {
2018-08-13 12:43:36 -04:00
newline = strchr ( c - > querybuf + c - > qb_pos , ' \r ' ) ;
2011-12-31 10:09:46 -05:00
if ( newline = = NULL ) {
2018-08-13 12:43:36 -04:00
if ( sdslen ( c - > querybuf ) - c - > qb_pos > PROTO_INLINE_MAX_SIZE ) {
2014-04-23 04:19:43 -04:00
addReplyError ( c ,
" Protocol error: too big bulk count string " ) ;
2018-08-23 00:21:23 -04:00
setProtocolError ( " too big bulk count string " , c ) ;
2015-07-26 17:17:55 -04:00
return C_ERR ;
2011-12-31 10:09:46 -05:00
}
2011-04-27 08:29:27 -04:00
break ;
2011-12-31 10:09:46 -05:00
}
2011-04-27 08:29:27 -04:00
/* Buffer should also contain \n */
2018-08-13 12:43:36 -04:00
if ( newline - ( c - > querybuf + c - > qb_pos ) > ( ssize_t ) ( sdslen ( c - > querybuf ) - c - > qb_pos - 2 ) )
2010-10-15 09:40:25 -04:00
break ;
2011-04-27 08:29:27 -04:00
2018-08-13 12:43:36 -04:00
if ( c - > querybuf [ c - > qb_pos ] ! = ' $ ' ) {
2011-04-27 08:29:27 -04:00
addReplyErrorFormat ( c ,
" Protocol error: expected '$', got '%c' " ,
2018-08-13 12:43:36 -04:00
c - > querybuf [ c - > qb_pos ] ) ;
2018-08-23 00:21:23 -04:00
setProtocolError ( " expected $ but got something else " , c ) ;
2015-07-26 17:17:55 -04:00
return C_ERR ;
2010-06-21 18:07:48 -04:00
}
2011-04-27 08:29:27 -04:00
2018-08-13 12:43:36 -04:00
ok = string2ll ( c - > querybuf + c - > qb_pos + 1 , newline - ( c - > querybuf + c - > qb_pos + 1 ) , & ll ) ;
2018-01-11 05:27:03 -05:00
if ( ! ok | | ll < 0 | | ll > server . proto_max_bulk_len ) {
2011-04-27 08:29:27 -04:00
addReplyError ( c , " Protocol error: invalid bulk length " ) ;
2018-08-23 00:21:23 -04:00
setProtocolError ( " invalid bulk length " , c ) ;
2015-07-26 17:17:55 -04:00
return C_ERR ;
2011-04-27 08:29:27 -04:00
}
2018-08-13 12:43:36 -04:00
c - > qb_pos = newline - c - > querybuf + 2 ;
2015-07-27 03:41:48 -04:00
if ( ll > = PROTO_MBULK_BIG_ARG ) {
2011-11-03 10:53:40 -04:00
/* If we are going to read a large object from network
* try to make it likely that it will start at c - > querybuf
2013-08-27 05:54:38 -04:00
* boundary so that we can optimize object creation
2018-09-03 12:02:18 -04:00
* avoiding a large copy of data .
*
* But only when the data we have not parsed is less than
* or equal to ll + 2. If the data length is greater than
* ll + 2 , trimming querybuf is just a waste of time , because
* at this time the querybuf contains not only our bulk . */
if ( sdslen ( c - > querybuf ) - c - > qb_pos < = ( size_t ) ll + 2 ) {
sdsrange ( c - > querybuf , c - > qb_pos , - 1 ) ;
c - > qb_pos = 0 ;
/* Hint the sds library about the amount of bytes this string is
* going to contain . */
c - > querybuf = sdsMakeRoomFor ( c - > querybuf , ll + 2 ) ;
}
2011-11-08 05:26:06 -05:00
}
2011-04-27 08:29:27 -04:00
c - > bulklen = ll ;
2010-10-15 09:40:25 -04:00
}
/* Read bulk argument */
2018-08-13 12:43:36 -04:00
if ( sdslen ( c - > querybuf ) - c - > qb_pos < ( size_t ) ( c - > bulklen + 2 ) ) {
2010-10-15 09:40:25 -04:00
/* Not enough data (+2 == trailing \r\n) */
break ;
} else {
2013-01-19 07:46:14 -05:00
/* Optimization: if the buffer contains JUST our bulk element
2011-11-02 12:30:19 -04:00
* instead of creating a new object by * copying * the sds we
* just use the current sds string . */
2018-08-13 12:43:36 -04:00
if ( c - > qb_pos = = 0 & &
2015-07-27 03:41:48 -04:00
c - > bulklen > = PROTO_MBULK_BIG_ARG & &
2017-12-21 04:10:48 -05:00
sdslen ( c - > querybuf ) = = ( size_t ) ( c - > bulklen + 2 ) )
2011-11-02 12:30:19 -04:00
{
2015-07-26 09:28:00 -04:00
c - > argv [ c - > argc + + ] = createObject ( OBJ_STRING , c - > querybuf ) ;
2011-11-02 12:30:19 -04:00
sdsIncrLen ( c - > querybuf , - 2 ) ; /* remove CRLF */
/* Assume that if we saw a fat argument we'll see another one
* likely . . . */
2017-02-23 06:04:08 -05:00
c - > querybuf = sdsnewlen ( SDS_NOINIT , c - > bulklen + 2 ) ;
2016-04-25 09:48:09 -04:00
sdsclear ( c - > querybuf ) ;
2011-11-02 12:30:19 -04:00
} else {
c - > argv [ c - > argc + + ] =
2018-08-13 12:43:36 -04:00
createStringObject ( c - > querybuf + c - > qb_pos , c - > bulklen ) ;
c - > qb_pos + = c - > bulklen + 2 ;
2011-11-02 12:30:19 -04:00
}
2010-10-15 09:40:25 -04:00
c - > bulklen = - 1 ;
c - > multibulklen - - ;
}
}
/* We're done when c->multibulk == 0 */
2015-07-26 17:17:55 -04:00
if ( c - > multibulklen = = 0 ) return C_OK ;
2011-12-31 10:09:46 -05:00
2017-04-12 04:12:27 -04:00
/* Still not ready to process the command */
2015-07-26 17:17:55 -04:00
return C_ERR ;
2010-10-15 09:40:25 -04:00
}
2019-04-26 13:29:50 -04:00
/* This function calls processCommand(), but also performs a few sub tasks
* that are useful in that context :
*
* 1. It sets the current client to the client ' c ' .
* 2. In the case of master clients , the replication offset is updated .
* 3. The client is reset unless there are reasons to avoid doing it .
*
* The function returns C_ERR in case the client was freed as a side effect
* of processing the command , otherwise C_OK is returned . */
int processCommandAndResetClient ( client * c ) {
int deadclient = 0 ;
server . current_client = c ;
if ( processCommand ( c ) = = C_OK ) {
if ( c - > flags & CLIENT_MASTER & & ! ( c - > flags & CLIENT_MULTI ) ) {
/* Update the applied replication offset of our master. */
c - > reploff = c - > read_reploff - sdslen ( c - > querybuf ) + c - > qb_pos ;
}
/* Don't reset the client structure for clients blocked in a
* module blocking command , so that the reply callback will
* still be able to access the client argv and argc field .
* The client will be reset in unblockClientFromModule ( ) . */
if ( ! ( c - > flags & CLIENT_BLOCKED ) | |
c - > btype ! = BLOCKED_MODULE )
{
resetClient ( c ) ;
}
}
if ( server . current_client = = NULL ) deadclient = 1 ;
server . current_client = NULL ;
/* freeMemoryIfNeeded may flush slave output buffers. This may
* result into a slave , that may be the active client , to be
* freed . */
return deadclient ? C_ERR : C_OK ;
}
2017-04-12 04:12:27 -04:00
/* This function is called every time, in the client structure 'c', there is
* more query buffer to process , because we read more data from the socket
* or because a client was blocked and later reactivated , so there could be
* pending query buffer , already representing a full command , to process . */
2015-07-26 09:20:46 -04:00
void processInputBuffer ( client * c ) {
2010-10-15 09:40:25 -04:00
/* Keep processing while there is something in the input buffer */
2018-08-13 12:43:36 -04:00
while ( c - > qb_pos < sdslen ( c - > querybuf ) ) {
2014-02-04 09:52:09 -05:00
/* Return if clients are paused. */
2015-07-27 03:41:48 -04:00
if ( ! ( c - > flags & CLIENT_SLAVE ) & & clientsArePaused ( ) ) break ;
2014-02-04 09:52:09 -05:00
2011-07-28 05:20:42 -04:00
/* Immediately abort if the client is in the middle of something. */
2015-07-27 03:41:48 -04:00
if ( c - > flags & CLIENT_BLOCKED ) break ;
2011-07-28 05:20:42 -04:00
2019-04-26 13:29:50 -04:00
/* Don't process more buffers from clients that have already pending
* commands to execute in c - > argv . */
if ( c - > flags & CLIENT_PENDING_COMMAND ) break ;
2018-08-30 11:40:58 -04:00
/* Don't process input from the master while there is a busy script
* condition on the slave . We want just to accumulate the replication
* stream ( instead of replying - BUSY like we do with other clients ) and
* later resume the processing . */
if ( server . lua_timedout & & c - > flags & CLIENT_MASTER ) break ;
2015-07-27 03:41:48 -04:00
/* CLIENT_CLOSE_AFTER_REPLY closes the connection once the reply is
2010-10-28 10:07:45 -04:00
* written to the client . Make sure to not let the reply grow after
2016-08-03 05:12:13 -04:00
* this flag has been set ( i . e . don ' t process more commands ) .
*
* The same applies for clients we want to terminate ASAP . */
if ( c - > flags & ( CLIENT_CLOSE_AFTER_REPLY | CLIENT_CLOSE_ASAP ) ) break ;
2010-10-15 09:40:25 -04:00
/* Determine request type when unknown. */
if ( ! c - > reqtype ) {
2018-08-13 12:43:36 -04:00
if ( c - > querybuf [ c - > qb_pos ] = = ' * ' ) {
2015-07-27 03:41:48 -04:00
c - > reqtype = PROTO_REQ_MULTIBULK ;
2010-06-21 18:07:48 -04:00
} else {
2015-07-27 03:41:48 -04:00
c - > reqtype = PROTO_REQ_INLINE ;
2010-06-21 18:07:48 -04:00
}
}
2010-10-15 09:40:25 -04:00
2015-07-27 03:41:48 -04:00
if ( c - > reqtype = = PROTO_REQ_INLINE ) {
2015-07-26 17:17:55 -04:00
if ( processInlineBuffer ( c ) ! = C_OK ) break ;
2019-02-21 17:13:08 -05:00
/* If the Gopher mode and we got zero or one argument, process
* the request in Gopher mode . */
2019-02-27 16:20:31 -05:00
if ( server . gopher_enabled & &
( ( c - > argc = = 1 & & ( ( char * ) ( c - > argv [ 0 ] - > ptr ) ) [ 0 ] = = ' / ' ) | |
c - > argc = = 0 ) )
{
2019-02-21 17:13:08 -05:00
processGopherRequest ( c ) ;
resetClient ( c ) ;
c - > flags | = CLIENT_CLOSE_AFTER_REPLY ;
break ;
}
2015-07-27 03:41:48 -04:00
} else if ( c - > reqtype = = PROTO_REQ_MULTIBULK ) {
2015-07-26 17:17:55 -04:00
if ( processMultibulkBuffer ( c ) ! = C_OK ) break ;
2010-10-15 09:40:25 -04:00
} else {
2015-07-27 03:41:48 -04:00
serverPanic ( " Unknown request type " ) ;
2010-06-21 18:07:48 -04:00
}
2010-10-15 09:40:25 -04:00
/* Multibulk processing could see a <= 0 length. */
2010-10-15 11:27:05 -04:00
if ( c - > argc = = 0 ) {
resetClient ( c ) ;
} else {
2019-04-26 13:29:50 -04:00
/* If we are in the context of an I/O thread, we can't really
* execute the command here . All we can do is to flag the client
* as one that needs to process the command . */
if ( c - > flags & CLIENT_PENDING_READ ) {
c - > flags | = CLIENT_PENDING_COMMAND ;
break ;
}
2017-07-11 06:33:00 -04:00
2019-04-26 13:29:50 -04:00
/* We are finally ready to execute the command. */
if ( processCommandAndResetClient ( c ) = = C_ERR ) {
/* If the client is no longer valid, we avoid exiting this
* loop and trimming the client buffer later . So we return
* ASAP in that case . */
return ;
Fix PSYNC2 incomplete command bug as described in #3899.
This bug was discovered by @kevinmcgehee and constituted a major hidden
bug in the PSYNC2 implementation, caused by the propagation from the
master of incomplete commands to slaves.
The bug had several results:
1. Borrowing from Kevin text in the issue: "Given that slaves blindly
copy over their master's input into their own replication backlog over
successive read syscalls, it's possible that with large commands or
small TCP buffers, partial commands are present in this buffer. If the
master were to fail before successfully propagating the entire command
to a slave, the slaves will never execute the partial command (since the
client is invalidated) but will copy it to replication backlog which may
relay those invalid bytes to its slaves on PSYNC2, corrupting the
backlog and possibly other valid commands that follow the failover.
Simple command boundaries aren't sufficient to capture this, either,
because in the case of a MULTI/EXEC block, if the master successfully
propagates a subset of the commands but not the EXEC, then the
transaction in the backlog becomes corrupt and could corrupt other
slaves that consume this data."
2. As identified by @yangsiran later, there is another effect of the
bug. For the same mechanism of the first problem, a slave having another
slave, could receive a full resynchronization request with an already
half-applied command in the backlog. Once the RDB is ready, it will be
sent to the slave, and the replication will continue sending to the
sub-slave the other half of the command, which is not valid.
The fix, designed by @yangsiran and @antirez, and implemented by
@antirez, uses a secondary buffer in order to feed the sub-masters and
update the replication backlog and offsets, only when a given part of
the query buffer is actually *applied* to the state of the instance,
that is, when the command gets processed and the command is not pending
in the Redis transaction buffer because of CLIENT_MULTI state.
Given that now the backlog and offsets representation are in agreement
with the actual processed commands, both issue 1 and 2 should no longer
be possible.
Thanks to @kevinmcgehee, @yangsiran and @oranagra for their work in
identifying and designing a fix for this problem.
2017-04-19 04:25:45 -04:00
}
2010-10-15 11:27:05 -04:00
}
2010-06-21 18:07:48 -04:00
}
2018-08-13 12:43:36 -04:00
/* Trim to pos */
2019-04-26 13:29:50 -04:00
if ( c - > qb_pos ) {
2018-08-13 12:43:36 -04:00
sdsrange ( c - > querybuf , c - > qb_pos , - 1 ) ;
c - > qb_pos = 0 ;
}
2010-06-21 18:07:48 -04:00
}
2018-09-03 12:17:25 -04:00
/* This is a wrapper for processInputBuffer that also cares about handling
2019-03-30 06:26:58 -04:00
* the replication forwarding to the sub - replicas , in case the client ' c '
2018-09-03 12:17:25 -04:00
* is flagged as master . Usually you want to call this instead of the
* raw processInputBuffer ( ) . */
void processInputBufferAndReplicate ( client * c ) {
if ( ! ( c - > flags & CLIENT_MASTER ) ) {
processInputBuffer ( c ) ;
} else {
2019-03-30 06:26:58 -04:00
/* If the client is a master we need to compute the difference
* between the applied offset before and after processing the buffer ,
* to understand how much of the replication stream was actually
* applied to the master state : this quantity , and its corresponding
* part of the replication stream , will be propagated to the
* sub - replicas and to the replication backlog . */
2018-09-03 12:17:25 -04:00
size_t prev_offset = c - > reploff ;
processInputBuffer ( c ) ;
size_t applied = c - > reploff - prev_offset ;
if ( applied ) {
replicationFeedSlavesFromMasterStream ( server . slaves ,
c - > pending_querybuf , applied ) ;
sdsrange ( c - > pending_querybuf , applied , - 1 ) ;
}
}
}
2019-09-12 03:56:54 -04:00
void readQueryFromClient ( connection * conn ) {
client * c = connGetPrivateData ( conn ) ;
2011-11-03 10:53:40 -04:00
int nread , readlen ;
2011-11-02 11:52:45 -04:00
size_t qblen ;
2010-06-21 18:07:48 -04:00
2019-03-30 06:26:58 -04:00
/* Check if we want to read from the client later when exiting from
* the event loop . This is the case if threaded I / O is enabled . */
if ( postponeClientRead ( c ) ) return ;
2015-07-27 03:41:48 -04:00
readlen = PROTO_IOBUF_LEN ;
2011-11-03 10:53:40 -04:00
/* If this is a multi bulk request, and we are processing a bulk reply
2012-11-01 06:14:55 -04:00
* that is large enough , try to maximize the probability that the query
* buffer contains exactly the SDS string representing the object , even
* at the risk of requiring more read ( 2 ) calls . This way the function
2011-11-03 10:53:40 -04:00
* processMultiBulkBuffer ( ) can avoid copying buffers to create the
* Redis Object representing the argument . */
2015-07-27 03:41:48 -04:00
if ( c - > reqtype = = PROTO_REQ_MULTIBULK & & c - > multibulklen & & c - > bulklen ! = - 1
& & c - > bulklen > = PROTO_MBULK_BIG_ARG )
2011-11-03 10:53:40 -04:00
{
2017-12-21 04:10:48 -05:00
ssize_t remaining = ( size_t ) ( c - > bulklen + 2 ) - sdslen ( c - > querybuf ) ;
2011-11-03 10:53:40 -04:00
2018-09-04 07:29:24 -04:00
/* Note that the 'remaining' variable may be zero in some edge case,
* for example once we resume a blocked client after CLIENT PAUSE . */
networking: fix unexpected negative or zero readlen
To avoid copying buffers to create a large Redis Object which
exceeding PROTO_IOBUF_LEN 32KB, we just read the remaining data
we need, which may less than PROTO_IOBUF_LEN. But the remaining
len may be zero, if the bulklen+2 equals sdslen(c->querybuf),
in client pause context.
For example:
Time1:
python
>>> import os, socket
>>> server="127.0.0.1"
>>> port=6379
>>> data1="*3\r\n$3\r\nset\r\n$1\r\na\r\n$33000\r\n"
>>> data2="".join("x" for _ in range(33000)) + "\r\n"
>>> data3="\n\n"
>>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.settimeout(10)
>>> s.connect((server, port))
>>> s.send(data1)
28
Time2:
redis-cli client pause 10000
Time3:
>>> s.send(data2)
33002
>>> s.send(data3)
2
>>> s.send(data3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
socket.error: [Errno 104] Connection reset by peer
To fix that, we should check if remaining is greater than zero.
2018-08-30 23:49:27 -04:00
if ( remaining > 0 & & remaining < readlen ) readlen = remaining ;
2011-11-03 10:53:40 -04:00
}
2011-11-02 11:52:45 -04:00
qblen = sdslen ( c - > querybuf ) ;
2012-03-14 10:32:30 -04:00
if ( c - > querybuf_peak < qblen ) c - > querybuf_peak = qblen ;
2011-11-03 10:53:40 -04:00
c - > querybuf = sdsMakeRoomFor ( c - > querybuf , readlen ) ;
2019-09-12 03:56:54 -04:00
nread = connRead ( c - > conn , c - > querybuf + qblen , readlen ) ;
2010-06-21 18:07:48 -04:00
if ( nread = = - 1 ) {
2019-09-12 03:56:54 -04:00
if ( connGetState ( conn ) = = CONN_STATE_CONNECTED ) {
2015-02-26 12:31:06 -05:00
return ;
2010-06-21 18:07:48 -04:00
} else {
2019-09-12 03:56:54 -04:00
serverLog ( LL_VERBOSE , " Reading from client: %s " , connGetLastError ( c - > conn ) ) ;
2019-03-31 15:59:50 -04:00
freeClientAsync ( c ) ;
2010-06-21 18:07:48 -04:00
return ;
}
} else if ( nread = = 0 ) {
2015-07-27 03:41:48 -04:00
serverLog ( LL_VERBOSE , " Client closed connection " ) ;
2019-03-31 15:59:50 -04:00
freeClientAsync ( c ) ;
2010-06-21 18:07:48 -04:00
return ;
Fix PSYNC2 incomplete command bug as described in #3899.
This bug was discovered by @kevinmcgehee and constituted a major hidden
bug in the PSYNC2 implementation, caused by the propagation from the
master of incomplete commands to slaves.
The bug had several results:
1. Borrowing from Kevin text in the issue: "Given that slaves blindly
copy over their master's input into their own replication backlog over
successive read syscalls, it's possible that with large commands or
small TCP buffers, partial commands are present in this buffer. If the
master were to fail before successfully propagating the entire command
to a slave, the slaves will never execute the partial command (since the
client is invalidated) but will copy it to replication backlog which may
relay those invalid bytes to its slaves on PSYNC2, corrupting the
backlog and possibly other valid commands that follow the failover.
Simple command boundaries aren't sufficient to capture this, either,
because in the case of a MULTI/EXEC block, if the master successfully
propagates a subset of the commands but not the EXEC, then the
transaction in the backlog becomes corrupt and could corrupt other
slaves that consume this data."
2. As identified by @yangsiran later, there is another effect of the
bug. For the same mechanism of the first problem, a slave having another
slave, could receive a full resynchronization request with an already
half-applied command in the backlog. Once the RDB is ready, it will be
sent to the slave, and the replication will continue sending to the
sub-slave the other half of the command, which is not valid.
The fix, designed by @yangsiran and @antirez, and implemented by
@antirez, uses a secondary buffer in order to feed the sub-masters and
update the replication backlog and offsets, only when a given part of
the query buffer is actually *applied* to the state of the instance,
that is, when the command gets processed and the command is not pending
in the Redis transaction buffer because of CLIENT_MULTI state.
Given that now the backlog and offsets representation are in agreement
with the actual processed commands, both issue 1 and 2 should no longer
be possible.
Thanks to @kevinmcgehee, @yangsiran and @oranagra for their work in
identifying and designing a fix for this problem.
2017-04-19 04:25:45 -04:00
} else if ( c - > flags & CLIENT_MASTER ) {
/* Append the query buffer to the pending (not applied) buffer
* of the master . We ' ll use this buffer later in order to have a
* copy of the string applied by the last command executed . */
c - > pending_querybuf = sdscatlen ( c - > pending_querybuf ,
c - > querybuf + qblen , nread ) ;
2010-06-21 18:07:48 -04:00
}
2015-02-26 12:31:06 -05:00
sdsIncrLen ( c - > querybuf , nread ) ;
c - > lastinteraction = server . unixtime ;
Fix PSYNC2 incomplete command bug as described in #3899.
This bug was discovered by @kevinmcgehee and constituted a major hidden
bug in the PSYNC2 implementation, caused by the propagation from the
master of incomplete commands to slaves.
The bug had several results:
1. Borrowing from Kevin text in the issue: "Given that slaves blindly
copy over their master's input into their own replication backlog over
successive read syscalls, it's possible that with large commands or
small TCP buffers, partial commands are present in this buffer. If the
master were to fail before successfully propagating the entire command
to a slave, the slaves will never execute the partial command (since the
client is invalidated) but will copy it to replication backlog which may
relay those invalid bytes to its slaves on PSYNC2, corrupting the
backlog and possibly other valid commands that follow the failover.
Simple command boundaries aren't sufficient to capture this, either,
because in the case of a MULTI/EXEC block, if the master successfully
propagates a subset of the commands but not the EXEC, then the
transaction in the backlog becomes corrupt and could corrupt other
slaves that consume this data."
2. As identified by @yangsiran later, there is another effect of the
bug. For the same mechanism of the first problem, a slave having another
slave, could receive a full resynchronization request with an already
half-applied command in the backlog. Once the RDB is ready, it will be
sent to the slave, and the replication will continue sending to the
sub-slave the other half of the command, which is not valid.
The fix, designed by @yangsiran and @antirez, and implemented by
@antirez, uses a secondary buffer in order to feed the sub-masters and
update the replication backlog and offsets, only when a given part of
the query buffer is actually *applied* to the state of the instance,
that is, when the command gets processed and the command is not pending
in the Redis transaction buffer because of CLIENT_MULTI state.
Given that now the backlog and offsets representation are in agreement
with the actual processed commands, both issue 1 and 2 should no longer
be possible.
Thanks to @kevinmcgehee, @yangsiran and @oranagra for their work in
identifying and designing a fix for this problem.
2017-04-19 04:25:45 -04:00
if ( c - > flags & CLIENT_MASTER ) c - > read_reploff + = nread ;
2015-02-26 12:31:06 -05:00
server . stat_net_input_bytes + = nread ;
2011-11-21 10:17:51 -05:00
if ( sdslen ( c - > querybuf ) > server . client_max_querybuf_len ) {
2014-04-28 11:36:57 -04:00
sds ci = catClientInfoString ( sdsempty ( ) , c ) , bytes = sdsempty ( ) ;
2011-11-25 11:08:25 -05:00
bytes = sdscatrepr ( bytes , c - > querybuf , 64 ) ;
2019-04-26 13:29:50 -04:00
serverLog ( LL_WARNING , " Closing client that reached max query buffer length: %s (qbuf initial bytes: %s) " , ci , bytes ) ;
2011-11-21 10:17:51 -05:00
sdsfree ( ci ) ;
2011-11-25 11:08:25 -05:00
sdsfree ( bytes ) ;
2019-03-31 15:59:50 -04:00
freeClientAsync ( c ) ;
2011-11-21 10:17:51 -05:00
return ;
}
Fix PSYNC2 incomplete command bug as described in #3899.
This bug was discovered by @kevinmcgehee and constituted a major hidden
bug in the PSYNC2 implementation, caused by the propagation from the
master of incomplete commands to slaves.
The bug had several results:
1. Borrowing from Kevin text in the issue: "Given that slaves blindly
copy over their master's input into their own replication backlog over
successive read syscalls, it's possible that with large commands or
small TCP buffers, partial commands are present in this buffer. If the
master were to fail before successfully propagating the entire command
to a slave, the slaves will never execute the partial command (since the
client is invalidated) but will copy it to replication backlog which may
relay those invalid bytes to its slaves on PSYNC2, corrupting the
backlog and possibly other valid commands that follow the failover.
Simple command boundaries aren't sufficient to capture this, either,
because in the case of a MULTI/EXEC block, if the master successfully
propagates a subset of the commands but not the EXEC, then the
transaction in the backlog becomes corrupt and could corrupt other
slaves that consume this data."
2. As identified by @yangsiran later, there is another effect of the
bug. For the same mechanism of the first problem, a slave having another
slave, could receive a full resynchronization request with an already
half-applied command in the backlog. Once the RDB is ready, it will be
sent to the slave, and the replication will continue sending to the
sub-slave the other half of the command, which is not valid.
The fix, designed by @yangsiran and @antirez, and implemented by
@antirez, uses a secondary buffer in order to feed the sub-masters and
update the replication backlog and offsets, only when a given part of
the query buffer is actually *applied* to the state of the instance,
that is, when the command gets processed and the command is not pending
in the Redis transaction buffer because of CLIENT_MULTI state.
Given that now the backlog and offsets representation are in agreement
with the actual processed commands, both issue 1 and 2 should no longer
be possible.
Thanks to @kevinmcgehee, @yangsiran and @oranagra for their work in
identifying and designing a fix for this problem.
2017-04-19 04:25:45 -04:00
2019-03-30 06:26:58 -04:00
/* There is more data in the client input buffer, continue parsing it
2019-04-12 11:18:10 -04:00
* in case to check if there is a full command to execute . */
processInputBufferAndReplicate ( c ) ;
2010-06-21 18:07:48 -04:00
}
2011-01-14 04:20:02 -05:00
void getClientsMaxBuffers ( unsigned long * longest_output_list ,
unsigned long * biggest_input_buffer ) {
2015-07-26 09:20:46 -04:00
client * c ;
2011-01-14 04:20:02 -05:00
listNode * ln ;
listIter li ;
unsigned long lol = 0 , bib = 0 ;
listRewind ( server . clients , & li ) ;
while ( ( ln = listNext ( & li ) ) ! = NULL ) {
c = listNodeValue ( ln ) ;
if ( listLength ( c - > reply ) > lol ) lol = listLength ( c - > reply ) ;
if ( sdslen ( c - > querybuf ) > bib ) bib = sdslen ( c - > querybuf ) ;
}
* longest_output_list = lol ;
* biggest_input_buffer = bib ;
}
2013-07-09 06:49:20 -04:00
/* A Redis "Peer ID" is a colon separated ip:port pair.
2014-09-15 13:40:11 -04:00
* For IPv4 it ' s in the form x . y . z . k : port , example : " 127.0.0.1:1234 " .
2013-07-09 06:49:20 -04:00
* For IPv6 addresses we use [ ] around the IP part , like in " [::1]:1234 " .
2014-09-15 13:40:11 -04:00
* For Unix sockets we use path : 0 , like in " /tmp/redis:0 " .
2013-07-09 06:49:20 -04:00
*
2015-07-27 03:41:48 -04:00
* A Peer ID always fits inside a buffer of NET_PEER_ID_LEN bytes , including
2013-07-09 06:49:20 -04:00
* the null term .
*
2013-07-09 09:28:30 -04:00
* On failure the function still populates ' peerid ' with the " ?:0 " string
* in case you want to relax error checking or need to display something
* anyway ( see anetPeerToString implementation for more info ) . */
2015-07-26 09:20:46 -04:00
void genClientPeerId ( client * client , char * peerid ,
2014-10-23 13:09:58 -04:00
size_t peerid_len ) {
2015-07-27 03:41:48 -04:00
if ( client - > flags & CLIENT_UNIX_SOCKET ) {
2013-07-09 06:49:20 -04:00
/* Unix socket client. */
snprintf ( peerid , peerid_len , " %s:0 " , server . unixsocket ) ;
} else {
/* TCP client. */
2019-09-12 03:56:54 -04:00
connFormatPeer ( client - > conn , peerid , peerid_len ) ;
2013-07-09 06:49:20 -04:00
}
}
2014-04-28 11:36:57 -04:00
/* This function returns the client peer id, by creating and caching it
2014-09-15 13:40:11 -04:00
* if client - > peerid is NULL , otherwise returning the cached value .
2014-04-28 11:36:57 -04:00
* The Peer ID never changes during the life of the client , however it
* is expensive to compute . */
2015-07-26 09:20:46 -04:00
char * getClientPeerId ( client * c ) {
2015-07-27 03:41:48 -04:00
char peerid [ NET_PEER_ID_LEN ] ;
2014-04-28 11:36:57 -04:00
if ( c - > peerid = = NULL ) {
genClientPeerId ( c , peerid , sizeof ( peerid ) ) ;
c - > peerid = sdsnew ( peerid ) ;
}
return c - > peerid ;
}
/* Concatenate a string representing the state of a client in an human
* readable format , into the sds string ' s ' . */
2015-07-26 09:20:46 -04:00
sds catClientInfoString ( sds s , client * client ) {
2019-09-12 03:56:54 -04:00
char flags [ 16 ] , events [ 3 ] , conninfo [ CONN_INFO_LEN ] , * p ;
2011-11-21 09:34:32 -05:00
p = flags ;
2015-07-27 03:41:48 -04:00
if ( client - > flags & CLIENT_SLAVE ) {
if ( client - > flags & CLIENT_MONITOR )
2011-11-21 09:34:32 -05:00
* p + + = ' O ' ;
else
* p + + = ' S ' ;
}
2015-07-27 03:41:48 -04:00
if ( client - > flags & CLIENT_MASTER ) * p + + = ' M ' ;
2018-06-28 05:28:38 -04:00
if ( client - > flags & CLIENT_PUBSUB ) * p + + = ' P ' ;
2015-07-27 03:41:48 -04:00
if ( client - > flags & CLIENT_MULTI ) * p + + = ' x ' ;
if ( client - > flags & CLIENT_BLOCKED ) * p + + = ' b ' ;
2019-06-29 20:08:41 -04:00
if ( client - > flags & CLIENT_TRACKING ) * p + + = ' t ' ;
2019-07-03 13:16:20 -04:00
if ( client - > flags & CLIENT_TRACKING_BROKEN_REDIR ) * p + + = ' R ' ;
2015-07-27 03:41:48 -04:00
if ( client - > flags & CLIENT_DIRTY_CAS ) * p + + = ' d ' ;
if ( client - > flags & CLIENT_CLOSE_AFTER_REPLY ) * p + + = ' c ' ;
if ( client - > flags & CLIENT_UNBLOCKED ) * p + + = ' u ' ;
if ( client - > flags & CLIENT_CLOSE_ASAP ) * p + + = ' A ' ;
if ( client - > flags & CLIENT_UNIX_SOCKET ) * p + + = ' U ' ;
if ( client - > flags & CLIENT_READONLY ) * p + + = ' r ' ;
2011-11-21 10:19:30 -05:00
if ( p = = flags ) * p + + = ' N ' ;
2011-11-21 09:34:32 -05:00
* p + + = ' \0 ' ;
2011-11-21 10:06:03 -05:00
p = events ;
2019-09-12 03:56:54 -04:00
if ( client - > conn ) {
if ( connHasReadHandler ( client - > conn ) ) * p + + = ' r ' ;
if ( connHasWriteHandler ( client - > conn ) ) * p + + = ' w ' ;
}
2011-11-21 10:06:03 -05:00
* p = ' \0 ' ;
2014-04-28 11:36:57 -04:00
return sdscatfmt ( s ,
2019-09-12 03:56:54 -04:00
" id=%U addr=%s %s name=%s age=%I idle=%I flags=%s db=%i sub=%i psub=%i multi=%i qbuf=%U qbuf-free=%U obl=%U oll=%U omem=%U events=%s cmd=%s user=%s " ,
2014-06-16 08:22:55 -04:00
( unsigned long long ) client - > id ,
2014-04-28 11:36:57 -04:00
getClientPeerId ( client ) ,
2019-09-12 03:56:54 -04:00
connGetInfo ( client - > conn , conninfo , sizeof ( conninfo ) ) ,
2013-01-11 12:43:28 -05:00
client - > name ? ( char * ) client - > name - > ptr : " " ,
2014-04-28 10:41:38 -04:00
( long long ) ( server . unixtime - client - > ctime ) ,
( long long ) ( server . unixtime - client - > lastinteraction ) ,
2011-11-21 09:34:32 -05:00
flags ,
client - > db - > id ,
( int ) dictSize ( client - > pubsub_channels ) ,
2011-11-21 09:54:49 -05:00
( int ) listLength ( client - > pubsub_patterns ) ,
2015-07-27 03:41:48 -04:00
( client - > flags & CLIENT_MULTI ) ? client - > mstate . count : - 1 ,
2014-04-28 10:41:38 -04:00
( unsigned long long ) sdslen ( client - > querybuf ) ,
( unsigned long long ) sdsavail ( client - > querybuf ) ,
( unsigned long long ) client - > bufpos ,
( unsigned long long ) listLength ( client - > reply ) ,
( unsigned long long ) getClientOutputBufferMemoryUsage ( client ) ,
2011-11-24 08:56:34 -05:00
events ,
2019-02-12 03:03:58 -05:00
client - > lastcmd ? client - > lastcmd - > name : " NULL " ,
2019-02-12 03:44:25 -05:00
client - > user ? client - > user - > name : " (superuser) " ) ;
2011-11-21 09:34:32 -05:00
}
2018-06-28 05:43:05 -04:00
sds getAllClientsInfoString ( int type ) {
2011-11-24 09:04:42 -05:00
listNode * ln ;
listIter li ;
2015-07-26 09:20:46 -04:00
client * client ;
2017-02-23 06:04:08 -05:00
sds o = sdsnewlen ( SDS_NOINIT , 200 * listLength ( server . clients ) ) ;
2016-04-25 09:48:09 -04:00
sdsclear ( o ) ;
2011-11-24 09:04:42 -05:00
listRewind ( server . clients , & li ) ;
while ( ( ln = listNext ( & li ) ) ! = NULL ) {
client = listNodeValue ( ln ) ;
2018-06-28 05:43:05 -04:00
if ( type ! = - 1 & & getClientType ( client ) ! = type ) continue ;
2014-04-28 11:36:57 -04:00
o = catClientInfoString ( o , client ) ;
2011-11-24 09:04:42 -05:00
o = sdscatlen ( o , " \n " , 1 ) ;
}
return o ;
}
2019-02-25 10:51:49 -05:00
/* This function implements CLIENT SETNAME, including replying to the
* user with an error if the charset is wrong ( in that case C_ERR is
* returned ) . If the function succeeeded C_OK is returned , and it ' s up
* to the caller to send a reply if needed .
*
* Setting an empty string as name has the effect of unsetting the
* currently set name : the client will remain unnamed .
*
* This function is also used to implement the HELLO SETNAME option . */
int clientSetNameOrReply ( client * c , robj * name ) {
int len = sdslen ( name - > ptr ) ;
char * p = name - > ptr ;
/* Setting the client name to an empty string actually removes
* the current name . */
if ( len = = 0 ) {
if ( c - > name ) decrRefCount ( c - > name ) ;
c - > name = NULL ;
addReply ( c , shared . ok ) ;
return C_OK ;
}
/* Otherwise check if the charset is ok. We need to do this otherwise
* CLIENT LIST format will break . You should always be able to
* split by space to get the different fields . */
for ( int j = 0 ; j < len ; j + + ) {
if ( p [ j ] < ' ! ' | | p [ j ] > ' ~ ' ) { /* ASCII is assumed. */
addReplyError ( c ,
" Client names cannot contain spaces, "
" newlines or special characters. " ) ;
return C_ERR ;
}
}
if ( c - > name ) decrRefCount ( c - > name ) ;
c - > name = name ;
incrRefCount ( name ) ;
return C_OK ;
}
2015-07-26 09:20:46 -04:00
void clientCommand ( client * c ) {
2011-04-21 09:47:47 -04:00
listNode * ln ;
listIter li ;
2015-07-26 09:20:46 -04:00
client * client ;
2011-04-21 09:47:47 -04:00
2017-12-03 09:49:29 -05:00
if ( c - > argc = = 2 & & ! strcasecmp ( c - > argv [ 1 ] - > ptr , " help " ) ) {
const char * help [ ] = {
2019-07-10 12:17:07 -04:00
" ID -- Return the ID of the current connection. " ,
" GETNAME -- Return the name of the current connection. " ,
" KILL <ip:port> -- Kill connection made from <ip:port>. " ,
" KILL <option> <value> [option value ...] -- Kill connections. Options are: " ,
" ADDR <ip:port> -- Kill connection made from <ip:port> " ,
" TYPE (normal|master|replica|pubsub) -- Kill connections by type. " ,
" SKIPME (yes|no) -- Skip killing current connection (default: yes). " ,
" LIST [options ...] -- Return information about client connections. Options: " ,
" TYPE (normal|master|replica|pubsub) -- Return clients of specified type. " ,
" PAUSE <timeout> -- Suspend all Redis clients for <timout> milliseconds. " ,
" REPLY (on|off|skip) -- Control the replies sent to the current connection. " ,
" SETNAME <name> -- Assign the name <name> to the current connection. " ,
" UNBLOCK <clientid> [TIMEOUT|ERROR] -- Unblock the specified blocked client. " ,
" TRACKING (on|off) [REDIRECT <id>] -- Enable client keys tracking for client side caching. " ,
" GETREDIR -- Return the client ID we are redirecting to when tracking is enabled. " ,
2017-12-06 06:05:11 -05:00
NULL
2017-12-03 09:49:29 -05:00
} ;
addReplyHelp ( c , help ) ;
2018-06-27 07:11:50 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " id " ) & & c - > argc = = 2 ) {
/* CLIENT ID */
addReplyLongLong ( c , c - > id ) ;
2018-06-28 05:43:05 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " list " ) ) {
2014-06-16 08:24:28 -04:00
/* CLIENT LIST */
2018-06-28 05:43:05 -04:00
int type = - 1 ;
if ( c - > argc = = 4 & & ! strcasecmp ( c - > argv [ 2 ] - > ptr , " type " ) ) {
type = getClientTypeByName ( c - > argv [ 3 ] - > ptr ) ;
if ( type = = - 1 ) {
addReplyErrorFormat ( c , " Unknown client type '%s' " ,
( char * ) c - > argv [ 3 ] - > ptr ) ;
return ;
}
} else if ( c - > argc ! = 2 ) {
addReply ( c , shared . syntaxerr ) ;
return ;
}
sds o = getAllClientsInfoString ( type ) ;
2019-09-18 12:52:13 -04:00
addReplyVerbatim ( c , o , sdslen ( o ) , " txt " ) ;
2011-04-21 09:38:02 -04:00
sdsfree ( o ) ;
CLIENT REPLY command implemented: ON, OFF and SKIP modes.
Sometimes it can be useful for clients to completely disable replies
from the Redis server. For example when the client sends fire and forget
commands or performs a mass loading of data, or in caching contexts
where new data is streamed constantly. In such contexts to use server
time and bandwidth in order to send back replies to clients, which are
going to be ignored, is a shame.
Multiple mechanisms are possible to implement such a feature. For
example it could be a feature of MULTI/EXEC, or a command prefix
such as "NOREPLY SADD myset foo", or a different mechanism that allows
to switch on/off requests using the CLIENT command.
The MULTI/EXEC approach has the problem that transactions are not
strictly part of the no-reply semantics, and if we want to insert a lot
of data in a bulk way, creating a huge MULTI/EXEC transaction in the
server memory is bad.
The prefix is the best in this specific use case since it does not allow
desynchronizations, and is pretty clear semantically. However Redis
internals and client libraries are not prepared to handle this
currently.
So the implementation uses the CLIENT command, providing a new REPLY
subcommand with three options:
CLIENT REPLY OFF disables the replies, and does not reply itself.
CLIENT REPLY ON re-enables the replies, replying +OK.
CLIENT REPLY SKIP only discards the reply of the next command, and
like OFF does not reply anything itself.
The reason to add the SKIP command is that it allows to have an easy
way to send conceptually "single" commands that don't need a reply
as the sum of two pipelined commands:
CLIENT REPLY SKIP
SET key value
Note that CLIENT REPLY ON replies with +OK so it should be used when
sending multiple commands that don't need a reply. However since it
replies with +OK the client can check that the connection is still
active and all the previous commands were received.
This is currently just into Redis "unstable" so the proposal can be
modified or abandoned based on users inputs.
2015-10-21 14:43:37 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " reply " ) & & c - > argc = = 3 ) {
/* CLIENT REPLY ON|OFF|SKIP */
if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , " on " ) ) {
c - > flags & = ~ ( CLIENT_REPLY_SKIP | CLIENT_REPLY_OFF ) ;
addReply ( c , shared . ok ) ;
} else if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , " off " ) ) {
c - > flags | = CLIENT_REPLY_OFF ;
} else if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , " skip " ) ) {
if ( ! ( c - > flags & CLIENT_REPLY_OFF ) )
c - > flags | = CLIENT_REPLY_SKIP_NEXT ;
} else {
addReply ( c , shared . syntaxerr ) ;
return ;
}
2014-06-16 08:24:28 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " kill " ) ) {
/* CLIENT KILL <ip:port>
2014-06-16 08:50:15 -04:00
* CLIENT KILL < option > [ value ] . . . < option > [ value ] */
2014-06-16 08:24:28 -04:00
char * addr = NULL ;
int type = - 1 ;
uint64_t id = 0 ;
2014-06-16 08:50:15 -04:00
int skipme = 1 ;
int killed = 0 , close_this_client = 0 ;
2014-06-16 08:24:28 -04:00
if ( c - > argc = = 3 ) {
2014-06-16 08:50:15 -04:00
/* Old style syntax: CLIENT KILL <addr> */
2014-06-16 08:24:28 -04:00
addr = c - > argv [ 2 ] - > ptr ;
2014-06-24 06:49:18 -04:00
skipme = 0 ; /* With the old form, you can kill yourself. */
2014-06-16 08:50:15 -04:00
} else if ( c - > argc > 3 ) {
int i = 2 ; /* Next option index. */
/* New style syntax: parse options. */
while ( i < c - > argc ) {
int moreargs = c - > argc > i + 1 ;
if ( ! strcasecmp ( c - > argv [ i ] - > ptr , " id " ) & & moreargs ) {
long long tmp ;
if ( getLongLongFromObjectOrReply ( c , c - > argv [ i + 1 ] , & tmp , NULL )
2015-07-26 17:17:55 -04:00
! = C_OK ) return ;
2014-06-16 08:50:15 -04:00
id = tmp ;
} else if ( ! strcasecmp ( c - > argv [ i ] - > ptr , " type " ) & & moreargs ) {
type = getClientTypeByName ( c - > argv [ i + 1 ] - > ptr ) ;
if ( type = = - 1 ) {
addReplyErrorFormat ( c , " Unknown client type '%s' " ,
( char * ) c - > argv [ i + 1 ] - > ptr ) ;
return ;
}
} else if ( ! strcasecmp ( c - > argv [ i ] - > ptr , " addr " ) & & moreargs ) {
addr = c - > argv [ i + 1 ] - > ptr ;
} else if ( ! strcasecmp ( c - > argv [ i ] - > ptr , " skipme " ) & & moreargs ) {
if ( ! strcasecmp ( c - > argv [ i + 1 ] - > ptr , " yes " ) ) {
skipme = 1 ;
} else if ( ! strcasecmp ( c - > argv [ i + 1 ] - > ptr , " no " ) ) {
skipme = 0 ;
} else {
addReply ( c , shared . syntaxerr ) ;
return ;
}
} else {
addReply ( c , shared . syntaxerr ) ;
2014-06-16 08:24:28 -04:00
return ;
}
2014-06-16 08:50:15 -04:00
i + = 2 ;
2014-06-16 08:24:28 -04:00
}
} else {
addReply ( c , shared . syntaxerr ) ;
return ;
}
/* Iterate clients killing all the matching clients. */
2011-04-21 09:47:47 -04:00
listRewind ( server . clients , & li ) ;
while ( ( ln = listNext ( & li ) ) ! = NULL ) {
client = listNodeValue ( ln ) ;
2014-06-16 08:24:28 -04:00
if ( addr & & strcmp ( getClientPeerId ( client ) , addr ) ! = 0 ) continue ;
2015-07-28 11:01:19 -04:00
if ( type ! = - 1 & & getClientType ( client ) ! = type ) continue ;
2014-06-16 08:24:28 -04:00
if ( id ! = 0 & & client - > id ! = id ) continue ;
2014-06-16 08:50:15 -04:00
if ( c = = client & & skipme ) continue ;
2014-06-16 08:24:28 -04:00
/* Kill it. */
if ( c = = client ) {
2014-06-16 08:28:23 -04:00
close_this_client = 1 ;
2014-06-16 08:24:28 -04:00
} else {
freeClient ( client ) ;
2011-04-21 09:47:47 -04:00
}
2014-06-16 08:24:28 -04:00
killed + + ;
}
/* Reply according to old/new format. */
if ( c - > argc = = 3 ) {
if ( killed = = 0 )
addReplyError ( c , " No such client " ) ;
else
addReply ( c , shared . ok ) ;
} else {
addReplyLongLong ( c , killed ) ;
2011-04-21 09:47:47 -04:00
}
2014-06-16 08:28:23 -04:00
/* If this client has to be closed, flag it as CLOSE_AFTER_REPLY
* only after we queued the reply to its output buffers . */
2015-07-27 03:41:48 -04:00
if ( close_this_client ) c - > flags | = CLIENT_CLOSE_AFTER_REPLY ;
2018-06-27 12:51:06 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " unblock " ) & & ( c - > argc = = 3 | |
c - > argc = = 4 ) )
{
/* CLIENT UNBLOCK <id> [timeout|error] */
2018-06-27 07:52:46 -04:00
long long id ;
2018-06-27 12:51:06 -04:00
int unblock_error = 0 ;
if ( c - > argc = = 4 ) {
if ( ! strcasecmp ( c - > argv [ 3 ] - > ptr , " timeout " ) ) {
unblock_error = 0 ;
} else if ( ! strcasecmp ( c - > argv [ 3 ] - > ptr , " error " ) ) {
unblock_error = 1 ;
} else {
addReplyError ( c ,
" CLIENT UNBLOCK reason should be TIMEOUT or ERROR " ) ;
return ;
}
}
2018-06-27 07:52:46 -04:00
if ( getLongLongFromObjectOrReply ( c , c - > argv [ 2 ] , & id , NULL )
! = C_OK ) return ;
struct client * target = lookupClientByID ( id ) ;
if ( target & & target - > flags & CLIENT_BLOCKED ) {
2018-06-27 12:51:06 -04:00
if ( unblock_error )
addReplyError ( target ,
" -UNBLOCKED client unblocked via CLIENT UNBLOCK " ) ;
else
replyToBlockedClientTimedOut ( target ) ;
2018-06-27 07:52:46 -04:00
unblockClient ( target ) ;
addReply ( c , shared . cone ) ;
} else {
addReply ( c , shared . czero ) ;
}
2013-01-11 12:43:28 -05:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " setname " ) & & c - > argc = = 3 ) {
2019-07-03 05:58:20 -04:00
/* CLIENT SETNAME */
2019-02-25 10:51:49 -05:00
if ( clientSetNameOrReply ( c , c - > argv [ 2 ] ) = = C_OK )
2013-01-11 12:43:28 -05:00
addReply ( c , shared . ok ) ;
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " getname " ) & & c - > argc = = 2 ) {
2019-07-03 05:58:20 -04:00
/* CLIENT GETNAME */
2013-01-11 12:43:28 -05:00
if ( c - > name )
addReplyBulk ( c , c - > name ) ;
else
2018-11-30 03:41:54 -05:00
addReplyNull ( c ) ;
2014-02-04 09:52:09 -05:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " pause " ) & & c - > argc = = 3 ) {
2019-07-03 05:58:20 -04:00
/* CLIENT PAUSE */
2014-02-04 09:52:09 -05:00
long long duration ;
2019-07-03 05:58:20 -04:00
if ( getTimeoutFromObjectOrReply ( c , c - > argv [ 2 ] , & duration ,
UNIT_MILLISECONDS ) ! = C_OK ) return ;
2014-02-04 09:52:09 -05:00
pauseClients ( duration ) ;
addReply ( c , shared . ok ) ;
2019-07-03 05:58:20 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " tracking " ) & &
( c - > argc = = 3 | | c - > argc = = 5 ) )
{
/* CLIENT TRACKING (on|off) [REDIRECT <id>] */
long long redir = 0 ;
/* Parse the redirection option: we'll require the client with
* the specified ID to exist right now , even if it is possible
* it will get disconnected later . */
if ( c - > argc = = 5 ) {
if ( strcasecmp ( c - > argv [ 3 ] - > ptr , " redirect " ) ! = 0 ) {
addReply ( c , shared . syntaxerr ) ;
return ;
} else {
if ( getLongLongFromObjectOrReply ( c , c - > argv [ 4 ] , & redir , NULL ) ! =
C_OK ) return ;
if ( lookupClientByID ( redir ) = = NULL ) {
addReplyError ( c , " The client ID you want redirect to "
" does not exist " ) ;
return ;
}
}
}
if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , " on " ) ) {
enableTracking ( c , redir ) ;
} else if ( ! strcasecmp ( c - > argv [ 2 ] - > ptr , " off " ) ) {
disableTracking ( c ) ;
} else {
addReply ( c , shared . syntaxerr ) ;
return ;
}
addReply ( c , shared . ok ) ;
2019-07-10 12:17:07 -04:00
} else if ( ! strcasecmp ( c - > argv [ 1 ] - > ptr , " getredir " ) & & c - > argc = = 2 ) {
/* CLIENT GETREDIR */
if ( c - > flags & CLIENT_TRACKING ) {
addReplyLongLong ( c , c - > client_tracking_redirection ) ;
} else {
addReplyLongLong ( c , - 1 ) ;
}
2011-04-21 09:38:02 -04:00
} else {
2017-12-06 06:05:11 -05:00
addReplyErrorFormat ( c , " Unknown subcommand or wrong number of arguments for '%s'. Try CLIENT HELP " , ( char * ) c - > argv [ 1 ] - > ptr ) ;
}
2011-04-21 09:38:02 -04:00
}
2011-06-20 11:07:18 -04:00
2019-02-25 10:33:36 -05:00
/* HELLO <protocol-version> [AUTH <user> <password>] [SETNAME <name>] */
2018-12-04 06:46:16 -05:00
void helloCommand ( client * c ) {
long long ver ;
if ( getLongLongFromObject ( c - > argv [ 1 ] , & ver ) ! = C_OK | |
ver < 2 | | ver > 3 )
{
addReplyError ( c , " -NOPROTO unsupported protocol version " ) ;
return ;
}
2019-02-25 10:55:16 -05:00
for ( int j = 2 ; j < c - > argc ; j + + ) {
int moreargs = ( c - > argc - 1 ) - j ;
const char * opt = c - > argv [ j ] - > ptr ;
if ( ! strcasecmp ( opt , " AUTH " ) & & moreargs > = 2 ) {
if ( ACLAuthenticateUser ( c , c - > argv [ j + 1 ] , c - > argv [ j + 2 ] ) = = C_ERR ) {
addReplyError ( c , " -WRONGPASS invalid username-password pair " ) ;
return ;
}
j + = 2 ;
2019-02-25 10:56:58 -05:00
} else if ( ! strcasecmp ( opt , " SETNAME " ) & & moreargs ) {
if ( clientSetNameOrReply ( c , c - > argv [ j + 1 ] ) = = C_ERR ) return ;
j + + ;
2019-02-25 10:55:16 -05:00
} else {
addReplyErrorFormat ( c , " Syntax error in HELLO option '%s' " , opt ) ;
return ;
}
}
2018-12-21 11:24:14 -05:00
/* At this point we need to be authenticated to continue. */
if ( ! c - > authenticated ) {
addReplyError ( c , " -NOAUTH HELLO must be called with the client already "
" authenticated, otherwise the HELLO AUTH <user> <pass> "
" option can be used to authenticate the client and "
" select the RESP protocol version at the same time " ) ;
return ;
}
2019-02-25 10:40:58 -05:00
/* Let's switch to the specified RESP mode. */
c - > resp = ver ;
2018-12-04 06:46:16 -05:00
addReplyMapLen ( c , 7 ) ;
addReplyBulkCString ( c , " server " ) ;
addReplyBulkCString ( c , " redis " ) ;
addReplyBulkCString ( c , " version " ) ;
addReplyBulkCString ( c , REDIS_VERSION ) ;
addReplyBulkCString ( c , " proto " ) ;
addReplyLongLong ( c , 3 ) ;
addReplyBulkCString ( c , " id " ) ;
addReplyLongLong ( c , c - > id ) ;
addReplyBulkCString ( c , " mode " ) ;
if ( server . sentinel_mode ) addReplyBulkCString ( c , " sentinel " ) ;
if ( server . cluster_enabled ) addReplyBulkCString ( c , " cluster " ) ;
else addReplyBulkCString ( c , " standalone " ) ;
if ( ! server . sentinel_mode ) {
addReplyBulkCString ( c , " role " ) ;
addReplyBulkCString ( c , server . masterhost ? " replica " : " master " ) ;
}
addReplyBulkCString ( c , " modules " ) ;
addReplyLoadedModules ( c ) ;
}
2016-08-03 05:12:13 -04:00
/* This callback is bound to POST and "Host:" command names. Those are not
* really commands , but are used in security attacks in order to talk to
* Redis instances via HTTP , with a technique called " cross protocol scripting "
* which exploits the fact that services like Redis will discard invalid
* HTTP headers and will process what follows .
*
* As a protection against this attack , Redis will terminate the connection
* when a POST or " Host: " header is seen , and will log the event from
* time to time ( to avoid creating a DOS as a result of too many logs ) . */
void securityWarningCommand ( client * c ) {
static time_t logged_time ;
time_t now = time ( NULL ) ;
if ( labs ( now - logged_time ) > 60 ) {
serverLog ( LL_WARNING , " Possible SECURITY ATTACK detected. It looks like somebody is sending POST or Host: commands to Redis. This is likely due to an attacker attempting to use Cross Protocol Scripting to compromise your Redis instance. Connection aborted. " ) ;
logged_time = now ;
}
freeClientAsync ( c ) ;
}
2011-07-13 09:38:03 -04:00
/* Rewrite the command vector of the client. All the new objects ref count
* is incremented . The old command vector is freed , and the old objects
* ref count is decremented . */
2015-07-26 09:20:46 -04:00
void rewriteClientCommandVector ( client * c , int argc , . . . ) {
2011-06-20 11:07:18 -04:00
va_list ap ;
int j ;
robj * * argv ; /* The new argument vector */
argv = zmalloc ( sizeof ( robj * ) * argc ) ;
va_start ( ap , argc ) ;
for ( j = 0 ; j < argc ; j + + ) {
robj * a ;
2014-06-26 12:48:40 -04:00
2011-06-20 11:07:18 -04:00
a = va_arg ( ap , robj * ) ;
argv [ j ] = a ;
incrRefCount ( a ) ;
}
/* We free the objects in the original vector at the end, so we are
* sure that if the same objects are reused in the new vector the
* refcount gets incremented before it gets decremented . */
for ( j = 0 ; j < c - > argc ; j + + ) decrRefCount ( c - > argv [ j ] ) ;
zfree ( c - > argv ) ;
/* Replace argv and argc with our new versions. */
c - > argv = argv ;
c - > argc = argc ;
2013-03-06 10:28:26 -05:00
c - > cmd = lookupCommandOrOriginal ( c - > argv [ 0 ] - > ptr ) ;
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( c , NULL , c - > cmd ! = NULL ) ;
2011-06-20 11:07:18 -04:00
va_end ( ap ) ;
}
2011-07-13 09:38:03 -04:00
2015-06-23 04:18:23 -04:00
/* Completely replace the client command vector with the provided one. */
2015-07-26 09:20:46 -04:00
void replaceClientCommandVector ( client * c , int argc , robj * * argv ) {
2015-06-23 04:18:23 -04:00
freeClientArgv ( c ) ;
zfree ( c - > argv ) ;
c - > argv = argv ;
c - > argc = argc ;
c - > cmd = lookupCommandOrOriginal ( c - > argv [ 0 ] - > ptr ) ;
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( c , NULL , c - > cmd ! = NULL ) ;
2015-06-23 04:18:23 -04:00
}
2011-07-13 09:38:03 -04:00
/* Rewrite a single item in the command vector.
2015-12-11 07:48:41 -05:00
* The new val ref count is incremented , and the old decremented .
*
* It is possible to specify an argument over the current size of the
* argument vector : in this case the array of objects gets reallocated
* and c - > argc set to the max value . However it ' s up to the caller to
*
* 1. Make sure there are no " holes " and all the arguments are set .
* 2. If the original argument vector was longer than the one we
* want to end with , it ' s up to the caller to set c - > argc and
* free the no longer used objects on c - > argv . */
2015-07-26 09:20:46 -04:00
void rewriteClientCommandArgument ( client * c , int i , robj * newval ) {
2011-07-13 09:38:03 -04:00
robj * oldval ;
2014-06-26 12:48:40 -04:00
2015-12-11 07:48:41 -05:00
if ( i > = c - > argc ) {
c - > argv = zrealloc ( c - > argv , sizeof ( robj * ) * ( i + 1 ) ) ;
c - > argc = i + 1 ;
c - > argv [ i ] = NULL ;
}
2011-07-13 09:38:03 -04:00
oldval = c - > argv [ i ] ;
c - > argv [ i ] = newval ;
incrRefCount ( newval ) ;
2015-12-11 07:48:41 -05:00
if ( oldval ) decrRefCount ( oldval ) ;
2011-07-13 09:38:03 -04:00
/* If this is the command name make sure to fix c->cmd. */
if ( i = = 0 ) {
2013-03-06 10:28:26 -05:00
c - > cmd = lookupCommandOrOriginal ( c - > argv [ 0 ] - > ptr ) ;
2015-07-26 09:29:53 -04:00
serverAssertWithInfo ( c , NULL , c - > cmd ! = NULL ) ;
2011-07-13 09:38:03 -04:00
}
}
2011-12-25 10:32:54 -05:00
2019-05-13 11:30:02 -04:00
/* This function returns the number of bytes that Redis is
2011-12-25 10:32:54 -05:00
* using to store the reply still not read by the client .
*
* Note : this function is very fast so can be called as many time as
* the caller wishes . The main usage of this function currently is
2012-01-24 09:33:15 -05:00
* enforcing the client output length limits . */
2015-07-26 09:20:46 -04:00
unsigned long getClientOutputBufferMemoryUsage ( client * c ) {
2018-02-21 13:18:34 -05:00
unsigned long list_item_size = sizeof ( listNode ) + sizeof ( clientReplyBlock ) ;
2011-12-25 10:32:54 -05:00
return c - > reply_bytes + ( list_item_size * listLength ( c - > reply ) ) ;
}
2012-01-17 06:43:01 -05:00
2012-11-01 06:14:55 -04:00
/* Get the class of a client, used in order to enforce limits to different
2012-01-17 06:43:01 -05:00
* classes of clients .
*
* The function will return one of the following :
2015-07-27 03:41:48 -04:00
* CLIENT_TYPE_NORMAL - > Normal client
* CLIENT_TYPE_SLAVE - > Slave or client executing MONITOR command
* CLIENT_TYPE_PUBSUB - > Client subscribed to Pub / Sub channels
2015-07-28 10:58:04 -04:00
* CLIENT_TYPE_MASTER - > The client representing our replication master .
2012-01-17 06:43:01 -05:00
*/
2015-07-26 09:20:46 -04:00
int getClientType ( client * c ) {
2015-07-28 10:58:04 -04:00
if ( c - > flags & CLIENT_MASTER ) return CLIENT_TYPE_MASTER ;
2015-07-27 03:41:48 -04:00
if ( ( c - > flags & CLIENT_SLAVE ) & & ! ( c - > flags & CLIENT_MONITOR ) )
return CLIENT_TYPE_SLAVE ;
2015-07-28 10:58:04 -04:00
if ( c - > flags & CLIENT_PUBSUB ) return CLIENT_TYPE_PUBSUB ;
2015-07-27 03:41:48 -04:00
return CLIENT_TYPE_NORMAL ;
2012-01-17 06:43:01 -05:00
}
2012-01-23 10:12:37 -05:00
2014-06-16 04:43:05 -04:00
int getClientTypeByName ( char * name ) {
2015-07-27 03:41:48 -04:00
if ( ! strcasecmp ( name , " normal " ) ) return CLIENT_TYPE_NORMAL ;
else if ( ! strcasecmp ( name , " slave " ) ) return CLIENT_TYPE_SLAVE ;
2018-09-10 06:27:37 -04:00
else if ( ! strcasecmp ( name , " replica " ) ) return CLIENT_TYPE_SLAVE ;
2015-07-27 03:41:48 -04:00
else if ( ! strcasecmp ( name , " pubsub " ) ) return CLIENT_TYPE_PUBSUB ;
2015-07-28 10:58:04 -04:00
else if ( ! strcasecmp ( name , " master " ) ) return CLIENT_TYPE_MASTER ;
2012-01-24 04:43:30 -05:00
else return - 1 ;
}
2014-06-16 04:43:05 -04:00
char * getClientTypeName ( int class ) {
2012-01-24 04:43:30 -05:00
switch ( class ) {
2015-07-27 03:41:48 -04:00
case CLIENT_TYPE_NORMAL : return " normal " ;
case CLIENT_TYPE_SLAVE : return " slave " ;
case CLIENT_TYPE_PUBSUB : return " pubsub " ;
2015-07-28 10:58:04 -04:00
case CLIENT_TYPE_MASTER : return " master " ;
2014-06-16 04:43:05 -04:00
default : return NULL ;
2012-01-24 04:43:30 -05:00
}
}
2012-01-23 10:12:37 -05:00
/* The function checks if the client reached output buffer soft or hard
* limit , and also update the state needed to check the soft limit as
* a side effect .
*
* Return value : non - zero if the client reached the soft or the hard limit .
* Otherwise zero is returned . */
2015-07-26 09:20:46 -04:00
int checkClientOutputBufferLimits ( client * c ) {
2012-01-23 10:12:37 -05:00
int soft = 0 , hard = 0 , class ;
unsigned long used_mem = getClientOutputBufferMemoryUsage ( c ) ;
2014-06-16 04:43:05 -04:00
class = getClientType ( c ) ;
2015-07-28 10:58:04 -04:00
/* For the purpose of output buffer limiting, masters are handled
* like normal clients . */
if ( class = = CLIENT_TYPE_MASTER ) class = CLIENT_TYPE_NORMAL ;
2012-01-23 10:12:37 -05:00
if ( server . client_obuf_limits [ class ] . hard_limit_bytes & &
used_mem > = server . client_obuf_limits [ class ] . hard_limit_bytes )
hard = 1 ;
if ( server . client_obuf_limits [ class ] . soft_limit_bytes & &
used_mem > = server . client_obuf_limits [ class ] . soft_limit_bytes )
soft = 1 ;
/* We need to check if the soft limit is reached continuously for the
* specified amount of seconds . */
if ( soft ) {
if ( c - > obuf_soft_limit_reached_time = = 0 ) {
c - > obuf_soft_limit_reached_time = server . unixtime ;
soft = 0 ; /* First time we see the soft limit reached */
} else {
time_t elapsed = server . unixtime - c - > obuf_soft_limit_reached_time ;
if ( elapsed < =
server . client_obuf_limits [ class ] . soft_limit_seconds ) {
soft = 0 ; /* The client still did not reached the max number of
seconds for the soft limit to be considered
reached . */
}
}
} else {
c - > obuf_soft_limit_reached_time = 0 ;
}
return soft | | hard ;
}
/* Asynchronously close a client if soft or hard limit is reached on the
2012-01-24 03:32:39 -05:00
* output buffer size . The caller can check if the client will be closed
2015-07-27 03:41:48 -04:00
* checking if the client CLIENT_CLOSE_ASAP flag is set .
2012-01-23 10:12:37 -05:00
*
* Note : we need to close the client asynchronously because this function is
* called from contexts where the client can ' t be freed safely , i . e . from the
* lower level functions pushing data inside the client output buffers . */
2015-07-26 09:20:46 -04:00
void asyncCloseClientOnOutputBufferLimitReached ( client * c ) {
2019-09-12 03:56:54 -04:00
if ( ! c - > conn ) return ; /* It is unsafe to free fake clients. */
2015-07-26 09:29:53 -04:00
serverAssert ( c - > reply_bytes < SIZE_MAX - ( 1024 * 64 ) ) ;
2015-07-27 03:41:48 -04:00
if ( c - > reply_bytes = = 0 | | c - > flags & CLIENT_CLOSE_ASAP ) return ;
2012-01-23 10:12:37 -05:00
if ( checkClientOutputBufferLimits ( c ) ) {
2014-04-28 11:36:57 -04:00
sds client = catClientInfoString ( sdsempty ( ) , c ) ;
2012-01-23 10:12:37 -05:00
freeClientAsync ( c ) ;
2015-07-27 03:41:48 -04:00
serverLog ( LL_WARNING , " Client %s scheduled to be closed ASAP for overcoming of output buffer limits. " , client ) ;
2012-01-23 10:12:37 -05:00
sdsfree ( client ) ;
}
}
2012-02-06 10:56:42 -05:00
/* Helper function used by freeMemoryIfNeeded() in order to flush slaves
2015-11-09 11:26:56 -05:00
* output buffers without returning control to the event loop .
* This is also called by SHUTDOWN for a best - effort attempt to send
* slaves the latest writes . */
2012-02-06 10:56:42 -05:00
void flushSlavesOutputBuffers ( void ) {
listIter li ;
listNode * ln ;
listRewind ( server . slaves , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
2015-07-26 09:20:46 -04:00
client * slave = listNodeValue ( ln ) ;
2019-09-12 03:56:54 -04:00
int can_receive_writes = connHasWriteHandler ( slave - > conn ) | |
2019-08-31 08:46:21 -04:00
( slave - > flags & CLIENT_PENDING_WRITE ) ;
2012-02-06 10:56:42 -05:00
2019-08-31 08:46:21 -04:00
/* We don't want to send the pending data to the replica in a few
* cases :
*
* 1. For some reason there is neither the write handler installed
* nor the client is flagged as to have pending writes : for some
* reason this replica may not be set to receive data . This is
* just for the sake of defensive programming .
*
* 2. The put_online_on_ack flag is true . To know why we don ' t want
* to send data to the replica in this case , please grep for the
* flag for this flag .
*
* 3. Obviously if the slave is not ONLINE .
*/
2019-08-02 05:17:19 -04:00
if ( slave - > replstate = = SLAVE_STATE_ONLINE & &
2019-08-31 08:46:21 -04:00
can_receive_writes & &
2019-08-02 05:17:19 -04:00
! slave - > repl_put_online_on_ack & &
2015-11-09 11:07:46 -05:00
clientHasPendingReplies ( slave ) )
2012-02-06 10:56:42 -05:00
{
2019-09-12 03:56:54 -04:00
writeToClient ( slave , 0 ) ;
2012-02-06 10:56:42 -05:00
}
}
}
2014-02-04 09:52:09 -05:00
/* Pause clients up to the specified unixtime (in ms). While clients
* are paused no command is processed from clients , so the data set can ' t
* change during that time .
*
* However while this function pauses normal and Pub / Sub clients , slaves are
* still served , so this function can be used on server upgrades where it is
* required that slaves process the latest bytes from the replication stream
* before being turned to masters .
*
* This function is also internally used by Redis Cluster for the manual
* failover procedure implemented by CLUSTER FAILOVER .
*
* The function always succeed , even if there is already a pause in progress .
* In such a case , the pause is extended if the duration is more than the
* time left for the previous duration . However if the duration is smaller
* than the time left for the previous pause , no change is made to the
* left duration . */
void pauseClients ( mstime_t end ) {
if ( ! server . clients_paused | | end > server . clients_pause_end_time )
server . clients_pause_end_time = end ;
server . clients_paused = 1 ;
}
/* Return non-zero if clients are currently paused. As a side effect the
* function checks if the pause time was reached and clear it . */
int clientsArePaused ( void ) {
2015-03-21 04:13:29 -04:00
if ( server . clients_paused & &
server . clients_pause_end_time < server . mstime )
{
2014-02-04 09:52:09 -05:00
listNode * ln ;
listIter li ;
2015-07-26 09:20:46 -04:00
client * c ;
2014-02-04 09:52:09 -05:00
server . clients_paused = 0 ;
/* Put all the clients in the unblocked clients queue in order to
* force the re - processing of the input buffer if any . */
listRewind ( server . clients , & li ) ;
while ( ( ln = listNext ( & li ) ) ! = NULL ) {
c = listNodeValue ( ln ) ;
2018-09-03 12:39:18 -04:00
/* Don't touch slaves and blocked clients.
* The latter pending requests will be processed when unblocked . */
if ( c - > flags & ( CLIENT_SLAVE | CLIENT_BLOCKED ) ) continue ;
queueClientForReprocessing ( c ) ;
2014-02-04 09:52:09 -05:00
}
}
return server . clients_paused ;
}
2014-04-24 11:36:47 -04:00
/* This function is called by Redis in order to process a few events from
* time to time while blocked into some not interruptible operation .
* This allows to reply to clients with the - LOADING error while loading the
* data set at startup or after a full resynchronization with the master
* and so forth .
*
* It calls the event loop in order to process a few events . Specifically we
2016-04-25 09:48:09 -04:00
* try to call the event loop 4 times as long as we receive acknowledge that
2014-04-24 11:36:47 -04:00
* some event was processed , in order to go forward with the accept , read ,
* write , close sequence needed to serve a client .
*
* The function returns the total number of events processed . */
int processEventsWhileBlocked ( void ) {
int iterations = 4 ; /* See the function top-comment. */
int count = 0 ;
while ( iterations - - ) {
2015-09-30 11:23:34 -04:00
int events = 0 ;
events + = aeProcessEvents ( server . el , AE_FILE_EVENTS | AE_DONT_WAIT ) ;
events + = handleClientsWithPendingWrites ( ) ;
2014-04-24 11:36:47 -04:00
if ( ! events ) break ;
count + = events ;
}
return count ;
}
2017-10-24 02:35:05 -04:00
2019-03-25 07:16:13 -04:00
/* ==========================================================================
2017-10-24 02:35:05 -04:00
* Threaded I / O
2019-03-25 07:16:13 -04:00
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
int tio_debug = 0 ;
2017-10-24 02:35:05 -04:00
2019-03-31 09:58:54 -04:00
# define IO_THREADS_MAX_NUM 128
# define IO_THREADS_OP_READ 0
# define IO_THREADS_OP_WRITE 1
2017-10-24 02:35:05 -04:00
2019-03-31 09:58:54 -04:00
pthread_t io_threads [ IO_THREADS_MAX_NUM ] ;
pthread_mutex_t io_threads_mutex [ IO_THREADS_MAX_NUM ] ;
_Atomic unsigned long io_threads_pending [ IO_THREADS_MAX_NUM ] ;
int io_threads_active ; /* Are the threads currently spinning waiting I/O? */
int io_threads_op ; /* IO_THREADS_OP_WRITE or IO_THREADS_OP_READ. */
list * io_threads_list [ IO_THREADS_MAX_NUM ] ;
2017-10-24 02:35:05 -04:00
void * IOThreadMain ( void * myid ) {
/* The ID is the thread number (from 0 to server.iothreads_num-1), and is
* used by the thread to just manipulate a single sub - array of clients . */
long id = ( unsigned long ) myid ;
while ( 1 ) {
2019-03-25 11:33:23 -04:00
/* Wait for start */
for ( int j = 0 ; j < 1000000 ; j + + ) {
if ( io_threads_pending [ id ] ! = 0 ) break ;
}
/* Give the main thread a chance to stop this thread. */
if ( io_threads_pending [ id ] = = 0 ) {
pthread_mutex_lock ( & io_threads_mutex [ id ] ) ;
pthread_mutex_unlock ( & io_threads_mutex [ id ] ) ;
continue ;
2017-10-24 02:35:05 -04:00
}
2019-03-25 11:33:23 -04:00
serverAssert ( io_threads_pending [ id ] ! = 0 ) ;
if ( tio_debug ) printf ( " [%ld] %d to handle \n " , id , ( int ) listLength ( io_threads_list [ id ] ) ) ;
/* Process: note that the main thread will never touch our list
* before we drop the pending count to 0. */
listIter li ;
listNode * ln ;
listRewind ( io_threads_list [ id ] , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
client * c = listNodeValue ( ln ) ;
2019-03-31 09:58:54 -04:00
if ( io_threads_op = = IO_THREADS_OP_WRITE ) {
2019-09-12 03:56:54 -04:00
writeToClient ( c , 0 ) ;
2019-03-31 15:59:50 -04:00
} else if ( io_threads_op = = IO_THREADS_OP_READ ) {
2019-09-12 03:56:54 -04:00
readQueryFromClient ( c - > conn ) ;
2019-03-31 15:59:50 -04:00
} else {
serverPanic ( " io_threads_op value is unknown " ) ;
2019-03-31 09:58:54 -04:00
}
2019-03-25 11:33:23 -04:00
}
listEmpty ( io_threads_list [ id ] ) ;
io_threads_pending [ id ] = 0 ;
2019-03-25 07:16:13 -04:00
if ( tio_debug ) printf ( " [%ld] Done \n " , id ) ;
2017-10-24 02:35:05 -04:00
}
}
/* Initialize the data structures needed for threaded I/O. */
void initThreadedIO ( void ) {
2019-03-25 07:56:48 -04:00
io_threads_active = 0 ; /* We start with threads not active. */
2019-03-27 13:39:13 -04:00
/* Don't spawn any thread if the user selected a single thread:
* we ' ll handle I / O directly from the main thread . */
if ( server . io_threads_num = = 1 ) return ;
2019-03-31 09:58:54 -04:00
if ( server . io_threads_num > IO_THREADS_MAX_NUM ) {
serverLog ( LL_WARNING , " Fatal: too many I/O threads configured. "
" The maximum number is %d. " , IO_THREADS_MAX_NUM ) ;
exit ( 1 ) ;
}
2019-03-27 13:39:13 -04:00
/* Spawn the I/O threads. */
2017-10-24 02:35:05 -04:00
for ( int i = 0 ; i < server . io_threads_num ; i + + ) {
2019-03-27 13:39:13 -04:00
pthread_t tid ;
2019-03-25 07:56:48 -04:00
pthread_mutex_init ( & io_threads_mutex [ i ] , NULL ) ;
io_threads_pending [ i ] = 0 ;
io_threads_list [ i ] = listCreate ( ) ;
pthread_mutex_lock ( & io_threads_mutex [ i ] ) ; /* Thread will be stopped. */
2017-10-24 02:35:05 -04:00
if ( pthread_create ( & tid , NULL , IOThreadMain , ( void * ) ( long ) i ) ! = 0 ) {
serverLog ( LL_WARNING , " Fatal: Can't initialize IO thread. " ) ;
exit ( 1 ) ;
}
io_threads [ i ] = tid ;
}
}
2019-03-25 07:56:48 -04:00
void startThreadedIO ( void ) {
2019-04-29 06:46:23 -04:00
if ( tio_debug ) { printf ( " S " ) ; fflush ( stdout ) ; }
2019-03-25 07:56:48 -04:00
if ( tio_debug ) printf ( " --- STARTING THREADED IO --- \n " ) ;
serverAssert ( io_threads_active = = 0 ) ;
for ( int j = 0 ; j < server . io_threads_num ; j + + )
pthread_mutex_unlock ( & io_threads_mutex [ j ] ) ;
io_threads_active = 1 ;
}
void stopThreadedIO ( void ) {
2019-03-31 16:06:00 -04:00
/* We may have still clients with pending reads when this function
* is called : handle them before stopping the threads . */
handleClientsWithPendingReadsUsingThreads ( ) ;
2019-04-29 06:46:23 -04:00
if ( tio_debug ) { printf ( " E " ) ; fflush ( stdout ) ; }
2019-03-31 16:06:00 -04:00
if ( tio_debug ) printf ( " --- STOPPING THREADED IO [R%d] [W%d] --- \n " ,
( int ) listLength ( server . clients_pending_read ) ,
( int ) listLength ( server . clients_pending_write ) ) ;
2019-03-25 07:56:48 -04:00
serverAssert ( io_threads_active = = 1 ) ;
for ( int j = 0 ; j < server . io_threads_num ; j + + )
pthread_mutex_lock ( & io_threads_mutex [ j ] ) ;
io_threads_active = 0 ;
}
2019-03-25 13:05:06 -04:00
/* This function checks if there are not enough pending clients to justify
* taking the I / O threads active : in that case I / O threads are stopped if
2019-03-30 06:26:58 -04:00
* currently active . We track the pending writes as a measure of clients
* we need to handle in parallel , however the I / O threading is disabled
* globally for reads as well if we have too little pending clients .
2019-03-25 13:05:06 -04:00
*
* The function returns 0 if the I / O threading should be used becuase there
* are enough active threads , otherwise 1 is returned and the I / O threads
* could be possibly stopped ( if already active ) as a side effect . */
int stopThreadedIOIfNeeded ( void ) {
int pending = listLength ( server . clients_pending_write ) ;
2019-03-27 13:39:13 -04:00
/* Return ASAP if IO threads are disabled (single threaded mode). */
2019-03-27 13:58:45 -04:00
if ( server . io_threads_num = = 1 ) return 1 ;
2019-03-27 13:39:13 -04:00
2019-03-25 13:05:06 -04:00
if ( pending < ( server . io_threads_num * 2 ) ) {
if ( io_threads_active ) stopThreadedIO ( ) ;
return 1 ;
} else {
return 0 ;
}
}
2017-10-24 02:35:05 -04:00
int handleClientsWithPendingWritesUsingThreads ( void ) {
int processed = listLength ( server . clients_pending_write ) ;
if ( processed = = 0 ) return 0 ; /* Return ASAP if there are no clients. */
2019-03-25 07:56:48 -04:00
/* If we have just a few clients to serve, don't use I/O threads, but the
* boring synchronous code . */
2019-03-25 13:05:06 -04:00
if ( stopThreadedIOIfNeeded ( ) ) {
2019-03-25 07:56:48 -04:00
return handleClientsWithPendingWrites ( ) ;
2017-10-24 02:35:05 -04:00
}
2019-03-25 07:56:48 -04:00
2019-03-25 13:05:06 -04:00
/* Start threads if needed. */
if ( ! io_threads_active ) startThreadedIO ( ) ;
2019-03-31 15:59:50 -04:00
if ( tio_debug ) printf ( " %d TOTAL WRITE pending clients \n " , processed ) ;
2017-10-24 02:35:05 -04:00
/* Distribute the clients across N different lists. */
listIter li ;
listNode * ln ;
listRewind ( server . clients_pending_write , & li ) ;
int item_id = 0 ;
while ( ( ln = listNext ( & li ) ) ) {
client * c = listNodeValue ( ln ) ;
c - > flags & = ~ CLIENT_PENDING_WRITE ;
int target_id = item_id % server . io_threads_num ;
listAddNodeTail ( io_threads_list [ target_id ] , c ) ;
item_id + + ;
}
2019-03-25 11:33:23 -04:00
/* Give the start condition to the waiting threads, by setting the
* start condition atomic var . */
2019-03-31 15:59:50 -04:00
io_threads_op = IO_THREADS_OP_WRITE ;
2019-03-25 11:33:23 -04:00
for ( int j = 0 ; j < server . io_threads_num ; j + + ) {
int count = listLength ( io_threads_list [ j ] ) ;
io_threads_pending [ j ] = count ;
}
2017-10-24 02:35:05 -04:00
/* Wait for all threads to end their work. */
2019-03-25 07:56:48 -04:00
while ( 1 ) {
unsigned long pending = 0 ;
for ( int j = 0 ; j < server . io_threads_num ; j + + )
pending + = io_threads_pending [ j ] ;
if ( pending = = 0 ) break ;
2017-10-24 02:35:05 -04:00
}
2019-03-31 15:59:50 -04:00
if ( tio_debug ) printf ( " I/O WRITE All threads finshed \n " ) ;
2017-10-24 02:35:05 -04:00
/* Run the list of clients again to install the write handler where
* needed . */
listRewind ( server . clients_pending_write , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
client * c = listNodeValue ( ln ) ;
/* Install the write handler if there are pending writes in some
* of the clients . */
if ( clientHasPendingReplies ( c ) & &
2019-09-12 03:56:54 -04:00
connSetWriteHandler ( c - > conn , sendReplyToClient ) = = AE_ERR )
2017-10-24 02:35:05 -04:00
{
freeClientAsync ( c ) ;
}
}
listEmpty ( server . clients_pending_write ) ;
return processed ;
}
2019-03-30 06:26:58 -04:00
/* Return 1 if we want to handle the client read later using threaded I/O.
* This is called by the readable handler of the event loop .
* As a side effect of calling this function the client is put in the
* pending read clients and flagged as such . */
int postponeClientRead ( client * c ) {
if ( io_threads_active & &
2019-04-30 09:39:27 -04:00
server . io_threads_do_reads & &
2019-03-30 06:26:58 -04:00
! ( c - > flags & ( CLIENT_MASTER | CLIENT_SLAVE | CLIENT_PENDING_READ ) ) )
{
c - > flags | = CLIENT_PENDING_READ ;
listAddNodeHead ( server . clients_pending_read , c ) ;
return 1 ;
} else {
return 0 ;
}
}
2019-03-31 09:58:54 -04:00
2019-04-30 09:59:23 -04:00
/* When threaded I/O is also enabled for the reading + parsing side, the
* readable handler will just put normal clients into a queue of clients to
* process ( instead of serving them synchronously ) . This function runs
* the queue using the I / O threads , and process them in order to accumulate
* the reads in the buffers , and also parse the first command available
* rendering it in the client structures . */
2019-03-31 09:58:54 -04:00
int handleClientsWithPendingReadsUsingThreads ( void ) {
2019-04-30 09:39:27 -04:00
if ( ! io_threads_active | | ! server . io_threads_do_reads ) return 0 ;
2019-03-31 15:59:50 -04:00
int processed = listLength ( server . clients_pending_read ) ;
if ( processed = = 0 ) return 0 ;
if ( tio_debug ) printf ( " %d TOTAL READ pending clients \n " , processed ) ;
/* Distribute the clients across N different lists. */
listIter li ;
listNode * ln ;
listRewind ( server . clients_pending_read , & li ) ;
int item_id = 0 ;
while ( ( ln = listNext ( & li ) ) ) {
client * c = listNodeValue ( ln ) ;
int target_id = item_id % server . io_threads_num ;
listAddNodeTail ( io_threads_list [ target_id ] , c ) ;
item_id + + ;
}
/* Give the start condition to the waiting threads, by setting the
* start condition atomic var . */
io_threads_op = IO_THREADS_OP_READ ;
for ( int j = 0 ; j < server . io_threads_num ; j + + ) {
int count = listLength ( io_threads_list [ j ] ) ;
io_threads_pending [ j ] = count ;
}
/* Wait for all threads to end their work. */
while ( 1 ) {
unsigned long pending = 0 ;
for ( int j = 0 ; j < server . io_threads_num ; j + + )
pending + = io_threads_pending [ j ] ;
if ( pending = = 0 ) break ;
}
if ( tio_debug ) printf ( " I/O READ All threads finshed \n " ) ;
/* Run the list of clients again to process the new buffers. */
listRewind ( server . clients_pending_read , & li ) ;
while ( ( ln = listNext ( & li ) ) ) {
client * c = listNodeValue ( ln ) ;
c - > flags & = ~ CLIENT_PENDING_READ ;
2019-04-26 13:29:50 -04:00
if ( c - > flags & CLIENT_PENDING_COMMAND ) {
c - > flags & = ~ CLIENT_PENDING_COMMAND ;
processCommandAndResetClient ( c ) ;
}
2019-03-31 15:59:50 -04:00
processInputBufferAndReplicate ( c ) ;
}
listEmpty ( server . clients_pending_read ) ;
return processed ;
2019-03-31 09:58:54 -04:00
}