diff --git a/redis.conf b/redis.conf index 9eed0cabc73f3117ad993564a53c8ebbeea88e08..c53c7200617fabee995b893bb6c1f082b8cc5001 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 d002cb31ca38abe9103d90208bff6dd86b3bd939..3f037dcad580fc8a6925327179b535127a56e302 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 56ae50573ebb012b6b74453a8df11582c1933516..dbf36dbd81f9e26a6e0a78377e5a48292e9a3a21 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 1510f7b51029ebb80b9f13ebf040aac5d69cada7..45dc6b7024d9de3a4f7594fd7df975d508cac761 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 6e5d32489861b802643259218061827ab2f0a621..3c28821d085c648cd9f64fa41d16687582e52bba 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 51c36e8ad3b977da88b0e8d0ef508825887ab8d1..be2e1931263d6ab14f3e6ec6202dbc97300665ca 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 7f9ca6efe0edd93907fc2be241e4729f2e4b429a..19fe877e07bed303dcada9bb7f504fb3d760d945 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 4fa62f255f0ce7a2bb4b35ee3c6f36f0313e2cd0..c12692ce233d0f0e67c7841715dfbfbb5133f7c0 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;