diff options
-rw-r--r-- | sentinel.conf | 6 | ||||
-rw-r--r-- | src/cluster.c | 1 | ||||
-rw-r--r-- | src/debug.c | 4 | ||||
-rw-r--r-- | src/redis.c | 3 | ||||
-rw-r--r-- | src/scripting.c | 18 | ||||
-rw-r--r-- | src/sentinel.c | 4 | ||||
-rw-r--r-- | src/t_zset.c | 2 | ||||
-rw-r--r-- | tests/cluster/cluster.tcl | 22 | ||||
-rw-r--r-- | tests/cluster/tests/00-base.tcl | 10 | ||||
-rw-r--r-- | tests/cluster/tests/01-faildet.tcl | 24 | ||||
-rw-r--r-- | tests/cluster/tests/02-failover.tcl | 35 | ||||
-rw-r--r-- | tests/cluster/tests/includes/init-tests.tcl | 2 | ||||
-rw-r--r-- | tests/cluster/tmp/.gitignore | 2 | ||||
-rw-r--r-- | tests/integration/replication-2.tcl | 60 | ||||
-rw-r--r-- | tests/support/server.tcl | 2 | ||||
-rw-r--r-- | tests/unit/scripting.tcl | 60 | ||||
-rw-r--r-- | tests/unit/sort.tcl | 6 |
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" { |