Commit 45b1b2f7 authored by charsyam's avatar charsyam Committed by antirez
Browse files

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.
parent eb07793b
...@@ -75,9 +75,8 @@ int anetNonBlock(char *err, int fd) ...@@ -75,9 +75,8 @@ int anetNonBlock(char *err, int fd)
return ANET_OK; 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) if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1)
{ {
anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno)); anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno));
...@@ -86,6 +85,17 @@ int anetTcpNoDelay(char *err, int fd) ...@@ -86,6 +85,17 @@ int anetTcpNoDelay(char *err, int fd)
return ANET_OK; 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) int anetSetSendBuffer(char *err, int fd, int buffsize)
{ {
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1) if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1)
......
...@@ -52,6 +52,7 @@ int anetUnixAccept(char *err, int serversock); ...@@ -52,6 +52,7 @@ int anetUnixAccept(char *err, int serversock);
int anetWrite(int fd, char *buf, int count); int anetWrite(int fd, char *buf, int count);
int anetNonBlock(char *err, int fd); int anetNonBlock(char *err, int fd);
int anetTcpNoDelay(char *err, int fd); int anetTcpNoDelay(char *err, int fd);
int anetTcpNoDelayOff(char *err, int fd);
int anetTcpKeepAlive(char *err, int fd); int anetTcpKeepAlive(char *err, int fd);
int anetPeerToString(int fd, char *ip, int *port); int anetPeerToString(int fd, char *ip, int *port);
......
...@@ -378,6 +378,8 @@ void loadServerConfigFromString(char *config) { ...@@ -378,6 +378,8 @@ void loadServerConfigFromString(char *config) {
if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) { if ((server.stop_writes_on_bgsave_err = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr; 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) { } else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) {
server.slave_priority = atoi(argv[1]); server.slave_priority = atoi(argv[1]);
} else if (!strcasecmp(argv[0],"sentinel")) { } else if (!strcasecmp(argv[0],"sentinel")) {
...@@ -697,6 +699,10 @@ void configSetCommand(redisClient *c) { ...@@ -697,6 +699,10 @@ void configSetCommand(redisClient *c) {
if (yn == -1) goto badfmt; if (yn == -1) goto badfmt;
server.rdb_checksum = yn; 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")) { } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
ll <= 0) goto badfmt; ll <= 0) goto badfmt;
...@@ -790,6 +796,7 @@ void configGetCommand(redisClient *c) { ...@@ -790,6 +796,7 @@ void configGetCommand(redisClient *c) {
config_get_numerical_field("repl-timeout",server.repl_timeout); config_get_numerical_field("repl-timeout",server.repl_timeout);
config_get_numerical_field("maxclients",server.maxclients); config_get_numerical_field("maxclients",server.maxclients);
config_get_numerical_field("watchdog-period",server.watchdog_period); 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); config_get_numerical_field("slave-priority",server.slave_priority);
/* Bool (yes/no) values */ /* Bool (yes/no) values */
......
...@@ -1160,6 +1160,7 @@ void initServerConfig() { ...@@ -1160,6 +1160,7 @@ void initServerConfig() {
server.repl_serve_stale_data = 1; server.repl_serve_stale_data = 1;
server.repl_slave_ro = 1; server.repl_slave_ro = 1;
server.repl_down_since = time(NULL); server.repl_down_since = time(NULL);
server.slave_tcp_nodelay_off = 1;
server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY; server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;
/* Client output buffer limits */ /* Client output buffer limits */
......
...@@ -616,6 +616,7 @@ struct redisServer { ...@@ -616,6 +616,7 @@ struct redisServer {
int repl_serve_stale_data; /* Serve stale data when link is down? */ int repl_serve_stale_data; /* Serve stale data when link is down? */
int repl_slave_ro; /* Slave is read only? */ int repl_slave_ro; /* Slave is read only? */
time_t repl_down_since; /* Unix time at which link with master went down */ 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. */ int slave_priority; /* Reported in INFO and used by Sentinel. */
/* Limits */ /* Limits */
unsigned int maxclients; /* Max number of simultaneous clients */ unsigned int maxclients; /* Max number of simultaneous clients */
......
...@@ -118,6 +118,14 @@ void syncCommand(redisClient *c) { ...@@ -118,6 +118,14 @@ void syncCommand(redisClient *c) {
/* ignore SYNC if already slave or in monitor mode */ /* ignore SYNC if already slave or in monitor mode */
if (c->flags & REDIS_SLAVE) return; 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 /* Refuse SYNC requests if we are a slave but the link with our master
* is not ok... */ * is not ok... */
if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED) { if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment