From 9ddd0f779686480e5ea462757e12e74b4fe4e2ac Mon Sep 17 00:00:00 2001 From: Gengliang Wang Date: Mon, 4 Feb 2013 14:01:08 +0800 Subject: Fix a bug in srandmemberWithCountCommand() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In CASE 2, the call sunionDiffGenericCommand will involve the string "srandmember" > sadd foo one (integer 1) > sadd srandmember two (integer 2) > srandmember foo 3 1)"one" 2)"two" --- src/t_set.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t_set.c b/src/t_set.c index 3064807d1..4db5372cd 100644 --- a/src/t_set.c +++ b/src/t_set.c @@ -450,7 +450,7 @@ void srandmemberWithCountCommand(redisClient *c) { * The number of requested elements is greater than the number of * elements inside the set: simply return the whole set. */ if (count >= size) { - sunionDiffGenericCommand(c,c->argv,c->argc-1,NULL,REDIS_OP_UNION); + sunionDiffGenericCommand(c,c->argv+1,1,NULL,REDIS_OP_UNION); return; } -- cgit v1.2.1 From 63dc34bd014d147705473c29d5577ffc7e972a53 Mon Sep 17 00:00:00 2001 From: David Celis Date: Sun, 3 Feb 2013 11:40:07 -0800 Subject: Fix a few typos and improve grammar of redis.conf Make several edits to the example redis.conf configuration file for improved flow and grammar. Signed-off-by: David Celis --- redis.conf | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/redis.conf b/redis.conf index 399f957fc..9eed0cabc 100644 --- a/redis.conf +++ b/redis.conf @@ -39,8 +39,8 @@ port 6379 # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 -# Set server verbosity to 'debug' -# it can be one of: +# Specify the server verbosity level. +# This can be one of: # debug (a lot of information, useful for development/testing) # verbose (many rarely useful info, but not a mess like the debug level) # notice (moderately verbose, what you want in production probably) @@ -59,7 +59,7 @@ logfile stdout # Specify the syslog identity. # syslog-ident redis -# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. # syslog-facility local0 # Set the number of databases. The default database is DB 0, you can select @@ -114,7 +114,7 @@ stop-writes-on-bgsave-error yes # the dataset will likely be bigger if you have compressible values or keys. rdbcompression yes -# Since verison 5 of RDB a CRC64 checksum is placed at the end of the file. +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. # This makes the format more resistant to corruption but there is a performance # hit to pay (around 10%) when saving and loading RDB files, so you can disable it # for maximum performances. @@ -131,7 +131,7 @@ dbfilename dump.rdb # The DB will be written inside this directory, with the filename specified # above using the 'dbfilename' configuration directive. # -# Also the Append Only File will be created inside this directory. +# The Append Only File will also be created inside this directory. # # Note that you must specify a directory here, not a file name. dir ./ @@ -152,7 +152,7 @@ dir ./ # # masterauth -# When a slave lost the connection with the master, or when the replication +# When a slave loses its connection with the master, or when the replication # is still in progress, the slave can act in two different ways: # # 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will @@ -230,14 +230,14 @@ slave-priority 100 # # It is possible to change the name of dangerous commands in a shared # environment. For instance the CONFIG command may be renamed into something -# of hard to guess so that it will be still available for internal-use -# tools but not available for general clients. +# hard to guess so that it will still be available for internal-use tools +# but not available for general clients. # # Example: # # rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 # -# It is also possible to completely kill a command renaming it into +# It is also possible to completely kill a command by renaming it into # an empty string: # # rename-command CONFIG "" @@ -281,7 +281,7 @@ slave-priority 100 # maxmemory # MAXMEMORY POLICY: how Redis will select what to remove when maxmemory -# is reached? You can select among five behavior: +# is reached. You can select among five behaviors: # # volatile-lru -> remove the key with an expire set using an LRU algorithm # allkeys-lru -> remove any key accordingly to the LRU algorithm @@ -290,7 +290,7 @@ slave-priority 100 # volatile-ttl -> remove the key with the nearest expire time (minor TTL) # noeviction -> don't expire at all, just return an error on write operations # -# Note: with all the kind of policies, Redis will return an error on write +# Note: with any of the above policies, Redis will return an error on write # operations, when there are not suitable keys for eviction. # # At the date of writing this commands are: set setnx setex append @@ -346,7 +346,7 @@ appendonly no # always: fsync after every write to the append only log . Slow, Safest. # everysec: fsync only one time every second. Compromise. # -# The default is "everysec" that's usually the right compromise between +# The default is "everysec", as that's usually the right compromise between # speed and data safety. It's up to you to understand if you can relax this to # "no" that will let the operating system flush the output buffer when # it wants, for better performances (but if you can live with the idea of @@ -374,9 +374,9 @@ appendfsync everysec # that will prevent fsync() from being called in the main process while a # BGSAVE or BGREWRITEAOF is in progress. # -# This means that while another child is saving the durability of Redis is -# the same as "appendfsync none", that in practical terms means that it is -# possible to lost up to 30 seconds of log in the worst scenario (with the +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync none". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the # default Linux settings). # # If you have latency problems turn this to "yes". Otherwise leave it as @@ -385,10 +385,10 @@ no-appendfsync-on-rewrite no # Automatic rewrite of the append only file. # Redis is able to automatically rewrite the log file implicitly calling -# BGREWRITEAOF when the AOF log size will growth by the specified percentage. +# BGREWRITEAOF when the AOF log size grows by the specified percentage. # # This is how it works: Redis remembers the size of the AOF file after the -# latest rewrite (or if no rewrite happened since the restart, the size of +# latest rewrite (if no rewrite has happened since the restart, the size of # the AOF at startup is used). # # This base size is compared to the current size. If the current size is @@ -524,7 +524,7 @@ activerehashing yes # Instead there is a default limit for pubsub and slave clients, since # subscribers and slaves receive data in a push fashion. # -# Both the hard or the soft limit can be disabled just setting it to zero. +# Both the hard or the soft limit can be disabled by setting them to zero. client-output-buffer-limit normal 0 0 0 client-output-buffer-limit slave 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60 -- cgit v1.2.1 From 8c0b9f778e8dcd3df6320edb565bb94ce6cd614a Mon Sep 17 00:00:00 2001 From: Rock Li Date: Tue, 5 Feb 2013 15:56:04 +0800 Subject: retval doesn't initalized If each if conditions are all fail, variable retval will under uninitlized --- src/t_set.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/t_set.c b/src/t_set.c index 4db5372cd..66e1f1f70 100644 --- a/src/t_set.c +++ b/src/t_set.c @@ -472,7 +472,7 @@ void srandmemberWithCountCommand(redisClient *c) { /* Add all the elements into the temporary dictionary. */ si = setTypeInitIterator(set); while((encoding = setTypeNext(si,&ele,&llele)) != -1) { - int retval; + int retval = DICT_ERR; if (encoding == REDIS_ENCODING_INTSET) { retval = dictAdd(d,createStringObjectFromLongLong(llele),NULL); -- cgit v1.2.1 From 6da090e916c56284054d846dbbe0b78e0cfd8914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Bergstr=C3=B6m?= Date: Thu, 24 Jan 2013 09:25:47 +1100 Subject: Check available tcl versions --- runtest | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/runtest b/runtest index 0eb384c24..d8451df57 100755 --- a/runtest +++ b/runtest @@ -1,9 +1,14 @@ #!/bin/sh -TCL=tclsh8.5 -which $TCL -if [ "$?" != "0" ] +TCL_VERSIONS="8.5 8.6" +TCLSH="" + +for VERSION in $TCL_VERSIONS; do + TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL +done + +if [ -z $TCLSH ] then - echo "You need '$TCL' in order to run the Redis test" + echo "You need tcl 8.5 or newer in order to run the Redis test" exit 1 fi -$TCL tests/test_helper.tcl $* +$TCLSH tests/test_helper.tcl $* -- cgit v1.2.1 From f9a8b0eced9be35d26a2c4f9df143530ad429067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Bergstr=C3=B6m?= Date: Thu, 24 Jan 2013 09:36:59 +1100 Subject: Enforce tcl 8.5 or newer --- tests/test_helper.tcl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index 38fb1f539..aeff69f7e 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -2,6 +2,8 @@ # This softare is released under the BSD License. See the COPYING file for # more information. +package require Tcl 8.5 + set tcl_precision 17 source tests/support/redis.tcl source tests/support/server.tcl -- cgit v1.2.1 From 22ccc6db0fe1b08180542a054f5ce5d5afd1f235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Bergstr=C3=B6m?= Date: Thu, 24 Jan 2013 09:37:18 +1100 Subject: Use `info nameofexectuable` to find current executable --- tests/integration/replication-4.tcl | 3 ++- tests/integration/replication.tcl | 3 ++- tests/test_helper.tcl | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/integration/replication-4.tcl b/tests/integration/replication-4.tcl index 69fcab373..0a0dcdc2b 100644 --- a/tests/integration/replication-4.tcl +++ b/tests/integration/replication-4.tcl @@ -1,5 +1,6 @@ proc start_bg_complex_data {host port db ops} { - exec tclsh8.5 tests/helpers/bg_complex_data.tcl $host $port $db $ops & + set tclsh [info nameofexecutable] + exec $tclsh tests/helpers/bg_complex_data.tcl $host $port $db $ops & } proc stop_bg_complex_data {handle} { diff --git a/tests/integration/replication.tcl b/tests/integration/replication.tcl index da94b0880..5ca6449f4 100644 --- a/tests/integration/replication.tcl +++ b/tests/integration/replication.tcl @@ -78,7 +78,8 @@ start_server {tags {"repl"}} { } proc start_write_load {host port seconds} { - exec tclsh8.5 tests/helpers/gen_write_load.tcl $host $port $seconds & + set tclsh [info nameofexecutable] + exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds & } proc stop_write_load {handle} { diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index aeff69f7e..483774219 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -178,6 +178,7 @@ proc find_available_port start { proc test_server_main {} { cleanup + set tclsh [info nameofexecutable] # Open a listening socket, trying different ports in order to find a # non busy one. set port [find_available_port 11111] @@ -191,7 +192,7 @@ proc test_server_main {} { set start_port [expr {$::port+100}] for {set j 0} {$j < $::numclients} {incr j} { set start_port [find_available_port $start_port] - set p [exec tclsh8.5 [info script] {*}$::argv \ + set p [exec $tclsh [info script] {*}$::argv \ --client $port --port $start_port &] lappend ::clients_pids $p incr start_port 10 -- cgit v1.2.1 From eb07793b59f9fc3237f71c602806d84965d0557f Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 5 Feb 2013 12:02:36 +0100 Subject: Test: No clients timeout while testing. --- tests/assets/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/assets/default.conf b/tests/assets/default.conf index 976852e91..c76eca3eb 100644 --- a/tests/assets/default.conf +++ b/tests/assets/default.conf @@ -35,7 +35,7 @@ port 6379 # unixsocket /tmp/redis.sock # Close the connection after a client is idle for N seconds (0 to disable) -timeout 300 +timeout 0 # Set server verbosity to 'debug' # it can be one of: -- cgit v1.2.1 From 45b1b2f7ada05d01b6dbd672ec28a587fc60a031 Mon Sep 17 00:00:00 2001 From: charsyam Date: Thu, 31 Jan 2013 12:09:16 +0900 Subject: Turn off TCP_NODELAY on the slave socket after SYNC. Further details from @antirez: It was reported by @StopForumSpam on Twitter that the Redis replication link was strangely using multiple TCP packets for multiple commands. This wastes a lot of bandwidth and is due to the TCP_NODELAY option we enable on the socket after accepting a new connection. However the master -> slave channel is a one-way channel since Redis replication is asynchronous, so there is no point in trying to reduce the latency, we should aim to reduce the bandwidth. For this reason this commit introduces the ability to disable the nagle algorithm on the socket after a successful SYNC. This feature is off by default because the delay can be up to 40 milliseconds with normally configured Linux kernels. --- src/anet.c | 14 ++++++++++++-- src/anet.h | 1 + src/config.c | 7 +++++++ src/redis.c | 1 + src/redis.h | 1 + src/replication.c | 8 ++++++++ 6 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/anet.c b/src/anet.c index 4da3e28db..d002cb31c 100644 --- a/src/anet.c +++ b/src/anet.c @@ -75,9 +75,8 @@ int anetNonBlock(char *err, int fd) return ANET_OK; } -int anetTcpNoDelay(char *err, int fd) +static int _anetTcpNoDelay(char *err, int fd, int yes) { - int yes = 1; if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); @@ -86,6 +85,17 @@ int anetTcpNoDelay(char *err, int fd) return ANET_OK; } +int anetTcpNoDelay(char *err, int fd) +{ + return _anetTcpNoDelay(err, fd, 1); +} + +int anetTcpNoDelayOff(char *err, int fd) +{ + return _anetTcpNoDelay(err, fd, 0); +} + + int anetSetSendBuffer(char *err, int fd, int buffsize) { if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1) diff --git a/src/anet.h b/src/anet.h index 062b22c56..56ae50573 100644 --- a/src/anet.h +++ b/src/anet.h @@ -52,6 +52,7 @@ int anetUnixAccept(char *err, int serversock); int anetWrite(int fd, char *buf, int count); int anetNonBlock(char *err, int fd); int anetTcpNoDelay(char *err, int fd); +int anetTcpNoDelayOff(char *err, int fd); int anetTcpKeepAlive(char *err, int fd); int anetPeerToString(int fd, char *ip, int *port); diff --git a/src/config.c b/src/config.c index be2dbce19..1510f7b51 100644 --- a/src/config.c +++ b/src/config.c @@ -378,6 +378,8 @@ void loadServerConfigFromString(char *config) { if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) { err = "argument must be 'yes' or 'no'"; goto loaderr; } + } else if (!strcasecmp(argv[0],"slave-tcp-nodelay-off") && argc == 2) { + server.slave_tcp_nodelay_off = atoi(argv[1]); } else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) { server.slave_priority = atoi(argv[1]); } else if (!strcasecmp(argv[0],"sentinel")) { @@ -697,6 +699,10 @@ void configSetCommand(redisClient *c) { if (yn == -1) goto badfmt; server.rdb_checksum = yn; + } else if (!strcasecmp(c->argv[2]->ptr,"slave-tcp-nodelay-off")) { + if (getLongLongFromObject(o,&ll) == REDIS_ERR ) goto badfmt; + + server.slave_tcp_nodelay_off = ll; } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) { if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt; @@ -790,6 +796,7 @@ void configGetCommand(redisClient *c) { config_get_numerical_field("repl-timeout",server.repl_timeout); config_get_numerical_field("maxclients",server.maxclients); config_get_numerical_field("watchdog-period",server.watchdog_period); + config_get_numerical_field("slave-tcp-nodelay-off",server.slave_tcp_nodelay_off); config_get_numerical_field("slave-priority",server.slave_priority); /* Bool (yes/no) values */ diff --git a/src/redis.c b/src/redis.c index 5af5200e8..51c36e8ad 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1160,6 +1160,7 @@ void initServerConfig() { server.repl_serve_stale_data = 1; server.repl_slave_ro = 1; server.repl_down_since = time(NULL); + server.slave_tcp_nodelay_off = 1; server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY; /* Client output buffer limits */ diff --git a/src/redis.h b/src/redis.h index 682829b7b..7f9ca6efe 100644 --- a/src/redis.h +++ b/src/redis.h @@ -616,6 +616,7 @@ struct redisServer { int repl_serve_stale_data; /* Serve stale data when link is down? */ int repl_slave_ro; /* Slave is read only? */ time_t repl_down_since; /* Unix time at which link with master went down */ + int slave_tcp_nodelay_off; /* turn off slave's tcp nodelay */ int slave_priority; /* Reported in INFO and used by Sentinel. */ /* Limits */ unsigned int maxclients; /* Max number of simultaneous clients */ diff --git a/src/replication.c b/src/replication.c index dc90c8d0e..4fa62f255 100644 --- a/src/replication.c +++ b/src/replication.c @@ -118,6 +118,14 @@ void syncCommand(redisClient *c) { /* ignore SYNC if already slave or in monitor mode */ if (c->flags & REDIS_SLAVE) return; + if (server.slave_tcp_nodelay_off) { + redisLog(REDIS_NOTICE, "Turning off slave's :%d TCP NODELAY SETTING", c->fd); + char err[1024]; + if (anetTcpNoDelayOff(err, c->fd) == ANET_ERR) + redisLog(REDIS_WARNING, + "Can't turn off %d 's tcp nodelay setting: %s", c->fd, err); + } + /* Refuse SYNC requests if we are a slave but the link with our master * is not ok... */ if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED) { -- cgit v1.2.1 From cf0191dc3cd391a0fd5b0f9a6fa6a9208756a9f7 Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 31 Jan 2013 11:14:15 +0100 Subject: TCP_NODELAY after SYNC: changes to the implementation. --- redis.conf | 15 +++++++++++++++ src/anet.c | 12 ++++++------ src/anet.h | 4 ++-- src/config.c | 16 ++++++++++------ src/networking.c | 2 +- src/redis.c | 2 +- src/redis.h | 2 +- src/replication.c | 11 +++-------- 8 files changed, 39 insertions(+), 25 deletions(-) diff --git a/redis.conf b/redis.conf index 9eed0cabc..c53c72006 100644 --- a/redis.conf +++ b/redis.conf @@ -196,6 +196,21 @@ slave-read-only yes # # repl-timeout 60 +# Disable TCP_NODELAY on the slave socket after SYNC? +# +# If you select "yes" Redis will use a smaller number of TCP packets and +# less bandwidth to send data to slaves. But this can add a delay for +# the data to appear on the slave side, up to 40 milliseconds with +# Linux kernels using a default configuration. +# +# If you select "no" the delay for data to appear on the slave side will +# be reduced but more bandwidth will be used for replication. +# +# By default we optimize for low latency, but in very high traffic conditions +# or when the master and slaves are many hops away, turning this to "yes" may +# be a good idea. +repl-disable-tcp-nodelay no + # The slave priority is an integer number published by Redis in the INFO output. # It is used by Redis Sentinel in order to select a slave to promote into a # master if the master is no longer working correctly. diff --git a/src/anet.c b/src/anet.c index d002cb31c..3f037dcad 100644 --- a/src/anet.c +++ b/src/anet.c @@ -75,9 +75,9 @@ int anetNonBlock(char *err, int fd) return ANET_OK; } -static int _anetTcpNoDelay(char *err, int fd, int yes) +static int anetSetTcpNoDelay(char *err, int fd, int val) { - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1) { anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); return ANET_ERR; @@ -85,14 +85,14 @@ static int _anetTcpNoDelay(char *err, int fd, int yes) return ANET_OK; } -int anetTcpNoDelay(char *err, int fd) +int anetEnableTcpNoDelay(char *err, int fd) { - return _anetTcpNoDelay(err, fd, 1); + return anetSetTcpNoDelay(err, fd, 1); } -int anetTcpNoDelayOff(char *err, int fd) +int anetDisableTcpNoDelay(char *err, int fd) { - return _anetTcpNoDelay(err, fd, 0); + return anetSetTcpNoDelay(err, fd, 0); } diff --git a/src/anet.h b/src/anet.h index 56ae50573..dbf36dbd8 100644 --- a/src/anet.h +++ b/src/anet.h @@ -51,8 +51,8 @@ int anetTcpAccept(char *err, int serversock, char *ip, int *port); int anetUnixAccept(char *err, int serversock); int anetWrite(int fd, char *buf, int count); int anetNonBlock(char *err, int fd); -int anetTcpNoDelay(char *err, int fd); -int anetTcpNoDelayOff(char *err, int fd); +int anetEnableTcpNoDelay(char *err, int fd); +int anetDisableTcpNoDelay(char *err, int fd); int anetTcpKeepAlive(char *err, int fd); int anetPeerToString(int fd, char *ip, int *port); diff --git a/src/config.c b/src/config.c index 1510f7b51..45dc6b702 100644 --- a/src/config.c +++ b/src/config.c @@ -230,6 +230,10 @@ void loadServerConfigFromString(char *config) { err = "repl-timeout must be 1 or greater"; goto loaderr; } + } else if (!strcasecmp(argv[0],"repl-disable-tcp-nodelay") && argc==2) { + if ((server.repl_disable_tcp_nodelay = yesnotoi(argv[1])) == -1) { + err = "argument must be 'yes' or 'no'"; goto loaderr; + } } else if (!strcasecmp(argv[0],"masterauth") && argc == 2) { server.masterauth = zstrdup(argv[1]); } else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) { @@ -378,8 +382,6 @@ void loadServerConfigFromString(char *config) { if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) { err = "argument must be 'yes' or 'no'"; goto loaderr; } - } else if (!strcasecmp(argv[0],"slave-tcp-nodelay-off") && argc == 2) { - server.slave_tcp_nodelay_off = atoi(argv[1]); } else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) { server.slave_priority = atoi(argv[1]); } else if (!strcasecmp(argv[0],"sentinel")) { @@ -699,10 +701,11 @@ void configSetCommand(redisClient *c) { if (yn == -1) goto badfmt; server.rdb_checksum = yn; - } else if (!strcasecmp(c->argv[2]->ptr,"slave-tcp-nodelay-off")) { - if (getLongLongFromObject(o,&ll) == REDIS_ERR ) goto badfmt; + } else if (!strcasecmp(c->argv[2]->ptr,"repl-disable-tcp-nodelay")) { + int yn = yesnotoi(o->ptr); - server.slave_tcp_nodelay_off = ll; + if (yn == -1) goto badfmt; + server.repl_disable_tcp_nodelay = yn; } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) { if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt; @@ -796,7 +799,6 @@ void configGetCommand(redisClient *c) { config_get_numerical_field("repl-timeout",server.repl_timeout); config_get_numerical_field("maxclients",server.maxclients); config_get_numerical_field("watchdog-period",server.watchdog_period); - config_get_numerical_field("slave-tcp-nodelay-off",server.slave_tcp_nodelay_off); config_get_numerical_field("slave-priority",server.slave_priority); /* Bool (yes/no) values */ @@ -812,6 +814,8 @@ void configGetCommand(redisClient *c) { config_get_bool_field("rdbcompression", server.rdb_compression); config_get_bool_field("rdbchecksum", server.rdb_checksum); config_get_bool_field("activerehashing", server.activerehashing); + config_get_bool_field("repl-disable-tcp-nodelay", + server.repl_disable_tcp_nodelay); /* Everything we can't handle with macros follows. */ diff --git a/src/networking.c b/src/networking.c index 6e5d32489..3c28821d0 100644 --- a/src/networking.c +++ b/src/networking.c @@ -58,7 +58,7 @@ redisClient *createClient(int fd) { * contexts (for instance a Lua script) we need a non connected client. */ if (fd != -1) { anetNonBlock(NULL,fd); - anetTcpNoDelay(NULL,fd); + anetEnableTcpNoDelay(NULL,fd); if (aeCreateFileEvent(server.el,fd,AE_READABLE, readQueryFromClient, c) == AE_ERR) { diff --git a/src/redis.c b/src/redis.c index 51c36e8ad..be2e19312 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1160,7 +1160,7 @@ void initServerConfig() { server.repl_serve_stale_data = 1; server.repl_slave_ro = 1; server.repl_down_since = time(NULL); - server.slave_tcp_nodelay_off = 1; + server.repl_disable_tcp_nodelay = 0; server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY; /* Client output buffer limits */ diff --git a/src/redis.h b/src/redis.h index 7f9ca6efe..19fe877e0 100644 --- a/src/redis.h +++ b/src/redis.h @@ -616,7 +616,7 @@ struct redisServer { int repl_serve_stale_data; /* Serve stale data when link is down? */ int repl_slave_ro; /* Slave is read only? */ time_t repl_down_since; /* Unix time at which link with master went down */ - int slave_tcp_nodelay_off; /* turn off slave's tcp nodelay */ + int repl_disable_tcp_nodelay; /* Disable TCP_NODELAY after SYNC? */ int slave_priority; /* Reported in INFO and used by Sentinel. */ /* Limits */ unsigned int maxclients; /* Max number of simultaneous clients */ diff --git a/src/replication.c b/src/replication.c index 4fa62f255..c12692ce2 100644 --- a/src/replication.c +++ b/src/replication.c @@ -118,14 +118,6 @@ void syncCommand(redisClient *c) { /* ignore SYNC if already slave or in monitor mode */ if (c->flags & REDIS_SLAVE) return; - if (server.slave_tcp_nodelay_off) { - redisLog(REDIS_NOTICE, "Turning off slave's :%d TCP NODELAY SETTING", c->fd); - char err[1024]; - if (anetTcpNoDelayOff(err, c->fd) == ANET_ERR) - redisLog(REDIS_WARNING, - "Can't turn off %d 's tcp nodelay setting: %s", c->fd, err); - } - /* Refuse SYNC requests if we are a slave but the link with our master * is not ok... */ if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED) { @@ -180,6 +172,9 @@ void syncCommand(redisClient *c) { } c->replstate = REDIS_REPL_WAIT_BGSAVE_END; } + + if (server.repl_disable_tcp_nodelay) + anetDisableTcpNoDelay(NULL, c->fd); /* Non critical if it fails. */ c->repldbfd = -1; c->flags |= REDIS_SLAVE; c->slaveseldb = 0; -- cgit v1.2.1 From d5d0b467dbafe4d549b3460a511e4ac48747e12f Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 7 Feb 2013 19:13:00 +0100 Subject: LASTSAVE is a "random" command. --- src/redis.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/redis.c b/src/redis.c index be2e19312..fe2610858 100644 --- a/src/redis.c +++ b/src/redis.c @@ -214,7 +214,7 @@ struct redisCommand redisCommandTable[] = { {"bgsave",bgsaveCommand,1,"ar",0,NULL,0,0,0,0,0}, {"bgrewriteaof",bgrewriteaofCommand,1,"ar",0,NULL,0,0,0,0,0}, {"shutdown",shutdownCommand,-1,"ar",0,NULL,0,0,0,0,0}, - {"lastsave",lastsaveCommand,1,"r",0,NULL,0,0,0,0,0}, + {"lastsave",lastsaveCommand,1,"rR",0,NULL,0,0,0,0,0}, {"type",typeCommand,2,"r",0,NULL,1,1,1,0,0}, {"multi",multiCommand,1,"rs",0,NULL,0,0,0,0,0}, {"exec",execCommand,1,"sM",0,NULL,0,0,0,0,0}, -- cgit v1.2.1 From 63ad0e88672cfcd1735119448cf8c5bf30c32956 Mon Sep 17 00:00:00 2001 From: antirez Date: Fri, 8 Feb 2013 10:26:19 +0100 Subject: Make all WATCHers dirty when the slave reloads the DB. --- src/replication.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/replication.c b/src/replication.c index c12692ce2..2eca8fa9d 100644 --- a/src/replication.c +++ b/src/replication.c @@ -427,6 +427,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) { return; } redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Loading DB in memory"); + signalFlushedDb(-1); emptyDb(); /* Before loading the DB into memory we need to delete the readable * handler, otherwise it will get called recursively since -- cgit v1.2.1 From c7b9a57fb2e50a9fc6d8734cb13ef7f48451fd2c Mon Sep 17 00:00:00 2001 From: Pierre Chapuis Date: Mon, 28 Jan 2013 11:07:17 +0100 Subject: fix comments forgotten in #285 (zipmap -> ziplist) --- src/redis.c | 2 +- src/ziplist.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/redis.c b/src/redis.c index fe2610858..dc37363a7 100644 --- a/src/redis.c +++ b/src/redis.c @@ -537,7 +537,7 @@ dictType commandTableDictType = { NULL /* val destructor */ }; -/* Hash type hash table (note that small hashes are represented with zipmaps) */ +/* Hash type hash table (note that small hashes are represented with ziplists) */ dictType hashDictType = { dictEncObjHash, /* hash function */ NULL, /* key dup */ diff --git a/src/ziplist.c b/src/ziplist.c index d4ac4f9b4..8a6b54a7c 100644 --- a/src/ziplist.c +++ b/src/ziplist.c @@ -739,10 +739,10 @@ unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) { } } -/* Get entry pointer to by 'p' and store in either 'e' or 'v' depending +/* Get entry pointed to by 'p' and store in either 'e' or 'v' depending * on the encoding of the entry. 'e' is always set to NULL to be able * to find out whether the string pointer or the integer value was set. - * Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */ + * Return 0 if 'p' points to the end of the ziplist, 1 otherwise. */ unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) { zlentry entry; if (p == NULL || p[0] == ZIP_END) return 0; -- cgit v1.2.1 From 0b50d1ce9aa27b049dde5191a1a1ff1cdde3eca4 Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 31 Jan 2013 17:19:21 +0100 Subject: Sentinel: advertise the promoted slave address only after successful setup. --- src/sentinel.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/sentinel.c b/src/sentinel.c index 8009e5ed9..fc857344c 100644 --- a/src/sentinel.c +++ b/src/sentinel.c @@ -2035,8 +2035,16 @@ void sentinelCommand(redisClient *c) { } else { sentinelAddr *addr = ri->addr; - if ((ri->flags & SRI_FAILOVER_IN_PROGRESS) && ri->promoted_slave) + /* If we are in the middle of a failover, and the slave was + * already successfully switched to master role, we can advertise + * the new address as slave in order to allow clients to talk + * with the new master ASAP. */ + if ((ri->flags & SRI_FAILOVER_IN_PROGRESS) && + ri->promoted_slave && + ri->failover_state >= SENTINEL_FAILOVER_STATE_RECONF_SLAVES) + { addr = ri->promoted_slave->addr; + } addReplyMultiBulkLen(c,2); addReplyBulkCString(c,addr->ip); addReplyBulkLongLong(c,addr->port); -- cgit v1.2.1 From 609a3ec1be7cf49411129805a5bf2ca660c9dd02 Mon Sep 17 00:00:00 2001 From: antirez Date: Fri, 8 Feb 2013 16:30:21 +0100 Subject: Add SO_KEEPALIVE support to anet.c. --- src/anet.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/anet.h | 1 + 2 files changed, 44 insertions(+) diff --git a/src/anet.c b/src/anet.c index 3f037dcad..82bca5534 100644 --- a/src/anet.c +++ b/src/anet.c @@ -75,6 +75,49 @@ int anetNonBlock(char *err, int fd) return ANET_OK; } +/* Set TCP keep alive option to detect dead peers. The interval option + * is only used for Linux as we are using Linux-specific APIs to set + * the probe send time, interval, and count. */ +int anetKeepAlive(char *err, int fd, int interval) +{ + int val = 1; + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1) + { + anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno)); + return ANET_ERR; + } + +#ifdef __linux__ + /* Default settings are more or less garbage, with the keepalive time + * set to 7200 by default on Linux. Modify settings to make the feature + * actually useful. */ + + /* Send first probe after interval. */ + val = interval; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) { + anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno)); + return ANET_ERR; + } + + /* Send next probes after interval. */ + val = interval; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) { + anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno)); + return ANET_ERR; + } + + /* Consider the socket in error state after just one missing ACK reply. */ + val = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) { + anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno)); + return ANET_ERR; + } +#endif + + return ANET_OK; +} + static int anetSetTcpNoDelay(char *err, int fd, int val) { if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1) diff --git a/src/anet.h b/src/anet.h index dbf36dbd8..696c2c225 100644 --- a/src/anet.h +++ b/src/anet.h @@ -55,5 +55,6 @@ int anetEnableTcpNoDelay(char *err, int fd); int anetDisableTcpNoDelay(char *err, int fd); int anetTcpKeepAlive(char *err, int fd); int anetPeerToString(int fd, char *ip, int *port); +int anetKeepAlive(char *err, int fd, int interval); #endif -- cgit v1.2.1 From e1e8b1cd29840d8055723836bf58c82f2a69ac45 Mon Sep 17 00:00:00 2001 From: antirez Date: Fri, 8 Feb 2013 16:40:59 +0100 Subject: Set SO_KEEPALIVE on client sockets if configured to do so. --- src/config.c | 10 ++++++++++ src/networking.c | 2 ++ src/redis.c | 1 + src/redis.h | 1 + 4 files changed, 14 insertions(+) diff --git a/src/config.c b/src/config.c index 45dc6b702..b9e6c5cfc 100644 --- a/src/config.c +++ b/src/config.c @@ -81,6 +81,11 @@ void loadServerConfigFromString(char *config) { if (server.maxidletime < 0) { err = "Invalid timeout value"; goto loaderr; } + } else if (!strcasecmp(argv[0],"tcp-keepalive") && argc == 2) { + server.tcpkeepalive = atoi(argv[1]); + if (server.tcpkeepalive < 0) { + err = "Invalid tcp-keepalive value"; goto loaderr; + } } else if (!strcasecmp(argv[0],"port") && argc == 2) { server.port = atoi(argv[1]); if (server.port < 0 || server.port > 65535) { @@ -503,6 +508,10 @@ void configSetCommand(redisClient *c) { if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0 || ll > LONG_MAX) goto badfmt; server.maxidletime = ll; + } else if (!strcasecmp(c->argv[2]->ptr,"tcp-keepalive")) { + if (getLongLongFromObject(o,&ll) == REDIS_ERR || + ll < 0 || ll > INT_MAX) goto badfmt; + server.tcpkeepalive = ll; } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) { if (!strcasecmp(o->ptr,"no")) { server.aof_fsync = AOF_FSYNC_NO; @@ -770,6 +779,7 @@ void configGetCommand(redisClient *c) { config_get_numerical_field("maxmemory",server.maxmemory); config_get_numerical_field("maxmemory-samples",server.maxmemory_samples); config_get_numerical_field("timeout",server.maxidletime); + config_get_numerical_field("tcp-keepalive",server.tcpkeepalive); config_get_numerical_field("auto-aof-rewrite-percentage", server.aof_rewrite_perc); config_get_numerical_field("auto-aof-rewrite-min-size", diff --git a/src/networking.c b/src/networking.c index 3c28821d0..3f14b17c8 100644 --- a/src/networking.c +++ b/src/networking.c @@ -59,6 +59,8 @@ redisClient *createClient(int fd) { if (fd != -1) { anetNonBlock(NULL,fd); anetEnableTcpNoDelay(NULL,fd); + if (server.tcpkeepalive) + anetKeepAlive(NULL,fd,server.tcpkeepalive); if (aeCreateFileEvent(server.el,fd,AE_READABLE, readQueryFromClient, c) == AE_ERR) { diff --git a/src/redis.c b/src/redis.c index dc37363a7..0a4ffed4c 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1094,6 +1094,7 @@ void initServerConfig() { server.dbnum = REDIS_DEFAULT_DBNUM; server.verbosity = REDIS_NOTICE; server.maxidletime = REDIS_MAXIDLETIME; + server.tcpkeepalive = 0; server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN; server.saveparams = NULL; server.loading = 0; diff --git a/src/redis.h b/src/redis.h index 19fe877e0..66250ba3d 100644 --- a/src/redis.h +++ b/src/redis.h @@ -551,6 +551,7 @@ struct redisServer { /* Configuration */ int verbosity; /* Loglevel in redis.conf */ int maxidletime; /* Client timeout in seconds */ + int tcpkeepalive; /* Set SO_KEEPALIVE if non-zero. */ size_t client_max_querybuf_len; /* Limit for client query buffer length */ int dbnum; /* Total number of configured DBs */ int daemonize; /* True if running as a daemon */ -- cgit v1.2.1 From 0fe052efaa6b7490b29a0073826ca3d1cc6a4754 Mon Sep 17 00:00:00 2001 From: antirez Date: Fri, 8 Feb 2013 17:03:11 +0100 Subject: tcp-keepalive option documented in redis.conf. --- redis.conf | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/redis.conf b/redis.conf index c53c72006..120e40577 100644 --- a/redis.conf +++ b/redis.conf @@ -39,6 +39,17 @@ port 6379 # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 +# TCP keepalive. +# +# When this option is set to a non-zero value, SO_KEEPALIVE option will be +# enabled in order to send ACKs just to avoid connection drops or to detect +# dead peers. +# +# The value you specify with this option is the period (in seconds) we use +# in order to refresh the connection with TCP ACKs, however the period is +# only actually set on Linux. Other kernels will use the system-wide default. +tcp-keepalive 0 + # Specify the server verbosity level. # This can be one of: # debug (a lot of information, useful for development/testing) -- cgit v1.2.1 From 2d89c53d0d9c4a29ab299568b90137ff6926b6f7 Mon Sep 17 00:00:00 2001 From: antirez Date: Fri, 8 Feb 2013 17:06:01 +0100 Subject: Tcp keep-alive: send three probes before detectin an error. Otherwise we end with less reliable connections because it's too easy that a single packet gets lost. --- src/anet.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/anet.c b/src/anet.c index 82bca5534..963b6688e 100644 --- a/src/anet.c +++ b/src/anet.c @@ -100,15 +100,19 @@ int anetKeepAlive(char *err, int fd, int interval) return ANET_ERR; } - /* Send next probes after interval. */ - val = interval; + /* Send next probes after the specified interval. Note that we set the + * delay as interval / 3, as we send three probes before detecting + * an error (see the next setsockopt call). */ + val = interval/3; + if (val == 0) val = 1; if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) { anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno)); return ANET_ERR; } - /* Consider the socket in error state after just one missing ACK reply. */ - val = 1; + /* Consider the socket in error state after three we send three ACK + * probes without getting a reply. */ + val = 3; if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) { anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno)); return ANET_ERR; -- cgit v1.2.1 From e88517ac30dbc166e047cc7e399752a36b50cb5e Mon Sep 17 00:00:00 2001 From: antirez Date: Sat, 9 Feb 2013 01:17:59 +0100 Subject: TCP keep-alive. Better documentation in redis.conf. --- redis.conf | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/redis.conf b/redis.conf index 120e40577..099a8e111 100644 --- a/redis.conf +++ b/redis.conf @@ -41,13 +41,18 @@ timeout 0 # TCP keepalive. # -# When this option is set to a non-zero value, SO_KEEPALIVE option will be -# enabled in order to send ACKs just to avoid connection drops or to detect -# dead peers. +# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence +# of communication. This is useful for two reasons: # -# The value you specify with this option is the period (in seconds) we use -# in order to refresh the connection with TCP ACKs, however the period is -# only actually set on Linux. Other kernels will use the system-wide default. +# 1) Detect dead peers. +# 2) Take the connection alive from the point of view of network +# equipment in the middle. +# +# On Linux, the specified value (in seconds) is the period used to send ACKs. +# Note that to close the connection the double of the time is needed. +# On other kernels the period depends on the kernel configuration. +# +# A reasonable value for this option is 60 seconds. tcp-keepalive 0 # Specify the server verbosity level. -- cgit v1.2.1 From fe97d7b1db826da5368e4b56cecd515c0eae860b Mon Sep 17 00:00:00 2001 From: antirez Date: Mon, 11 Feb 2013 12:11:21 +0100 Subject: Makefile: valgrind target added (forces -O0 and libc malloc). --- src/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 204a27148..e20955833 100644 --- a/src/Makefile +++ b/src/Makefile @@ -217,7 +217,10 @@ gcov: $(MAKE) REDIS_CFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" REDIS_LDFLAGS="-fprofile-arcs -ftest-coverage" noopt: - $(MAKE) OPT="-O0" + $(MAKE) OPTIMIZATION="-O0" + +valgrind: + $(MAKE) OPTIMIZATION="-O0" MALLOC="libc" src/help.h: @../utils/generate-command-help.rb > help.h -- cgit v1.2.1 From 948fdb5f7d20ccd795e8750f60d205022fcee410 Mon Sep 17 00:00:00 2001 From: antirez Date: Mon, 11 Feb 2013 13:24:39 +0100 Subject: Redis 2.6.10 --- 00-RELEASENOTES | 21 +++++++++++++++++++++ src/version.h | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/00-RELEASENOTES b/00-RELEASENOTES index 2c5010660..3306784e5 100644 --- a/00-RELEASENOTES +++ b/00-RELEASENOTES @@ -14,6 +14,27 @@ HIGH: There is a critical bug that may affect a subset of users. Upgrade! CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP. -------------------------------------------------------------------------------- +--[ Redis 2.6.10 ] + +UPGRADE URGENCY: MODERATE, this release contains many non-critical fixes + and many small improvements. + +* [BUGFIX] redis-cli --rdb, fixed when the server sends newlines to ping. +* [BUGFIX] redis-cli, minor fixes on connection handling, prompt. +* [BUGFIX] Slow log: don't log EXEC, just executed commands. +* [BUGFIX] On failed shutdown don't try again and again compulsively. +* [BUGFIX] Fix build on sunos without backtrace(). +* [BUGFIX] UNSUBSCRIBE and PUNSUBSCRIBE: always provide a reply (see 742e580) +* [BUGFIX] Lua struct library was broken, upgraded. +* [BUGFIX] Fix a bug in srandmemberWithCountCommand() with count argument. +* [BUGFIX] Test: disable clients timeout to prevent issues on slow systems. +* [BUGFIX] Sentinel: don't advertise the promoted slave as master too early. +* [IMPROVED] Whitelist SIGUSR1, see http://redis.io/topics/signals. +* [IMPROVED] Simpler to understand redis-cli --bigkeys output. +* [IMPROVED] Test now works with tclsh > 8.5. +* [IMPROVED] Added option to turn of the Nagle algorithm in slave socket. +* [IMPROVED] Optionally use SO_KEEPALIVE to detect dead peers. + --[ Redis 2.6.9 ] UPGRADE URGENCY: MODERATE if you use replication. diff --git a/src/version.h b/src/version.h index 293bd1bfb..765dc9100 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define REDIS_VERSION "2.6.9" +#define REDIS_VERSION "2.6.10" -- cgit v1.2.1