Hello community, here is the log from the commit of package redis for openSUSE:Factory checked in at 2015-11-26 17:02:00 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/redis (Old) and /work/SRC/openSUSE:Factory/.redis.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "redis" Changes: -------- --- /work/SRC/openSUSE:Factory/redis/redis.changes 2015-09-27 08:39:46.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.redis.new/redis.changes 2015-11-26 17:02:20.000000000 +0100 @@ -1,0 +2,17 @@ +Sun Nov 22 09:18:54 UTC 2015 - mpluskal@suse.com + +- Update to 3.0.5 + * [FIX] MOVE now moves the TTL as well. A bug lasting forever... + finally fixed thanks to Andy Grunwald that reported it. + * [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. + * [FIX] Slave detection of master timeout. + * [NEW] Cluster: redis-trib fix can fix an additional case for + opens lots. + * [NEW] Cluster: redis-trib import support for --copy and + --replace options + +------------------------------------------------------------------- Old: ---- redis-3.0.4.tar.gz New: ---- redis-3.0.5.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ redis.spec ++++++ --- /var/tmp/diff_new_pack.JlfYQy/_old 2015-11-26 17:02:21.000000000 +0100 +++ /var/tmp/diff_new_pack.JlfYQy/_new 2015-11-26 17:02:21.000000000 +0100 @@ -27,7 +27,7 @@ %define _conf_dir %{_sysconfdir}/%{name} Name: redis -Version: 3.0.4 +Version: 3.0.5 Release: 0 Summary: Persistent key-value database License: BSD-3-Clause ++++++ redis-3.0.4.tar.gz -> redis-3.0.5.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/00-RELEASENOTES new/redis-3.0.5/00-RELEASENOTES --- old/redis-3.0.4/00-RELEASENOTES 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/00-RELEASENOTES 2015-10-15 15:44:54.000000000 +0200 @@ -10,6 +10,30 @@ 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 @@ -34,7 +58,7 @@ 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] Senitnel: fixed a bug during the master switch process, where for a +* [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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/redis.conf new/redis-3.0.5/redis.conf --- old/redis-3.0.4/redis.conf 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/redis.conf 2015-10-15 15:44:54.000000000 +0200 @@ -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: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/src/db.c new/redis-3.0.5/src/db.c --- old/redis-3.0.4/src/db.c 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/src/db.c 2015-10-15 15:44:54.000000000 +0200 @@ -714,7 +714,7 @@ robj *o; redisDb *src, *dst; int srcid; - long long dbid; + long long dbid, expire; if (server.cluster_enabled) { addReplyError(c,"MOVE is not allowed in cluster mode"); @@ -748,6 +748,7 @@ 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) { @@ -755,6 +756,7 @@ 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 */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/src/redis-cli.c new/redis-3.0.5/src/redis-cli.c --- old/redis-3.0.4/src/redis-cli.c 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/src/redis-cli.c 2015-10-15 15:44:54.000000000 +0200 @@ -1397,6 +1397,7 @@ * Bulk import (pipe) mode *--------------------------------------------------------------------------- */ +#define PIPEMODE_WRITE_LOOP_MAX_BYTES (128*1024) static void pipeMode(void) { int fd = context->fd; long long errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0; @@ -1473,6 +1474,8 @@ /* 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) { @@ -1489,6 +1492,7 @@ } 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. */ @@ -1524,7 +1528,8 @@ obuf_pos = 0; } } - if (obuf_len == 0 && eof) break; + if ((obuf_len == 0 && eof) || + loop_nwritten > PIPEMODE_WRITE_LOOP_MAX_BYTES) break; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/src/redis-trib.rb new/redis-3.0.5/src/redis-trib.rb --- old/redis-3.0.4/src/redis-trib.rb 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/src/redis-trib.rb 2015-10-15 15:44:54.000000000 +0200 @@ -496,6 +496,10 @@ # 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| @@ -504,8 +508,14 @@ 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 @@ -812,7 +822,7 @@ 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 "" @@ -1139,7 +1149,9 @@ 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 @@ -1174,7 +1186,10 @@ 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 @@ -1334,7 +1349,7 @@ 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} } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/src/redis.h new/redis-3.0.5/src/redis.h --- old/redis-3.0.4/src/redis.h 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/src/redis.h 2015-10-15 15:44:54.000000000 +0200 @@ -272,6 +272,7 @@ #define REDIS_REPL_NONE 0 /* No active replication */ #define REDIS_REPL_CONNECT 1 /* Must connect to master */ #define REDIS_REPL_CONNECTING 2 /* Connecting to master */ +/* --- Handshake states, must be ordered --- */ #define REDIS_REPL_RECEIVE_PONG 3 /* Wait for PING reply */ #define REDIS_REPL_SEND_AUTH 4 /* Send AUTH to master */ #define REDIS_REPL_RECEIVE_AUTH 5 /* Wait for AUTH reply */ @@ -281,6 +282,7 @@ #define REDIS_REPL_RECEIVE_CAPA 9 /* Wait for REPLCONF reply */ #define REDIS_REPL_SEND_PSYNC 10 /* Send PSYNC */ #define REDIS_REPL_RECEIVE_PSYNC 11 /* Wait for PSYNC reply */ +/* --- End of handshake states --- */ #define REDIS_REPL_TRANSFER 12 /* Receiving .rdb from master */ #define REDIS_REPL_CONNECTED 13 /* Connected to master */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/src/replication.c new/redis-3.0.5/src/replication.c --- old/redis-3.0.4/src/replication.c 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/src/replication.c 2015-10-15 15:44:54.000000000 +0200 @@ -909,6 +909,13 @@ /* ----------------------------------- SLAVE -------------------------------- */ +/* Returns 1 if the given replication state is a handshake state, + * 0 otherwise. */ +int slaveIsInHandshakeState(void) { + return server.repl_state >= REDIS_REPL_RECEIVE_PONG && + server.repl_state <= REDIS_REPL_RECEIVE_PSYNC; +} + /* Abort the async download of the bulk dataset while SYNC-ing with master */ void replicationAbortSyncTransfer(void) { redisAssert(server.repl_state == REDIS_REPL_TRANSFER); @@ -1196,6 +1203,7 @@ return sdscatprintf(sdsempty(),"-Reading from master: %s", strerror(errno)); } + server.repl_transfer_lastio = server.unixtime; return sdsnew(buf); } return NULL; @@ -1626,7 +1634,7 @@ int fd = server.repl_transfer_s; redisAssert(server.repl_state == REDIS_REPL_CONNECTING || - server.repl_state == REDIS_REPL_RECEIVE_PONG); + slaveIsInHandshakeState()); aeDeleteFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE); close(fd); server.repl_transfer_s = -1; @@ -1645,7 +1653,7 @@ if (server.repl_state == REDIS_REPL_TRANSFER) { replicationAbortSyncTransfer(); } else if (server.repl_state == REDIS_REPL_CONNECTING || - server.repl_state == REDIS_REPL_RECEIVE_PONG) + slaveIsInHandshakeState()) { undoConnectWithMaster(); } else { @@ -1780,22 +1788,17 @@ addReplyBulkCBuffer(c,"slave",5); addReplyBulkCString(c,server.masterhost); addReplyLongLong(c,server.masterport); - switch(server.repl_state) { - case REDIS_REPL_NONE: slavestate = "none"; break; - case REDIS_REPL_CONNECT: slavestate = "connect"; break; - case REDIS_REPL_CONNECTING: slavestate = "connecting"; break; - case REDIS_REPL_RECEIVE_PONG: - case REDIS_REPL_SEND_AUTH: - case REDIS_REPL_RECEIVE_AUTH: - case REDIS_REPL_SEND_PORT: - case REDIS_REPL_RECEIVE_PORT: - case REDIS_REPL_SEND_CAPA: - case REDIS_REPL_RECEIVE_CAPA: - case REDIS_REPL_SEND_PSYNC: - case REDIS_REPL_RECEIVE_PSYNC: slavestate = "handshake"; break; - case REDIS_REPL_TRANSFER: slavestate = "sync"; break; - case REDIS_REPL_CONNECTED: slavestate = "connected"; break; - default: slavestate = "unknown"; break; + if (slaveIsInHandshakeState()) { + slavestate = "handshake"; + } else { + switch(server.repl_state) { + case REDIS_REPL_NONE: slavestate = "none"; break; + case REDIS_REPL_CONNECT: slavestate = "connect"; break; + case REDIS_REPL_CONNECTING: slavestate = "connecting"; break; + case REDIS_REPL_TRANSFER: slavestate = "sync"; break; + case REDIS_REPL_CONNECTED: slavestate = "connected"; break; + default: slavestate = "unknown"; break; + } } addReplyBulkCString(c,slavestate); addReplyLongLong(c,server.master ? server.master->reploff : -1); @@ -2186,8 +2189,8 @@ /* Non blocking connection timeout? */ if (server.masterhost && (server.repl_state == REDIS_REPL_CONNECTING || - server.repl_state == REDIS_REPL_RECEIVE_PONG) && - (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout) + slaveIsInHandshakeState()) && + (time(NULL)-server.repl_transfer_lastio) > server.repl_timeout) { redisLog(REDIS_WARNING,"Timeout connecting to the MASTER..."); undoConnectWithMaster(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/src/version.h new/redis-3.0.5/src/version.h --- old/redis-3.0.4/src/version.h 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/src/version.h 2015-10-15 15:44:54.000000000 +0200 @@ -1 +1 @@ -#define REDIS_VERSION "3.0.4" +#define REDIS_VERSION "3.0.5" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/tests/integration/replication.tcl new/redis-3.0.5/tests/integration/replication.tcl --- old/redis-3.0.4/tests/integration/replication.tcl 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/tests/integration/replication.tcl 2015-10-15 15:44:54.000000000 +0200 @@ -1,3 +1,56 @@ +proc log_file_matches {log pattern} { + set fp [open $log r] + set content [read $fp] + close $fp + string match $pattern $content +} + +start_server {tags {"repl"}} { + set slave [srv 0 client] + set slave_host [srv 0 host] + set slave_port [srv 0 port] + set slave_log [srv 0 stdout] + start_server {} { + set master [srv 0 client] + set master_host [srv 0 host] + set master_port [srv 0 port] + + # Configure the master in order to hang waiting for the BGSAVE + # operation, so that the slave remains in the handshake state. + $master config set repl-diskless-sync yes + $master config set repl-diskless-sync-delay 1000 + + # Use a short replication timeout on the slave, so that if there + # are no bugs the timeout is triggered in a reasonable amount + # of time. + $slave config set repl-timeout 5 + + # Start the replication process... + $slave slaveof $master_host $master_port + + test {Slave enters handshake} { + wait_for_condition 50 1000 { + [string match *handshake* [$slave role]] + } else { + fail "Slave does not enter handshake state" + } + } + + # But make the master unable to send + # the periodic newlines to refresh the connection. The slave + # should detect the timeout. + $master debug sleep 10 + + test {Slave is able to detect timeout during handshake} { + wait_for_condition 50 1000 { + [log_file_matches $slave_log "*Timeout connecting to the MASTER*"] + } else { + fail "Slave is not able to detect timeout" + } + } + } +} + start_server {tags {"repl"}} { set A [srv 0 client] set A_host [srv 0 host] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/tests/unit/basic.tcl new/redis-3.0.5/tests/unit/basic.tcl --- old/redis-3.0.4/tests/unit/basic.tcl 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/tests/unit/basic.tcl 2015-10-15 15:44:54.000000000 +0200 @@ -433,6 +433,32 @@ set e } {*ERR*index out of range} + test {MOVE can move key expire metadata as well} { + r select 10 + r flushdb + r select 9 + r set mykey foo ex 100 + r move mykey 10 + assert {[r ttl mykey] == -2} + r select 10 + assert {[r ttl mykey] > 0 && [r ttl mykey] <= 100} + assert {[r get mykey] eq "foo"} + r select 9 + } + + test {MOVE does not create an expire if it does not exist} { + r select 10 + r flushdb + r select 9 + r set mykey foo + r move mykey 10 + assert {[r ttl mykey] == -2} + r select 10 + assert {[r ttl mykey] == -1} + assert {[r get mykey] eq "foo"} + r select 9 + } + test {SET/GET keys in different DBs} { r set a hello r set b world diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-3.0.4/tests/unit/type/hash.tcl new/redis-3.0.5/tests/unit/type/hash.tcl --- old/redis-3.0.4/tests/unit/type/hash.tcl 2015-09-08 10:02:10.000000000 +0200 +++ new/redis-3.0.5/tests/unit/type/hash.tcl 2015-10-15 15:44:54.000000000 +0200 @@ -2,8 +2,8 @@ test {HSET/HLEN - Small hash creation} { array set smallhash {} for {set i 0} {$i < 8} {incr i} { - set key [randstring 0 8 alpha] - set val [randstring 0 8 alpha] + set key __avoid_collisions__[randstring 0 8 alpha] + set val __avoid_collisions__[randstring 0 8 alpha] if {[info exists smallhash($key)]} { incr i -1 continue @@ -21,8 +21,8 @@ test {HSET/HLEN - Big hash creation} { array set bighash {} for {set i 0} {$i < 1024} {incr i} { - set key [randstring 0 8 alpha] - set val [randstring 0 8 alpha] + set key __avoid_collisions__[randstring 0 8 alpha] + set val __avoid_collisions__[randstring 0 8 alpha] if {[info exists bighash($key)]} { incr i -1 continue @@ -33,7 +33,7 @@ list [r hlen bighash] } {1024} - test {Is the big hash encoded with a ziplist?} { + test {Is the big hash encoded with an hash table?} { assert_encoding hashtable bighash }