summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/Makefile5
-rw-r--r--src/anet.c63
-rw-r--r--src/anet.h4
-rwxr-xr-xsrc/config.c21
-rw-r--r--src/networking.c4
-rwxr-xr-xsrc/redis.c6
-rwxr-xr-xsrc/redis.h2
-rwxr-xr-xsrc/replication.c7
-rw-r--r--src/sentinel.c10
-rw-r--r--src/t_set.c4
-rw-r--r--src/version.h2
-rw-r--r--src/ziplist.c4
12 files changed, 117 insertions, 15 deletions
diff --git a/src/Makefile b/src/Makefile
index ebf5786e2..7a553d030 100755
--- a/src/Makefile
+++ b/src/Makefile
@@ -241,7 +241,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:
@$(SRCDIR)/../utils/generate-command-help.rb > help.h
diff --git a/src/anet.c b/src/anet.c
index 4da3e28db..963b6688e 100644
--- a/src/anet.c
+++ b/src/anet.c
@@ -75,10 +75,56 @@ int anetNonBlock(char *err, int fd)
return ANET_OK;
}
-int anetTcpNoDelay(char *err, int fd)
+/* 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 yes = 1;
- if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1)
+ 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 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 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;
+ }
+#endif
+
+ return ANET_OK;
+}
+
+static int anetSetTcpNoDelay(char *err, int fd, int val)
+{
+ if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1)
{
anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno));
return ANET_ERR;
@@ -86,6 +132,17 @@ int anetTcpNoDelay(char *err, int fd)
return ANET_OK;
}
+int anetEnableTcpNoDelay(char *err, int fd)
+{
+ return anetSetTcpNoDelay(err, fd, 1);
+}
+
+int anetDisableTcpNoDelay(char *err, int fd)
+{
+ return anetSetTcpNoDelay(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..696c2c225 100644
--- a/src/anet.h
+++ b/src/anet.h
@@ -51,8 +51,10 @@ 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 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
diff --git a/src/config.c b/src/config.c
index 71157313c..dbea5513d 100755
--- 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) {
@@ -238,6 +243,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) {
@@ -521,6 +530,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;
@@ -719,6 +732,11 @@ void configSetCommand(redisClient *c) {
if (yn == -1) goto badfmt;
server.rdb_checksum = yn;
+ } else if (!strcasecmp(c->argv[2]->ptr,"repl-disable-tcp-nodelay")) {
+ int yn = yesnotoi(o->ptr);
+
+ 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;
@@ -784,6 +802,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",
@@ -828,6 +847,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..3f14b17c8 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -58,7 +58,9 @@ 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 (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 9b4e1ac28..e6faf8ada 100755
--- a/src/redis.c
+++ b/src/redis.c
@@ -215,7 +215,7 @@ struct redisCommand redisCommandTable[] = {
{"bgsaveto",bgsavetoCommand,2,"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},
@@ -541,7 +541,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 */
@@ -1187,6 +1187,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;
@@ -1256,6 +1257,7 @@ void initServerConfig() {
server.repl_serve_stale_data = 1;
server.repl_slave_ro = 1;
server.repl_down_since = time(NULL);
+ 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 3319aa87b..b4d58db40 100755
--- a/src/redis.h
+++ b/src/redis.h
@@ -574,6 +574,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 */
@@ -647,6 +648,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 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 9c6d4dff7..7fbc4591b 100755
--- a/src/replication.c
+++ b/src/replication.c
@@ -276,7 +276,11 @@ void syncCommand(redisClient *c) {
}
c->repldbfd = -1;
}
-
+
+ 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;
listAddNodeTail(server.slaves,c);
@@ -539,6 +543,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
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);
diff --git a/src/t_set.c b/src/t_set.c
index f384dc76c..2cdcf86c6 100644
--- a/src/t_set.c
+++ b/src/t_set.c
@@ -451,7 +451,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;
}
@@ -473,7 +473,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);
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"
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;