Skip to content

Commit

Permalink
Merged tag 3.0.5 from antirez/3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
enricogior committed Oct 27, 2015
1 parent cab66d7 commit 9143f42
Show file tree
Hide file tree
Showing 18 changed files with 778 additions and 303 deletions.
72 changes: 72 additions & 0 deletions 00-RELEASENOTES
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,78 @@ 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 3.0.5 ] Release date: 15 Oct 2015

Upgrade urgency: MODERATE, the most important thing is a fix in the replication
code that may make the slave hanging forever if the master
remains with an open socket even if it is no longer able to
reply.

* [FIX] MOVE now moves the TTL as well. A bug lasting forever... finally
fixed thanks to Andy Grunwald that reported it.
(reported by Andy Grunwald, fixed by Salvatore Sanfilippo)
* [FIX] Fix a false positive in HSTRLEN test.
* [FIX] Fix a bug in redis-cli --pipe mode that was not able to read back
replies from the server incrementally. Now a mass import will use
a lot less memory, and you can use --pipe to do incremental streaming.
(reported by Twitter user @fsaintjacques, fixed by Salvatore
Sanfilippo)
* [FIX] Slave detection of master timeout. (fixed by Kevin McGehee, refactoring
and regression test by Salvatore Sanfilippo)

* [NEW] Cluster: redis-trib fix can fix an additional case for opens lots.
(Salvatore Sanfilippo)
* [NEW] Cluster: redis-trib import support for --copy and --replace options
(David Thomson)

--[ Redis 3.0.4 ] Release date: 8 Sep 2015

Upgrade urgency: HIGH for Redis and Sentinel. However note that in order to
fix certain replication bugs, the replication internals were
modified in a very heavy way. So while this release is
conceptually saner, it may contain regressions. For this
reason, before the release, QA activities were performed by
me (antirez) and Redis Labs and no evident bug was found.

* [FIX] A number of bugs related to replication PSYNC and the (yet experimental)
diskless replication feature were fixed. The bugs could lead to
inconsistency between masters and slaves. (Salvatore Sanfilippo, Oran
Agra fixed the issue found by Yuval Inbar)
* [FIX] A replication bug in the context of PSYNC partial resynchonization was
found and fixed. This bug happens even when diskless replication is off
in the case different slaves connect at different times while the master
is creating an RDB file, and later a partial resynchronization is
attempted by a slave that connected not as the first one. (Salvatore
Sanfilippo, Oran Agra)
* [FIX] Chained replication and PSYNC interactions leading to potential stale
chained slaves data set, see issue #2694. (Salvatore Sanfilippo fixed
an issue reported by "GeorgeBJ" user at Github)
* [FIX] redis-cli --scan iteration fixed when returned cursor overflows
32 bit signed integer. (Ofir Luzon, Yuval Inbar)
* [FIX] Sentinel: fixed a bug during the master switch process, where for a
failed conditional check, the new configuration is rewritten, during
a small window of time, in a corrupted way where the master is
also reported to be one of the slaves. This bug is rare to trigger
but apparently it happens in the wild, and the effect is to see
a replication loop where the master will try to replicate with itself.
A detailed explanation of the bug and its effects can be found in
the commit message here: https://github.com/antirez/redis/commit/c20218eb5770b2cafb12bc7092313b8358fedc0a.
The bug was found by Jan-Erik Rediger using a static analyzer and
fixed by Salvatore Sanfilippo.
* [FIX] Sentinel lack of arity checks for certain commands.
(Rogerio Goncalves, Salvatore Sanfilippo)

* [NEW] Replication internals rewritten in order to be more resistant to bugs.
The replication handshake in the slave side was rewritten as a non
blocking state machine. (Salvatore Sanfilippo, Oran Agra)
* [NEW] New "replication capabilities" feature introduced in order to signal
from the master to the slave what are the features supported, so that
the master can choose the kind of replication to start (diskless or
not) when master and slave are of different versions. (Oran Agra,
Salvatore Sanfilippo)
* [NEW] Log clients details when SLAVEOF command is received. (Salvatore
Sanfilippo with inputs from Nick Craver and Marc Gravell).

--[ Redis 3.0.3 ] Release date: 17 Jul 2015

Upgrade urgency: LOW for Redis and Sentinel.
Expand Down
7 changes: 6 additions & 1 deletion redis.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Redis configuration file example
# Redis configuration file example.
#
# Note that in order to read the configuration file, Redis must be
# started with the file path as first argument:
#
# ./redis-server /path/to/redis.conf

# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
Expand Down
4 changes: 3 additions & 1 deletion src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ void moveCommand(redisClient *c) {
robj *o;
redisDb *src, *dst;
int srcid;
PORT_LONGLONG dbid;
PORT_LONGLONG dbid, expire;

if (server.cluster_enabled) {
addReplyError(c,"MOVE is not allowed in cluster mode");
Expand Down Expand Up @@ -756,13 +756,15 @@ void moveCommand(redisClient *c) {
addReply(c,shared.czero);
return;
}
expire = getExpire(c->db,c->argv[1]);

/* Return zero if the key already exists in the target DB */
if (lookupKeyWrite(dst,c->argv[1]) != NULL) {
addReply(c,shared.czero);
return;
}
dbAdd(dst,c->argv[1],o);
if (expire != -1) setExpire(dst,c->argv[1],expire);
incrRefCount(o);

/* OK! key moved, free the entry in the source DB */
Expand Down
21 changes: 7 additions & 14 deletions src/networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ redisClient *createClient(int fd) {
c->repl_ack_off = 0;
c->repl_ack_time = 0;
c->slave_listening_port = 0;
c->slave_capa = SLAVE_CAPA_NONE;
c->reply = listCreate();
c->reply_bytes = 0;
c->obuf_soft_limit_reached_time = 0;
Expand Down Expand Up @@ -683,20 +684,6 @@ void disconnectSlaves(void) {
}
}

/* This function is called when the slave lose the connection with the
* master into an unexpected way. */
void replicationHandleMasterDisconnection(void) {
server.master = NULL;
server.repl_state = REDIS_REPL_CONNECT;
server.repl_down_since = server.unixtime;
/* We lost connection with our master, force our slaves to resync
* with us as well to load the new data set.
*
* If server.masterhost is NULL the user called SLAVEOF NO ONE so
* slave resync is not needed. */
if (server.masterhost != NULL) disconnectSlaves();
}

void freeClient(redisClient *c) {
listNode *ln;

Expand Down Expand Up @@ -1823,6 +1810,12 @@ void flushSlavesOutputBuffers(void) {
redisClient *slave = listNodeValue(ln);
int events;

/* Note that the following will not flush output buffers of slaves
* in STATE_ONLINE but having put_online_on_ack set to true: in this
* case the writable event is never installed, since the purpose
* of put_online_on_ack is to postpone the moment it is installed.
* This is what we want since slaves in this state should not receive
* writes before the first ACK. */
events = aeGetFileEvents(server.el,slave->fd);
if (events & AE_WRITABLE &&
slave->replstate == REDIS_REPL_ONLINE &&
Expand Down
36 changes: 26 additions & 10 deletions src/rdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1450,7 +1450,7 @@ int rdbSaveToSlavesSockets(void) {
if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) {
clientids[numfds] = slave->id;
fds[numfds++] = slave->fd;
slave->replstate = REDIS_REPL_WAIT_BGSAVE_END;
replicationSetupSlaveForFullResync(slave,getPsyncInitialOffset());
/* Put the socket in non-blocking mode to simplify RDB transfer.
* We'll restore it when the children returns (since duped socket
* will share the O_NONBLOCK attribute with the parent). */
Expand Down Expand Up @@ -1533,25 +1533,41 @@ int rdbSaveToSlavesSockets(void) {
} else {
#endif
/* Parent */
zfree(clientids); /* Not used by parent. Free ASAP. */
server.stat_fork_time = ustime()-start;
server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
if (childpid == -1) {
redisLog(REDIS_WARNING,"Can't save in background: fork: %s",
strerror(errno));
zfree(fds);

/* Undo the state change. The caller will perform cleanup on
* all the slaves in BGSAVE_START state, but an early call to
* replicationSetupSlaveForFullResync() turned it into BGSAVE_END */
listRewind(server.slaves,&li);
while((ln = listNext(&li))) {
redisClient *slave = ln->value;
int j;

for (j = 0; j < numfds; j++) {
if (slave->id == clientids[j]) {
slave->replstate = REDIS_REPL_WAIT_BGSAVE_START;
break;
}
}
}
close(pipefds[0]);
close(pipefds[1]);
return REDIS_ERR;
} else {
redisLog(REDIS_NOTICE,"Background RDB transfer started by pid %d",
childpid);
server.rdb_save_time_start = time(NULL);
server.rdb_child_pid = childpid;
server.rdb_child_type = REDIS_RDB_CHILD_TYPE_SOCKET;
updateDictResizePolicy();
}
redisLog(REDIS_NOTICE,"Background RDB transfer started by pid %d",childpid);
server.rdb_save_time_start = time(NULL);
server.rdb_child_pid = childpid;
server.rdb_child_type = REDIS_RDB_CHILD_TYPE_SOCKET;
updateDictResizePolicy();
zfree(clientids);
zfree(fds);
return REDIS_OK;
return (childpid == -1) ? REDIS_ERR : REDIS_OK;
#ifndef _WIN32
}
#endif
Expand Down
9 changes: 7 additions & 2 deletions src/redis-cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -1440,6 +1440,7 @@ static void getRDB(void) {
* Bulk import (pipe) mode
*--------------------------------------------------------------------------- */

#define PIPEMODE_WRITE_LOOP_MAX_BYTES (128*1024)
static void pipeMode(void) {
int fd = (int)context->fd;
PORT_LONGLONG errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0;
Expand Down Expand Up @@ -1521,6 +1522,8 @@ static void pipeMode(void) {

/* Handle the writable state: we can send protocol to the server. */
if (mask & AE_WRITABLE) {
ssize_t loop_nwritten = 0;

while(1) {
/* Transfer current buffer to server. */
if (obuf_len != 0) {
Expand All @@ -1537,6 +1540,7 @@ static void pipeMode(void) {
}
obuf_len -= nwritten;
obuf_pos += nwritten;
loop_nwritten += nwritten;
if (obuf_len != 0) break; /* Can't accept more data. */
}
/* If buffer is empty, load from stdin. */
Expand Down Expand Up @@ -1572,7 +1576,8 @@ static void pipeMode(void) {
obuf_pos = 0;
}
}
if (obuf_len == 0 && eof) break;
if ((obuf_len == 0 && eof) ||
loop_nwritten > PIPEMODE_WRITE_LOOP_MAX_BYTES) break;
}
}

Expand Down Expand Up @@ -1630,7 +1635,7 @@ static redisReply *sendScan(PORT_ULONGLONG *it) {
assert(reply->element[1]->type == REDIS_REPLY_ARRAY);

/* Update iterator */
*it = atoi(reply->element[0]->str);
*it = strtoull(reply->element[0]->str, NULL, 10);

return reply;
}
Expand Down
25 changes: 20 additions & 5 deletions src/redis-trib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,10 @@ def fix_open_slot(slot)
# importing state in 1 slot. That's trivial to address.
if migrating.length == 1 && importing.length == 1
move_slot(migrating[0],importing[0],slot,:verbose=>true,:fix=>true)
# Case 2: There are multiple nodes that claim the slot as importing,
# they probably got keys about the slot after a restart so opened
# the slot. In this case we just move all the keys to the owner
# according to the configuration.
elsif migrating.length == 0 && importing.length > 0
xputs ">>> Moving all the #{slot} slot keys to its owner #{owner}"
importing.each {|node|
Expand All @@ -504,8 +508,14 @@ def fix_open_slot(slot)
xputs ">>> Setting #{slot} as STABLE in #{node}"
node.r.cluster("setslot",slot,"stable")
}
# Case 3: There are no slots claiming to be in importing state, but
# there is a migrating node that actually don't have any key. We
# can just close the slot, probably a reshard interrupted in the middle.
elsif importing.length == 0 && migrating.length == 1 &&
migrating[0].r.cluster("getkeysinslot",slot,10).length == 0
migrating[0].r.cluster("setslot",slot,"stable")
else
xputs "[ERR] Sorry, Redis-trib can't fix this slot yet (work in progress)"
xputs "[ERR] Sorry, Redis-trib can't fix this slot yet (work in progress). Slot is set as migrating in #{migrating.join(",")}, as importing in #{importing.join(",")}, owner is #{owner}"
end
end

Expand Down Expand Up @@ -812,7 +822,7 @@ def move_slot(source,target,slot,o={})
source.r.client.call(["migrate",target.info[:host],target.info[:port],key,0,15000])
rescue => e
if o[:fix] && e.to_s =~ /BUSYKEY/
xputs "*** Target key #{key} exists. Replace it for FIX."
xputs "*** Target key #{key} exists. Replacing it for FIX."
source.r.client.call(["migrate",target.info[:host],target.info[:port],key,0,15000,:replace])
else
puts ""
Expand Down Expand Up @@ -1139,7 +1149,9 @@ def call_cluster_cmd(argv,opt)
def import_cluster_cmd(argv,opt)
source_addr = opt['from']
xputs ">>> Importing data from #{source_addr} to cluster #{argv[1]}"

use_copy = opt['copy']
use_replace = opt['replace']

# Check the existing cluster.
load_cluster_info_from_node(argv[0])
check_cluster
Expand Down Expand Up @@ -1174,7 +1186,10 @@ def import_cluster_cmd(argv,opt)
print "Migrating #{k} to #{target}: "
STDOUT.flush
begin
source.client.call(["migrate",target.info[:host],target.info[:port],k,0,15000])
cmd = ["migrate",target.info[:host],target.info[:port],k,0,15000]
cmd << :copy if use_copy
cmd << :replace if use_replace
source.client.call(cmd)
rescue => e
puts e
else
Expand Down Expand Up @@ -1334,7 +1349,7 @@ def key_to_slot(key)
ALLOWED_OPTIONS={
"create" => {"replicas" => true},
"add-node" => {"slave" => false, "master-id" => true},
"import" => {"from" => :required},
"import" => {"from" => :required, "copy" => false, "replace" => false},
"reshard" => {"from" => true, "to" => true, "slots" => true, "yes" => false}
}
Expand Down
2 changes: 1 addition & 1 deletion src/redis.c
Original file line number Diff line number Diff line change
Expand Up @@ -3730,14 +3730,14 @@ int main(int argc, char **argv) {
if (server.daemonize) createPidFile();
redisSetProcTitle(argv[0]);
redisAsciiArt();
checkTcpBacklogSettings();

if (!server.sentinel_mode) {
/* Things not needed when running in Sentinel mode. */
redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);
#ifdef __linux__
linuxMemoryWarnings();
#endif
checkTcpBacklogSettings();
loadDataFromDisk();
if (server.cluster_enabled) {
if (verifyClusterConfigWithData() == REDIS_ERR) {
Expand Down
Loading

0 comments on commit 9143f42

Please sign in to comment.