summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sentinel.conf6
-rw-r--r--src/cluster.c1
-rw-r--r--src/debug.c4
-rw-r--r--src/redis.c3
-rw-r--r--src/scripting.c18
-rw-r--r--src/sentinel.c4
-rw-r--r--src/t_zset.c2
-rw-r--r--tests/cluster/cluster.tcl22
-rw-r--r--tests/cluster/tests/00-base.tcl10
-rw-r--r--tests/cluster/tests/01-faildet.tcl24
-rw-r--r--tests/cluster/tests/02-failover.tcl35
-rw-r--r--tests/cluster/tests/includes/init-tests.tcl2
-rw-r--r--tests/cluster/tmp/.gitignore2
-rw-r--r--tests/integration/replication-2.tcl60
-rw-r--r--tests/support/server.tcl2
-rw-r--r--tests/unit/scripting.tcl60
-rw-r--r--tests/unit/sort.tcl6
17 files changed, 203 insertions, 58 deletions
diff --git a/sentinel.conf b/sentinel.conf
index 114b8474f..2384e9bc7 100644
--- a/sentinel.conf
+++ b/sentinel.conf
@@ -20,6 +20,12 @@ dir /tmp
# be elected by the majority of the known Sentinels in order to
# start a failover, so no failover can be performed in minority.
#
+# Slaves are auto-discovered, so you don't need to specify slaves in
+# any way. Sentinel itself will rewrite this configuration file adding
+# the slaves using additional configuration options.
+# Also note that the configuration file is rewritten when a
+# slave is promoted to master.
+#
# Note: master name should not include special characters or spaces.
# The valid charset is A-z 0-9 and the three characters ".-_".
sentinel monitor mymaster 127.0.0.1 6379 2
diff --git a/src/cluster.c b/src/cluster.c
index 638189dec..948cc9140 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -3076,6 +3076,7 @@ void clusterUpdateState(void) {
* to don't count the DB loading time. */
if (first_call_time == 0) first_call_time = mstime();
if (nodeIsMaster(myself) &&
+ server.cluster->state == REDIS_CLUSTER_FAIL &&
mstime() - first_call_time < REDIS_CLUSTER_WRITABLE_DELAY) return;
/* Start assuming the state is OK. We'll turn it into FAIL if there
diff --git a/src/debug.c b/src/debug.c
index 3fb491cb7..d4fabb9e4 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -864,7 +864,7 @@ void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
"\n=== REDIS BUG REPORT END. Make sure to include from START to END. ===\n\n"
" Please report the crash opening an issue on github:\n\n"
" http://github.com/antirez/redis/issues\n\n"
-" Suspect RAM error? Use redis-server --test-memory to veryfy it.\n\n"
+" Suspect RAM error? Use redis-server --test-memory to verify it.\n\n"
);
/* free(messages); Don't call free() with possibly corrupted memory. */
if (server.daemonize) unlink(server.pidfile);
@@ -947,7 +947,7 @@ void enableWatchdog(int period) {
/* Watchdog was actually disabled, so we have to setup the signal
* handler. */
sigemptyset(&act.sa_mask);
- act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_SIGINFO;
+ act.sa_flags = SA_ONSTACK | SA_SIGINFO;
act.sa_sigaction = watchdogSignalHandler;
sigaction(SIGALRM, &act, NULL);
}
diff --git a/src/redis.c b/src/redis.c
index 6a72ca53d..dc9f4c955 100644
--- a/src/redis.c
+++ b/src/redis.c
@@ -2170,7 +2170,8 @@ int processCommand(redisClient *c) {
/* Don't accept write commands if there are not enough good slaves and
* user configured the min-slaves-to-write option. */
- if (server.repl_min_slaves_to_write &&
+ if (server.masterhost == NULL &&
+ server.repl_min_slaves_to_write &&
server.repl_min_slaves_max_lag &&
c->cmd->flags & REDIS_CMD_WRITE &&
server.repl_good_slaves_count < server.repl_min_slaves_to_write)
diff --git a/src/scripting.c b/src/scripting.c
index 4b485d9b4..27964771f 100644
--- a/src/scripting.c
+++ b/src/scripting.c
@@ -232,13 +232,23 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
for (j = 0; j < argc; j++) {
char *obj_s;
size_t obj_len;
+ char dbuf[64];
- obj_s = (char*)lua_tolstring(lua,j+1,&obj_len);
- if (obj_s == NULL) break; /* Not a string. */
+ if (lua_isnumber(lua,j+1)) {
+ /* We can't use lua_tolstring() for number -> string conversion
+ * since Lua uses a format specifier that loses precision. */
+ lua_Number num = lua_tonumber(lua,j+1);
+
+ obj_len = snprintf(dbuf,sizeof(dbuf),"%.17g",(double)num);
+ obj_s = dbuf;
+ } else {
+ obj_s = (char*)lua_tolstring(lua,j+1,&obj_len);
+ if (obj_s == NULL) break; /* Not a string. */
+ }
/* Try to use a cached object. */
- if (j < LUA_CMD_OBJCACHE_SIZE && cached_objects[j] &&
- cached_objects_len[j] >= obj_len)
+ if (j < LUA_CMD_OBJCACHE_SIZE && cached_objects[j] &&
+ cached_objects_len[j] >= obj_len)
{
char *s = cached_objects[j]->ptr;
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
diff --git a/src/sentinel.c b/src/sentinel.c
index 1a5d78f1c..b1d843667 100644
--- a/src/sentinel.c
+++ b/src/sentinel.c
@@ -88,7 +88,7 @@ typedef struct sentinelAddr {
/* Failover machine different states. */
#define SENTINEL_FAILOVER_STATE_NONE 0 /* No failover in progress. */
-#define SENTINEL_FAILOVER_STATE_WAIT_START 1 /* Wait for failover_start_time*/
+#define SENTINEL_FAILOVER_STATE_WAIT_START 1 /* Wait for failover_start_time*/
#define SENTINEL_FAILOVER_STATE_SELECT_SLAVE 2 /* Select slave to promote */
#define SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE 3 /* Slave -> Master */
#define SENTINEL_FAILOVER_STATE_WAIT_PROMOTION 4 /* Wait slave to change role */
@@ -2875,7 +2875,7 @@ void sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) {
if (ri->last_ping_time)
elapsed = mstime() - ri->last_ping_time;
- /* Check if we are in need for a reconnection of one of the
+ /* Check if we are in need for a reconnection of one of the
* links, because we are detecting low activity.
*
* 1) Check if the command link seems connected, was connected not less
diff --git a/src/t_zset.c b/src/t_zset.c
index 4e946b4f5..8bb5bde6d 100644
--- a/src/t_zset.c
+++ b/src/t_zset.c
@@ -1333,6 +1333,7 @@ void zremCommand(redisClient *c) {
zobj->ptr = zzlDelete(zobj->ptr,eptr);
if (zzlLength(zobj->ptr) == 0) {
dbDelete(c->db,key);
+ keyremoved = 1;
break;
}
}
@@ -1356,6 +1357,7 @@ void zremCommand(redisClient *c) {
if (htNeedsResize(zs->dict)) dictResize(zs->dict);
if (dictSize(zs->dict) == 0) {
dbDelete(c->db,key);
+ keyremoved = 1;
break;
}
}
diff --git a/tests/cluster/cluster.tcl b/tests/cluster/cluster.tcl
index 46b4a6303..55f979f2f 100644
--- a/tests/cluster/cluster.tcl
+++ b/tests/cluster/cluster.tcl
@@ -106,3 +106,25 @@ proc create_cluster {masters slaves} {
}
assert_cluster_state ok
}
+
+# Set the cluster node-timeout to all the reachalbe nodes.
+proc set_cluster_node_timeout {to} {
+ foreach_redis_id id {
+ catch {R $id CONFIG SET cluster-node-timeout $to}
+ }
+}
+
+# Check if the cluster is writable and readable. Use node "id"
+# as a starting point to talk with the cluster.
+proc cluster_write_test {id} {
+ set prefix [randstring 20 20 alpha]
+ set port [get_instance_attrib redis $id port]
+ set cluster [redis_cluster 127.0.0.1:$port]
+ for {set j 0} {$j < 100} {incr j} {
+ $cluster set key.$j $prefix.$j
+ }
+ for {set j 0} {$j < 100} {incr j} {
+ assert {[$cluster get key.$j] eq "$prefix.$j"}
+ }
+ $cluster close
+}
diff --git a/tests/cluster/tests/00-base.tcl b/tests/cluster/tests/00-base.tcl
index 2c6585970..280befb9d 100644
--- a/tests/cluster/tests/00-base.tcl
+++ b/tests/cluster/tests/00-base.tcl
@@ -55,13 +55,5 @@ test "Nodes should report cluster_state is ok now" {
}
test "It is possible to write and read from the cluster" {
- set port [get_instance_attrib redis 0 port]
- set cluster [redis_cluster 127.0.0.1:$port]
- for {set j 0} {$j < 100} {incr j} {
- $cluster set key.$j $j
- }
- for {set j 0} {$j < 100} {incr j} {
- assert {[$cluster get key.$j] eq $j}
- }
- $cluster close
+ cluster_write_test 0
}
diff --git a/tests/cluster/tests/01-faildet.tcl b/tests/cluster/tests/01-faildet.tcl
index a31f7eb67..8fe87c91f 100644
--- a/tests/cluster/tests/01-faildet.tcl
+++ b/tests/cluster/tests/01-faildet.tcl
@@ -6,10 +6,25 @@ test "Create a 5 nodes cluster" {
create_cluster 5 5
}
+test "Cluster should start ok" {
+ assert_cluster_state ok
+}
+
+test "Killing two slave nodes" {
+ kill_instance redis 5
+ kill_instance redis 6
+}
+
+test "Cluster should be still up" {
+ assert_cluster_state ok
+}
+
test "Killing one master node" {
kill_instance redis 0
}
+# Note: the only slave of instance 0 is already down so no
+# failover is possible, that would change the state back to ok.
test "Cluster should be down now" {
assert_cluster_state fail
}
@@ -21,12 +36,3 @@ test "Restarting master node" {
test "Cluster should be up again" {
assert_cluster_state ok
}
-
-test "Killing two slave nodes" {
- kill_instance redis 5
- kill_instance redis 6
-}
-
-test "Cluster should be still up" {
- assert_cluster_state ok
-}
diff --git a/tests/cluster/tests/02-failover.tcl b/tests/cluster/tests/02-failover.tcl
new file mode 100644
index 000000000..2dff279a5
--- /dev/null
+++ b/tests/cluster/tests/02-failover.tcl
@@ -0,0 +1,35 @@
+# Check the basic monitoring and failover capabilities.
+
+source "../tests/includes/init-tests.tcl"
+
+test "Create a 5 nodes cluster" {
+ create_cluster 5 5
+}
+
+test "Cluster is up" {
+ assert_cluster_state ok
+}
+
+test "Cluster is writable" {
+ cluster_write_test 0
+}
+
+test "Instance #5 is a slave" {
+ assert {[RI 5 role] eq {slave}}
+}
+
+test "Killing one master node" {
+ kill_instance redis 0
+}
+
+test "Cluster should eventually be up again" {
+ assert_cluster_state ok
+}
+
+test "Cluster is writable" {
+ cluster_write_test 1
+}
+
+test "Instance #5 is now a master" {
+ assert {[RI 5 role] eq {master}}
+}
diff --git a/tests/cluster/tests/includes/init-tests.tcl b/tests/cluster/tests/includes/init-tests.tcl
index 9f34be181..75417f20c 100644
--- a/tests/cluster/tests/includes/init-tests.tcl
+++ b/tests/cluster/tests/includes/init-tests.tcl
@@ -21,7 +21,7 @@ test "Cluster nodes are reachable" {
test "Cluster nodes hard reset" {
foreach_redis_id id {
- R $id flushall
+ catch {R $id flushall} ; # May fail for readonly slaves.
R $id cluster reset hard
R $id config set cluster-node-timeout 3000
}
diff --git a/tests/cluster/tmp/.gitignore b/tests/cluster/tmp/.gitignore
new file mode 100644
index 000000000..f581f73e2
--- /dev/null
+++ b/tests/cluster/tmp/.gitignore
@@ -0,0 +1,2 @@
+redis_*
+sentinel_*
diff --git a/tests/integration/replication-2.tcl b/tests/integration/replication-2.tcl
index 5450bdd85..9446e5cd9 100644
--- a/tests/integration/replication-2.tcl
+++ b/tests/integration/replication-2.tcl
@@ -6,6 +6,66 @@ start_server {tags {"repl"}} {
s -1 role
} {slave}
+ test {If min-slaves-to-write is honored, write is accepted} {
+ r config set min-slaves-to-write 1
+ r config set min-slaves-max-lag 10
+ r set foo 12345
+ wait_for_condition 50 100 {
+ [r -1 get foo] eq {12345}
+ } else {
+ fail "Write did not reached slave"
+ }
+ }
+
+ test {No write if min-slaves-to-write is < attached slaves} {
+ r config set min-slaves-to-write 2
+ r config set min-slaves-max-lag 10
+ catch {r set foo 12345} err
+ set err
+ } {NOREPLICAS*}
+
+ test {If min-slaves-to-write is honored, write is accepted (again)} {
+ r config set min-slaves-to-write 1
+ r config set min-slaves-max-lag 10
+ r set foo 12345
+ wait_for_condition 50 100 {
+ [r -1 get foo] eq {12345}
+ } else {
+ fail "Write did not reached slave"
+ }
+ }
+
+ test {No write if min-slaves-max-lag is > of the slave lag} {
+ r -1 deferred 1
+ r config set min-slaves-to-write 1
+ r config set min-slaves-max-lag 2
+ r -1 debug sleep 6
+ assert {[r set foo 12345] eq {OK}}
+ after 4000
+ catch {r set foo 12345} err
+ assert {[r -1 read] eq {OK}}
+ r -1 deferred 0
+ set err
+ } {NOREPLICAS*}
+
+ test {min-slaves-to-write is ignored by slaves} {
+ r config set min-slaves-to-write 1
+ r config set min-slaves-max-lag 10
+ r -1 config set min-slaves-to-write 1
+ r -1 config set min-slaves-max-lag 10
+ r set foo aaabbb
+ wait_for_condition 50 100 {
+ [r -1 get foo] eq {aaabbb}
+ } else {
+ fail "Write did not reached slave"
+ }
+ }
+
+ # Fix parameters for the next test to work
+ r config set min-slaves-to-write 0
+ r -1 config set min-slaves-to-write 0
+ r flushall
+
test {MASTER and SLAVE dataset should be identical after complex ops} {
createComplexDataset r 10000
after 500
diff --git a/tests/support/server.tcl b/tests/support/server.tcl
index edcbbcc5d..9f92ce31e 100644
--- a/tests/support/server.tcl
+++ b/tests/support/server.tcl
@@ -236,7 +236,7 @@ proc start_server {options {code undefined}} {
# find out the pid
while {![info exists pid]} {
- regexp {\[(\d+)\]} [exec cat $stdout] _ pid
+ regexp {PID:\s(\d+)} [exec cat $stdout] _ pid
after 100
}
diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl
index 922177eff..d7b6d2ca0 100644
--- a/tests/unit/scripting.tcl
+++ b/tests/unit/scripting.tcl
@@ -40,15 +40,15 @@ start_server {tags {"scripting"}} {
test {EVAL - is Lua able to call Redis API?} {
r set mykey myval
- r eval {return redis.call('get','mykey')} 0
+ r eval {return redis.call('get',KEYS[1])} 1 mykey
} {myval}
test {EVALSHA - Can we call a SHA1 if already defined?} {
- r evalsha 9bd632c7d33e571e9f24556ebed26c3479a87129 0
+ r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey
} {myval}
test {EVALSHA - Can we call a SHA1 in uppercase?} {
- r evalsha 9BD632C7D33E571E9F24556EBED26C3479A87129 0
+ r evalsha FD758D1589D044DD850A6F05D52F2EEFD27F033F 1 mykey
} {myval}
test {EVALSHA - Do we get an error on invalid SHA1?} {
@@ -175,18 +175,18 @@ start_server {tags {"scripting"}} {
set e {}
r set foo bar
catch {
- r eval "redis.call('lpush','foo','val')" 0
+ r eval {redis.call('lpush',KEYS[1],'val')} 1 foo
} e
set e
} {*against a key*}
test {SCRIPTING FLUSH - is able to clear the scripts cache?} {
r set mykey myval
- set v [r evalsha 9bd632c7d33e571e9f24556ebed26c3479a87129 0]
+ set v [r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey]
assert_equal $v myval
set e ""
r script flush
- catch {r evalsha 9bd632c7d33e571e9f24556ebed26c3479a87129 0} e
+ catch {r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey} e
set e
} {NOSCRIPT*}
@@ -204,25 +204,25 @@ start_server {tags {"scripting"}} {
test "In the context of Lua the output of random commands gets ordered" {
r del myset
r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz
- r eval {return redis.call('smembers','myset')} 0
+ r eval {return redis.call('smembers',KEYS[1])} 1 myset
} {a aa aaa azz b c d e f g h i l m n o p q r s t u v z}
test "SORT is normally not alpha re-ordered for the scripting engine" {
r del myset
r sadd myset 1 2 3 4 10
- r eval {return redis.call('sort','myset','desc')} 0
+ r eval {return redis.call('sort',KEYS[1],'desc')} 1 myset
} {10 4 3 2 1}
test "SORT BY <constant> output gets ordered for scripting" {
r del myset
r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz
- r eval {return redis.call('sort','myset','by','_')} 0
+ r eval {return redis.call('sort',KEYS[1],'by','_')} 1 myset
} {a aa aaa azz b c d e f g h i l m n o p q r s t u v z}
test "SORT BY <constant> with GET gets ordered for scripting" {
r del myset
r sadd myset a b c
- r eval {return redis.call('sort','myset','by','_','get','#','get','_:*')} 0
+ r eval {return redis.call('sort',KEYS[1],'by','_','get','#','get','_:*')} 1 myset
} {a {} b {} c {}}
test "redis.sha1hex() implementation" {
@@ -300,9 +300,9 @@ start_server {tags {"scripting"}} {
test {EVAL processes writes from AOF in read-only slaves} {
r flushall
r config set appendonly yes
- r eval {redis.call("set","foo","100")} 0
- r eval {redis.call("incr","foo")} 0
- r eval {redis.call("incr","foo")} 0
+ r eval {redis.call("set",KEYS[1],"100")} 1 foo
+ r eval {redis.call("incr",KEYS[1])} 1 foo
+ r eval {redis.call("incr",KEYS[1])} 1 foo
wait_for_condition 50 100 {
[s aof_rewrite_in_progress] == 0
} else {
@@ -339,6 +339,14 @@ start_server {tags {"scripting"}} {
return redis.call('lrange','mylist',0,-1)
} 0
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100}
+
+ test {Number conversion precision test (issue #1118)} {
+ r eval {
+ local value = 9007199254740991
+ redis.call("set","foo",value)
+ return redis.call("get","foo")
+ } 0
+ } {9007199254740991}
}
# Start a new server since the last test in this stanza will kill the
@@ -365,7 +373,7 @@ start_server {tags {"scripting"}} {
test {Timedout scripts that modified data can't be killed by SCRIPT KILL} {
set rd [redis_deferring_client]
r config set lua-time-limit 10
- $rd eval {redis.call('set','x','y'); while true do end} 0
+ $rd eval {redis.call('set',KEYS[1],'y'); while true do end} 1 x
after 200
catch {r ping} e
assert_match {BUSY*} $e
@@ -392,13 +400,13 @@ start_server {tags {"scripting repl"}} {
start_server {} {
test {Before the slave connects we issue two EVAL commands} {
# One with an error, but still executing a command.
- # SHA is: 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56
+ # SHA is: 67164fc43fa971f76fd1aaeeaf60c1c178d25876
catch {
- r eval {redis.call('incr','x'); redis.call('nonexisting')} 0
+ r eval {redis.call('incr',KEYS[1]); redis.call('nonexisting')} 1 x
}
# One command is correct:
- # SHA is: ae3477e27be955de7e1bc9adfdca626b478d3cb2
- r eval {return redis.call('incr','x')} 0
+ # SHA is: 6f5ade10a69975e903c6d07b10ea44c6382381a5
+ r eval {return redis.call('incr',KEYS[1])} 1 x
} {2}
test {Connect a slave to the main instance} {
@@ -415,9 +423,9 @@ start_server {tags {"scripting repl"}} {
# The server should replicate successful and unsuccessful
# commands as EVAL instead of EVALSHA.
catch {
- r evalsha 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56 0
+ r evalsha 67164fc43fa971f76fd1aaeeaf60c1c178d25876 1 x
}
- r evalsha ae3477e27be955de7e1bc9adfdca626b478d3cb2 0
+ r evalsha 6f5ade10a69975e903c6d07b10ea44c6382381a5 1 x
} {4}
test {If EVALSHA was replicated as EVAL, 'x' should be '4'} {
@@ -432,9 +440,9 @@ start_server {tags {"scripting repl"}} {
set rd [redis_deferring_client]
$rd brpop a 0
r eval {
- redis.call("lpush","a","1");
- redis.call("lpush","a","2");
- } 0
+ redis.call("lpush",KEYS[1],"1");
+ redis.call("lpush",KEYS[1],"2");
+ } 1 a
set res [$rd read]
$rd close
wait_for_condition 50 100 {
@@ -447,9 +455,9 @@ start_server {tags {"scripting repl"}} {
test {EVALSHA replication when first call is readonly} {
r del x
- r eval {if tonumber(KEYS[1]) > 0 then redis.call('incr', 'x') end} 1 0
- r evalsha 38fe3ddf5284a1d48f37f824b4c4e826879f3cb9 1 0
- r evalsha 38fe3ddf5284a1d48f37f824b4c4e826879f3cb9 1 1
+ r eval {if tonumber(ARGV[1]) > 0 then redis.call('incr', KEYS[1]) end} 1 x 0
+ r evalsha 6e0e2745aa546d0b50b801a20983b70710aef3ce 1 x 0
+ r evalsha 6e0e2745aa546d0b50b801a20983b70710aef3ce 1 x 1
wait_for_condition 50 100 {
[r -1 get x] eq {1}
} else {
diff --git a/tests/unit/sort.tcl b/tests/unit/sort.tcl
index 6c5644a79..f48f88b5d 100644
--- a/tests/unit/sort.tcl
+++ b/tests/unit/sort.tcl
@@ -154,9 +154,9 @@ start_server {
r zadd zset 10 d
r zadd zset 3 e
r eval {
- return {redis.call('sort','zset','by','nosort','asc'),
- redis.call('sort','zset','by','nosort','desc')}
- } 0
+ return {redis.call('sort',KEYS[1],'by','nosort','asc'),
+ redis.call('sort',KEYS[1],'by','nosort','desc')}
+ } 1 zset
} {{a c e b d} {d b e c a}}
test "SORT sorted set: +inf and -inf handling" {