diff options
author | antirez <antirez@gmail.com> | 2015-10-21 20:43:37 +0200 |
---|---|---|
committer | antirez <antirez@gmail.com> | 2015-10-21 20:43:37 +0200 |
commit | 86f0a2ee877c0822402a113777cc623c91c9eac0 (patch) | |
tree | 123a78d998e60261f1a0a25841a9ea128ab86809 /src/networking.c | |
parent | bdcb14556644ff501acb061b0a6bdfc24d6b5b81 (diff) | |
download | redis-86f0a2ee877c0822402a113777cc623c91c9eac0.tar.gz |
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.
Diffstat (limited to 'src/networking.c')
-rw-r--r-- | src/networking.c | 29 |
1 files changed, 28 insertions, 1 deletions
diff --git a/src/networking.c b/src/networking.c index ff2d29a8e..60f1b798e 100644 --- a/src/networking.c +++ b/src/networking.c @@ -160,6 +160,9 @@ int prepareClientToWrite(client *c) { * handler since there is no socket at all. */ if (c->flags & CLIENT_LUA) return C_OK; + /* CLIENT REPLY OFF / SKIP handling: don't send replies. */ + if (c->flags & (CLIENT_REPLY_OFF|CLIENT_REPLY_SKIP)) return C_ERR; + /* Masters don't receive replies, unless CLIENT_MASTER_FORCE_REPLY flag * is set. */ if ((c->flags & CLIENT_MASTER) && @@ -945,10 +948,20 @@ void resetClient(client *c) { c->reqtype = 0; c->multibulklen = 0; c->bulklen = -1; + /* 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. */ if (!(c->flags & CLIENT_MULTI) && prevcmd != askingCommand) - c->flags &= (~CLIENT_ASKING); + 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; + } } int processInlineBuffer(client *c) { @@ -1396,6 +1409,20 @@ void clientCommand(client *c) { sds o = getAllClientsInfoString(); addReplyBulkCBuffer(c,o,sdslen(o)); sdsfree(o); + } 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; + } } else if (!strcasecmp(c->argv[1]->ptr,"kill")) { /* CLIENT KILL <ip:port> * CLIENT KILL <option> [value] ... <option> [value] */ |