networking.c/writeToClient: handle WSAEWOULDBLOCK

- fixed writeToClient() after failed tests
- adjusted tests to match antirez/redis@4.0.2
This commit is contained in:
Tomasz Poradowski
2018-08-02 00:31:59 +02:00
parent 155111f7e4
commit bdcf80ea4e
30 changed files with 770 additions and 131 deletions
+11 -3
View File
@@ -939,6 +939,14 @@ int writeToClient(int fd, client *c, int handler_installed) {
server.el, c, c->buf, NULL);
if (result == SOCKET_ERROR && errno != WSA_IO_PENDING) {
nwritten = -1;
//[tporadowski/#11] we might be bursting data too fast, so turn it into another try that will put back the client
// in the sending queue
if (errno == WSAEWOULDBLOCK) {
serverLog(LL_DEBUG, "writeToClient: will try again (EAGAIN) due to WSAEWOULDBLOCK");
errno = EAGAIN;
}
break;
}
#else
@@ -1002,7 +1010,7 @@ int writeToClient(int fd, client *c, int handler_installed) {
if (listLength(c->reply) == 0)
serverAssert(c->reply_bytes == 0);
}
}
}
/* Note that we avoid to send more than NET_MAX_WRITES_PER_EVENT
* bytes, in a single threaded server it's a good idea to serve
* other clients as well, even if a very large request comes from
@@ -1014,7 +1022,7 @@ int writeToClient(int fd, client *c, int handler_installed) {
if (totwritten > NET_MAX_WRITES_PER_EVENT &&
(server.maxmemory == 0 ||
zmalloc_used_memory() < server.maxmemory)) break;
}
}
server.stat_net_output_bytes += totwritten;
if (nwritten == -1) {
if (errno == EAGAIN) {
@@ -1070,7 +1078,7 @@ int writeToClient(int fd, client *c, int handler_installed) {
}
#endif
return C_OK;
}
}
/* Write event handler. Just send data to the client. */
void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask) {
+1
View File
@@ -1,5 +1,6 @@
# Redis configuration for testing.
#always-show-logo yes
notify-keyspace-events KEA
daemonize no
pidfile /var/run/redis.pid
+8 -3
View File
@@ -59,6 +59,7 @@ array set content {}
set tribpid {}
test "Cluster consistency during live resharding" {
set ele 0
for {set j 0} {$j < $numops} {incr j} {
# Trigger the resharding once we execute half the ops.
if {$tribpid ne {} &&
@@ -87,7 +88,7 @@ test "Cluster consistency during live resharding" {
# Write random data to random list.
set listid [randomInt $numkeys]
set key "key:$listid"
set ele [randomValue]
incr ele
# We write both with Lua scripts and with plain commands.
# This way we are able to stress Lua -> Redis command invocation
# as well, that has tests to prevent Lua to write into wrong
@@ -116,7 +117,9 @@ test "Cluster consistency during live resharding" {
test "Verify $numkeys keys for consistency with logical content" {
# Check that the Redis Cluster content matches our logical content.
foreach {key value} [array get content] {
assert {[$cluster lrange $key 0 -1] eq $value}
if {[$cluster lrange $key 0 -1] ne $value} {
fail "Key $key expected to hold '$value' but actual content is [$cluster lrange $key 0 -1]"
}
}
}
@@ -134,7 +137,9 @@ test "Cluster should eventually be up again" {
test "Verify $numkeys keys after the crash & restart" {
# Check that the Redis Cluster content matches our logical content.
foreach {key value} [array get content] {
assert {[$cluster lrange $key 0 -1] eq $value}
if {[$cluster lrange $key 0 -1] ne $value} {
fail "Key $key expected to hold '$value' but actual content is [$cluster lrange $key 0 -1]"
}
}
}
+2 -3
View File
@@ -69,10 +69,9 @@ tags {"aof"} {
assert_equal 1 [is_alive $srv]
}
test "Truncated AOF loaded: we expect foo to be equal to 6 now" {
assert_equal 1 [is_alive $srv]
set client [redis [dict get $srv host] [dict get $srv port]]
set client [redis [dict get $srv host] [dict get $srv port]]
test "Truncated AOF loaded: we expect foo to be equal to 6 now" {
assert {[$client get foo] eq "6"}
}
}
+78
View File
@@ -0,0 +1,78 @@
# Issue 3899 regression test.
# We create a chain of three instances: master -> slave -> slave2
# and continuously break the link while traffic is generated by
# redis-benchmark. At the end we check that the data is the same
# everywhere.
start_server {tags {"psync2"}} {
start_server {} {
start_server {} {
# Config
set debug_msg 0 ; # Enable additional debug messages
set no_exit 0 ; # Do not exit at end of the test
set duration 20 ; # Total test seconds
for {set j 0} {$j < 3} {incr j} {
set R($j) [srv [expr 0-$j] client]
set R_host($j) [srv [expr 0-$j] host]
set R_port($j) [srv [expr 0-$j] port]
if {$debug_msg} {puts "Log file: [srv [expr 0-$j] stdout]"}
}
# Setup the replication and backlog parameters
test "PSYNC2 #3899 regression: setup" {
$R(1) slaveof $R_host(0) $R_port(0)
$R(2) slaveof $R_host(0) $R_port(0)
$R(0) set foo bar
wait_for_condition 50 1000 {
[$R(1) dbsize] == 1 && [$R(2) dbsize] == 1
} else {
fail "Slaves not replicating from master"
}
$R(0) config set repl-backlog-size 10mb
$R(1) config set repl-backlog-size 10mb
}
set cycle_start_time [clock milliseconds]
set bench_pid [exec src/redis-benchmark -p $R_port(0) -n 10000000 -r 1000 incr __rand_int__ > /dev/null &]
while 1 {
set elapsed [expr {[clock milliseconds]-$cycle_start_time}]
if {$elapsed > $duration*1000} break
if {rand() < .05} {
test "PSYNC2 #3899 regression: kill first slave" {
$R(1) client kill type master
}
}
if {rand() < .05} {
test "PSYNC2 #3899 regression: kill chained slave" {
$R(2) client kill type master
}
}
after 100
}
exec kill -9 $bench_pid
if {$debug_msg} {
for {set j 0} {$j < 100} {incr j} {
if {
[$R(0) debug digest] == [$R(1) debug digest] &&
[$R(1) debug digest] == [$R(2) debug digest]
} break
puts [$R(0) debug digest]
puts [$R(1) debug digest]
puts [$R(2) debug digest]
after 1000
}
}
test "PSYNC2 #3899 regression: verify consistency" {
wait_for_condition 50 1000 {
([$R(0) debug digest] eq [$R(1) debug digest]) &&
([$R(1) debug digest] eq [$R(2) debug digest])
} else {
fail "The three instances have different data sets"
}
}
}}}
+182
View File
@@ -0,0 +1,182 @@
start_server {tags {"psync2"}} {
start_server {} {
start_server {} {
start_server {} {
start_server {} {
set master_id 0 ; # Current master
set start_time [clock seconds] ; # Test start time
set counter_value 0 ; # Current value of the Redis counter "x"
# Config
set debug_msg 0 ; # Enable additional debug messages
set no_exit 0; ; # Do not exit at end of the test
set duration 20 ; # Total test seconds
set genload 1 ; # Load master with writes at every cycle
set genload_time 5000 ; # Writes duration time in ms
set disconnect 1 ; # Break replication link between random
# master and slave instances while the
# master is loaded with writes.
set disconnect_period 1000 ; # Disconnect repl link every N ms.
for {set j 0} {$j < 5} {incr j} {
set R($j) [srv [expr 0-$j] client]
set R_host($j) [srv [expr 0-$j] host]
set R_port($j) [srv [expr 0-$j] port]
if {$debug_msg} {puts "Log file: [srv [expr 0-$j] stdout]"}
}
set cycle 1
while {([clock seconds]-$start_time) < $duration} {
test "PSYNC2: --- CYCLE $cycle ---" {
incr cycle
}
# Create a random replication layout.
# Start with switching master (this simulates a failover).
# 1) Select the new master.
set master_id [randomInt 5]
set used [list $master_id]
test "PSYNC2: \[NEW LAYOUT\] Set #$master_id as master" {
$R($master_id) slaveof no one
if {$counter_value == 0} {
$R($master_id) set x $counter_value
}
}
# 2) Attach all the slaves to a random instance
while {[llength $used] != 5} {
while 1 {
set slave_id [randomInt 5]
if {[lsearch -exact $used $slave_id] == -1} break
}
set rand [randomInt [llength $used]]
set mid [lindex $used $rand]
set master_host $R_host($mid)
set master_port $R_port($mid)
test "PSYNC2: Set #$slave_id to replicate from #$mid" {
$R($slave_id) slaveof $master_host $master_port
}
lappend used $slave_id
}
# 3) Increment the counter and wait for all the instances
# to converge.
test "PSYNC2: cluster is consistent after failover" {
$R($master_id) incr x; incr counter_value
for {set j 0} {$j < 5} {incr j} {
wait_for_condition 50 1000 {
[$R($j) get x] == $counter_value
} else {
fail "Instance #$j x variable is inconsistent"
}
}
}
# 4) Generate load while breaking the connection of random
# slave-master pairs.
test "PSYNC2: generate load while killing replication links" {
set t [clock milliseconds]
set next_break [expr {$t+$disconnect_period}]
while {[clock milliseconds]-$t < $genload_time} {
if {$genload} {
$R($master_id) incr x; incr counter_value
}
if {[clock milliseconds] == $next_break} {
set next_break \
[expr {[clock milliseconds]+$disconnect_period}]
set slave_id [randomInt 5]
if {$disconnect} {
$R($slave_id) client kill type master
if {$debug_msg} {
puts "+++ Breaking link for slave #$slave_id"
}
}
}
}
}
# 5) Increment the counter and wait for all the instances
set x [$R($master_id) get x]
test "PSYNC2: cluster is consistent after load (x = $x)" {
for {set j 0} {$j < 5} {incr j} {
wait_for_condition 50 1000 {
[$R($j) get x] == $counter_value
} else {
fail "Instance #$j x variable is inconsistent"
}
}
}
# Put down the old master so that it cannot generate more
# replication stream, this way in the next master switch, the time at
# which we move slaves away is not important, each will have full
# history (otherwise PINGs will make certain slaves have more history),
# and sometimes a full resync will be needed.
$R($master_id) slaveof 127.0.0.1 0 ;# We use port zero to make it fail.
if {$debug_msg} {
for {set j 0} {$j < 5} {incr j} {
puts "$j: sync_full: [status $R($j) sync_full]"
puts "$j: id1 : [status $R($j) master_replid]:[status $R($j) master_repl_offset]"
puts "$j: id2 : [status $R($j) master_replid2]:[status $R($j) second_repl_offset]"
puts "$j: backlog : firstbyte=[status $R($j) repl_backlog_first_byte_offset] len=[status $R($j) repl_backlog_histlen]"
puts "---"
}
}
test "PSYNC2: total sum of full synchronizations is exactly 4" {
set sum 0
for {set j 0} {$j < 5} {incr j} {
incr sum [status $R($j) sync_full]
}
assert {$sum == 4}
}
}
test "PSYNC2: Bring the master back again for next test" {
$R($master_id) slaveof no one
set master_host $R_host($master_id)
set master_port $R_port($master_id)
for {set j 0} {$j < 5} {incr j} {
if {$j == $master_id} continue
$R($j) slaveof $master_host $master_port
}
# Wait for slaves to sync
wait_for_condition 50 1000 {
[status $R($master_id) connected_slaves] == 4
} else {
fail "Slave not reconnecting"
}
}
test "PSYNC2: Partial resync after restart using RDB aux fields" {
# Pick a random slave
set slave_id [expr {($master_id+1)%5}]
set sync_count [status $R($master_id) sync_full]
catch {
$R($slave_id) config rewrite
$R($slave_id) debug restart
}
wait_for_condition 50 1000 {
[status $R($master_id) connected_slaves] == 4
} else {
fail "Slave not reconnecting"
}
set new_sync_count [status $R($master_id) sync_full]
assert {$sync_count == $new_sync_count}
}
if {$no_exit} {
while 1 { puts -nonewline .; flush stdout; after 1000}
}
}}}}}
+3 -5
View File
@@ -50,7 +50,6 @@ proc start_server_and_kill_it {overrides code} {
kill_server $srv
}
if { $::tcl_platform(platform) != "windows" } {
# Make the RDB file unreadable
file attributes [file join $server_path dump.rdb] -permissions 0222
@@ -67,7 +66,7 @@ if {!$isroot} {
test {Server should not start if RDB file can't be open} {
wait_for_condition 50 100 {
[string match {*Fatal error loading*} \
[exec tail -n1 < [dict get $srv stdout]]]
[exec tail -1 < [dict get $srv stdout]]]
} else {
fail "Server started even if RDB was unreadable!"
}
@@ -90,11 +89,10 @@ close $fd
start_server_and_kill_it [list "dir" $server_path] {
test {Server should not start if RDB is corrupted} {
wait_for_condition 50 100 {
[string match {*RDB checksum*} \
[exec tail -n10 < [dict get $srv stdout]]]
[string match {*CRC error*} \
[exec tail -10 < [dict get $srv stdout]]]
} else {
fail "Server started even if RDB was corrupted!"
}
}
}
}
+2 -12
View File
@@ -2,11 +2,7 @@ start_server {tags {"repl"}} {
start_server {} {
test {First server should have role slave after SLAVEOF} {
r -1 slaveof [srv 0 host] [srv 0 port]
if { $::tcl_platform(platform) == "windows" } {
after 2000
} else {
after 1000
}
after 1000
s -1 role
} {slave}
@@ -14,9 +10,7 @@ start_server {tags {"repl"}} {
r config set min-slaves-to-write 1
r config set min-slaves-max-lag 10
r set foo 12345
#WIN_PORT_FIX 'wait_for_condition 50 100' -> 'wait_for_condition 50 250'
wait_for_condition 50 250 {
wait_for_condition 50 100 {
[r -1 get foo] eq {12345}
} else {
fail "Write did not reached slave"
@@ -75,9 +69,6 @@ start_server {tags {"repl"}} {
test {MASTER and SLAVE dataset should be identical after complex ops} {
createComplexDataset r 10000
after 500
wait_for_condition 50 100 {
[r debug digest] eq [r -1 debug digest]
} else {
if {[r debug digest] ne [r -1 debug digest]} {
set csv1 [csvdump r]
set csv2 [csvdump {r -1}]
@@ -90,7 +81,6 @@ start_server {tags {"repl"}} {
puts "Master - Slave inconsistency"
puts "Run diff -u against /tmp/repldump*.txt for more info"
}
}
assert_equal [r debug digest] [r -1 debug digest]
}
}
+12 -4
View File
@@ -16,9 +16,6 @@ start_server {tags {"repl"}} {
after 4000 ;# Make sure everything expired before taking the digest
r keys * ;# Force DEL syntesizing to slave
after 1000 ;# Wait another second. Now everything should be fine.
wait_for_condition 50 100 {
[r debug digest] eq [r -1 debug digest]
} else {
if {[r debug digest] ne [r -1 debug digest]} {
set csv1 [csvdump r]
set csv2 [csvdump {r -1}]
@@ -31,9 +28,20 @@ start_server {tags {"repl"}} {
puts "Master - Slave inconsistency"
puts "Run diff -u against /tmp/repldump*.txt for more info"
}
}
assert_equal [r debug digest] [r -1 debug digest]
}
test {Slave is able to evict keys created in writable slaves} {
r -1 select 5
assert {[r -1 dbsize] == 0}
r -1 config set slave-read-only no
r -1 set key1 1 ex 5
r -1 set key2 2 ex 5
r -1 set key3 3 ex 5
assert {[r -1 dbsize] == 3}
after 6000
r -1 dbsize
} {0}
}
}
+3 -2
View File
@@ -51,7 +51,7 @@ proc test_psync {descr duration backlog_size backlog_ttl delay cond diskless rec
# Check that the background clients are actually writing.
test {Detect write load to master} {
wait_for_condition 50 100 {
wait_for_condition 50 1000 {
[$master dbsize] > 100
} else {
fail "Can't detect write load from background clients."
@@ -90,6 +90,7 @@ proc test_psync {descr duration backlog_size backlog_ttl delay cond diskless rec
incr retry -1
}
assert {[$master dbsize] > 0}
if {[$master debug digest] ne [$slave debug digest]} {
set csv1 [csvdump r]
set csv2 [csvdump {r -1}]
@@ -127,7 +128,7 @@ foreach diskless {no yes} {
test_psync {no reconnection, just sync} 6 1000000 3600 0 {
} $diskless 0
test_psync {ok psync} 6 1000000 3600 0 {
test_psync {ok psync} 6 100000000 3600 0 {
assert {[s -1 sync_partial_ok] > 0}
} $diskless 1
+7 -20
View File
@@ -243,9 +243,7 @@ foreach dl {no yes} {
# Make sure that slaves and master have same
# number of keys
#WIN_PORT_FIX 'wait_for_condition 500 100' -> 'wait_for_condition 200 250'
wait_for_condition 200 250 {
wait_for_condition 500 100 {
[$master dbsize] == [[lindex $slaves 0] dbsize] &&
[$master dbsize] == [[lindex $slaves 1] dbsize] &&
[$master dbsize] == [[lindex $slaves 2] dbsize]
@@ -255,24 +253,13 @@ foreach dl {no yes} {
# Check digests
set digest [$master debug digest]
set digest0 [[lindex $slaves 0] debug digest]
set digest1 [[lindex $slaves 1] debug digest]
set digest2 [[lindex $slaves 2] debug digest]
assert {$digest ne 0000000000000000000000000000000000000000}
if { $::tcl_platform(platform) == "windows" } {
wait_for_condition 200 250 {
$digest eq [[lindex $slaves 0] debug digest] &&
$digest eq [[lindex $slaves 1] debug digest] &&
$digest eq [[lindex $slaves 2] debug digest]
} else {
fail "slave digest not equal to master digest"
}
} else {
set digest0 [[lindex $slaves 0] debug digest]
set digest1 [[lindex $slaves 1] debug digest]
set digest2 [[lindex $slaves 2] debug digest]
assert {$digest eq $digest0}
assert {$digest eq $digest1}
assert {$digest eq $digest2}
}
assert {$digest eq $digest0}
assert {$digest eq $digest1}
assert {$digest eq $digest2}
}
}
}
@@ -0,0 +1,68 @@
# Test conditions where an instance is considered to be down
source "../tests/includes/init-tests.tcl"
proc ensure_master_up {} {
wait_for_condition 1000 50 {
[dict get [S 4 sentinel master mymaster] flags] eq "master"
} else {
fail "Master flags are not just 'master'"
}
}
proc ensure_master_down {} {
wait_for_condition 1000 50 {
[string match *down* \
[dict get [S 4 sentinel master mymaster] flags]]
} else {
fail "Master is not flagged SDOWN"
}
}
test "Crash the majority of Sentinels to prevent failovers for this unit" {
for {set id 0} {$id < $quorum} {incr id} {
kill_instance sentinel $id
}
}
test "SDOWN is triggered by non-responding but not crashed instance" {
lassign [S 4 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] host port
ensure_master_up
exec ../../../src/redis-cli -h $host -p $port debug sleep 10 > /dev/null &
ensure_master_down
ensure_master_up
}
test "SDOWN is triggered by crashed instance" {
lassign [S 4 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] host port
ensure_master_up
kill_instance redis 0
ensure_master_down
restart_instance redis 0
ensure_master_up
}
test "SDOWN is triggered by masters advertising as slaves" {
ensure_master_up
R 0 slaveof 127.0.0.1 34567
ensure_master_down
R 0 slaveof no one
ensure_master_up
}
test "SDOWN is triggered by misconfigured instance repling with errors" {
ensure_master_up
set orig_dir [lindex [R 0 config get dir] 1]
set orig_save [lindex [R 0 config get save] 1]
# Set dir to / and filename to "tmp" to make sure it will fail.
R 0 config set dir /
R 0 config set dbfilename tmp
R 0 config set save "1000000 1000000"
R 0 bgsave
ensure_master_down
R 0 config set save $orig_save
R 0 config set dir $orig_dir
R 0 config set dbfilename dump.rdb
R 0 bgsave
ensure_master_up
}
+4 -2
View File
@@ -58,7 +58,8 @@ proc ::redis_cluster::__method__refresh_nodes_map {id} {
set idx 0; # Index of the node that will respond.
set errmsg {}
foreach start_node $::redis_cluster::startup_nodes($id) {
lassign [split $start_node :] start_host start_port
set ip_port [lindex [split $start_node @] 0]
lassign [split $ip_port :] start_host start_port
if {[catch {
set r {}
set r [redis $start_host $start_port]
@@ -68,7 +69,7 @@ proc ::redis_cluster::__method__refresh_nodes_map {id} {
if {$r ne {}} {catch {$r close}}
incr idx
if {[string length $errmsg] < 200} {
append errmsg " $start_node: $e"
append errmsg " $ip_port: $e"
}
continue ; # Try next.
} else {
@@ -98,6 +99,7 @@ proc ::redis_cluster::__method__refresh_nodes_map {id} {
set args [split $line " "]
lassign $args nodeid addr flags slaveof pingsent pongrecv configepoch linkstate
set slots [lrange $args 8 end]
set addr [lindex [split $addr @] 0]
if {$addr eq {:0}} {
set addr $start_host:$start_port
}
+1 -1
View File
@@ -334,7 +334,7 @@ proc start_server {options {code undefined}} {
while 1 {
# check that the server actually started and is ready for connections
if {[exec grep -i "ready to accept" | wc -l < $stdout] > 0} {
if {[exec grep -i "Ready to accept" | wc -l < $stdout] > 0} {
break
}
after 10
+10
View File
@@ -42,6 +42,8 @@ set ::all_tests {
integration/rdb
integration/convert-zipmap-hash-on-load
integration/logging
integration/psync2
integration/psync2-reg
unit/pubsub
unit/slowlog
unit/scripting
@@ -55,6 +57,8 @@ set ::all_tests {
unit/geo
unit/memefficiency
unit/hyperloglog
unit/lazyfree
unit/wait
}
# Index to the next test to run in the ::all_tests list.
set ::next_test 0
@@ -63,6 +67,7 @@ set ::host 127.0.0.1
set ::port 21111
set ::traceleaks 0
set ::valgrind 0
set ::stack_logging 0
set ::verbose 0
set ::quiet 0
set ::denytags {}
@@ -395,6 +400,7 @@ proc send_data_packet {fd status data} {
proc print_help_screen {} {
puts [join {
"--valgrind Run the test over valgrind."
"--stack-logging Enable OSX leaks/malloc stack logging."
"--accurate Run slow randomized tests for more iterations."
"--quiet Don't show individual tests."
"--single <unit> Just execute the specified unit (see next option)."
@@ -421,6 +427,10 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
incr j
} elseif {$opt eq {--valgrind}} {
set ::valgrind 1
} elseif {$opt eq {--stack-logging}} {
if {[string match {*Darwin*} [exec uname -a]]} {
set ::stack_logging 1
}
} elseif {$opt eq {--quiet}} {
set ::quiet 1
} elseif {$opt eq {--host}} {
+55 -55
View File
@@ -4,62 +4,63 @@ start_server {tags {"aofrw"}} {
r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite.
waitForBgrewriteaof r
test {AOF rewrite during write load} {
# Start a write load for 10 seconds
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
set load_handle0 [start_write_load $master_host $master_port 10]
set load_handle1 [start_write_load $master_host $master_port 10]
set load_handle2 [start_write_load $master_host $master_port 10]
set load_handle3 [start_write_load $master_host $master_port 10]
set load_handle4 [start_write_load $master_host $master_port 10]
foreach rdbpre {yes no} {
r config set aof-use-rdb-preamble $rdbpre
test "AOF rewrite during write load: RDB preamble=$rdbpre" {
# Start a write load for 10 seconds
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
set load_handle0 [start_write_load $master_host $master_port 10]
set load_handle1 [start_write_load $master_host $master_port 10]
set load_handle2 [start_write_load $master_host $master_port 10]
set load_handle3 [start_write_load $master_host $master_port 10]
set load_handle4 [start_write_load $master_host $master_port 10]
# Make sure the instance is really receiving data
wait_for_condition 50 100 {
[r dbsize] > 0
} else {
fail "No write load detected."
# Make sure the instance is really receiving data
wait_for_condition 50 100 {
[r dbsize] > 0
} else {
fail "No write load detected."
}
# After 3 seconds, start a rewrite, while the write load is still
# active.
after 3000
r bgrewriteaof
waitForBgrewriteaof r
# Let it run a bit more so that we'll append some data to the new
# AOF.
after 1000
# Stop the processes generating the load if they are still active
stop_write_load $load_handle0
stop_write_load $load_handle1
stop_write_load $load_handle2
stop_write_load $load_handle3
stop_write_load $load_handle4
# Make sure that we remain the only connected client.
# This step is needed to make sure there are no pending writes
# that will be processed between the two "debug digest" calls.
wait_for_condition 50 100 {
[llength [split [string trim [r client list]] "\n"]] == 1
} else {
puts [r client list]
fail "Clients generating loads are not disconnecting"
}
# Get the data set digest
set d1 [r debug digest]
# Load the AOF
r debug loadaof
set d2 [r debug digest]
# Make sure they are the same
assert {$d1 eq $d2}
}
# After 3 seconds, start a rewrite, while the write load is still
# active.
after 3000
r bgrewriteaof
waitForBgrewriteaof r
# Let it run a bit more so that we'll append some data to the new
# AOF.
after 1000
# Stop the processes generating the load if they are still active
stop_write_load $load_handle0
stop_write_load $load_handle1
stop_write_load $load_handle2
stop_write_load $load_handle3
stop_write_load $load_handle4
# Make sure that we remain the only connected client.
# This step is needed to make sure there are no pending writes
# that will be processed between the two "debug digest" calls.
#WIN_PORT_FIX 'wait_for_condition 50 100' -> 'wait_for_condition 50 250'
wait_for_condition 50 250 {
[llength [split [string trim [r client list]] "\n"]] == 1
} else {
puts [r client list]
fail "Clients generating loads are not disconnecting"
}
# Get the data set digest
set d1 [r debug digest]
# Load the AOF
r debug loadaof
set d2 [r debug digest]
# Make sure they are the same
assert {$d1 eq $d2}
}
}
@@ -71,7 +72,6 @@ start_server {tags {"aofrw"}} {
r bgrewriteaof
r config set appendonly no
r exec
#WIN_PORT_FIX 'tail -n5' -> 'tail -5'
wait_for_condition 50 100 {
[string match {*Killing*AOF*child*} [exec tail -5 < [srv 0 stdout]]]
} else {
+9
View File
@@ -189,4 +189,13 @@ start_server {tags {"bitops"}} {
r set bits 1
r bitfield bits get u1 0
} {0}
test {BITFIELD regression for #3564} {
for {set j 0} {$j < 10} {incr j} {
r del mystring
set res [r BITFIELD mystring SET i8 0 10 SET i8 64 10 INCRBY i8 10 99900]
assert {$res eq {0 0 60}}
}
r del mystring
}
}
+21
View File
@@ -198,4 +198,25 @@ start_server {tags {"expire"}} {
r set foo b
lsort [r keys *]
} {a e foo s t}
test {EXPIRE with empty string as TTL should report an error} {
r set foo bar
catch {r expire foo ""} e
set e
} {*not an integer*}
test {SET - use EX/PX option, TTL should not be reseted after loadaof} {
r config set appendonly yes
r set foo bar EX 100
after 2000
r debug loadaof
set ttl [r ttl foo]
assert {$ttl <= 98 && $ttl > 90}
r set foo bar PX 100000
after 2000
r debug loadaof
set ttl [r ttl foo]
assert {$ttl <= 98 && $ttl > 90}
}
}
+95 -6
View File
@@ -1,4 +1,4 @@
# Helper functins to simulate search-in-radius in the Tcl side in order to
# Helper functions to simulate search-in-radius in the Tcl side in order to
# verify the Redis implementation with a fuzzy test.
proc geo_degrad deg {expr {$deg*atan(1)*8/360}}
@@ -23,6 +23,47 @@ proc geo_random_point {lonvar latvar} {
set lat [expr {-70 + rand()*140}]
}
# Return elements non common to both the lists.
# This code is from http://wiki.tcl.tk/15489
proc compare_lists {List1 List2} {
set DiffList {}
foreach Item $List1 {
if {[lsearch -exact $List2 $Item] == -1} {
lappend DiffList $Item
}
}
foreach Item $List2 {
if {[lsearch -exact $List1 $Item] == -1} {
if {[lsearch -exact $DiffList $Item] == -1} {
lappend DiffList $Item
}
}
}
return $DiffList
}
# The following list represents sets of random seed, search position
# and radius that caused bugs in the past. It is used by the randomized
# test later as a starting point. When the regression vectors are scanned
# the code reverts to using random data.
#
# The format is: seed km lon lat
set regression_vectors {
{1482225976969 7083 81.634948934258375 30.561509253718668}
{1482340074151 5416 -70.863281847379767 -46.347003465679947}
{1499014685896 6064 -89.818768962202014 -40.463868561416803}
{1412 156 149.29737817929004 15.95807862745508}
{441574 143 59.235461856813856 66.269555127373678}
{160645 187 -101.88575239939883 49.061997951502917}
{750269 154 -90.187939661642517 66.615930412251487}
{342880 145 163.03472387745728 64.012747720821181}
{729955 143 137.86663517256579 63.986745399416776}
{939895 151 59.149620271823181 65.204186651485145}
{1412 156 149.29737817929004 15.95807862745508}
{564862 149 84.062063109158544 -65.685403922426232}
}
set rv_idx 0
start_server {tags {"geo"}} {
test {GEOADD create} {
r geoadd nyc -73.9454966 40.747533 "lic market"
@@ -183,35 +224,83 @@ start_server {tags {"geo"}} {
}
test {GEOADD + GEORANGE randomized test} {
set attempt 10
set attempt 30
while {[incr attempt -1]} {
set rv [lindex $regression_vectors $rv_idx]
incr rv_idx
unset -nocomplain debuginfo
set srand_seed [randomInt 1000000]
set srand_seed [clock milliseconds]
if {$rv ne {}} {set srand_seed [lindex $rv 0]}
lappend debuginfo "srand_seed is $srand_seed"
expr {srand($srand_seed)} ; # If you need a reproducible run
r del mypoints
set radius_km [expr {[randomInt 200]+10}]
if {[randomInt 10] == 0} {
# From time to time use very big radiuses
set radius_km [expr {[randomInt 50000]+10}]
} else {
# Normally use a few - ~200km radiuses to stress
# test the code the most in edge cases.
set radius_km [expr {[randomInt 200]+10}]
}
if {$rv ne {}} {set radius_km [lindex $rv 1]}
set radius_m [expr {$radius_km*1000}]
geo_random_point search_lon search_lat
if {$rv ne {}} {
set search_lon [lindex $rv 2]
set search_lat [lindex $rv 3]
}
lappend debuginfo "Search area: $search_lon,$search_lat $radius_km km"
set tcl_result {}
set argv {}
for {set j 0} {$j < 20000} {incr j} {
geo_random_point lon lat
lappend argv $lon $lat "place:$j"
if {[geo_distance $lon $lat $search_lon $search_lat] < $radius_m} {
set distance [geo_distance $lon $lat $search_lon $search_lat]
if {$distance < $radius_m} {
lappend tcl_result "place:$j"
lappend debuginfo "place:$j $lon $lat [expr {[geo_distance $lon $lat $search_lon $search_lat]/1000}] km"
}
lappend debuginfo "place:$j $lon $lat [expr {$distance/1000}] km"
}
r geoadd mypoints {*}$argv
set res [lsort [r georadius mypoints $search_lon $search_lat $radius_km km]]
set res2 [lsort $tcl_result]
set test_result OK
if {$res != $res2} {
set rounding_errors 0
set diff [compare_lists $res $res2]
foreach place $diff {
set mydist [geo_distance $lon $lat $search_lon $search_lat]
set mydist [expr $mydist/1000]
if {($mydist / $radius_km) > 0.999} {incr rounding_errors}
}
# Make sure this is a real error and not a rounidng issue.
if {[llength $diff] == $rounding_errors} {
set res $res2; # Error silenced
}
}
if {$res != $res2} {
set diff [compare_lists $res $res2]
puts "*** Possible problem in GEO radius query ***"
puts "Redis: $res"
puts "Tcl : $res2"
puts "Diff : $diff"
puts [join $debuginfo "\n"]
foreach place $diff {
if {[lsearch -exact $res2 $place] != -1} {
set where "(only in Tcl)"
} else {
set where "(only in Redis)"
}
lassign [lindex [r geopos mypoints $place] 0] lon lat
set mydist [geo_distance $lon $lat $search_lon $search_lat]
set mydist [expr $mydist/1000]
puts "$place -> [r geopos mypoints $place] $mydist $where"
if {($mydist / $radius_km) > 0.999} {incr rounding_errors}
}
set test_result FAIL
}
unset -nocomplain debuginfo
+4 -3
View File
@@ -6,16 +6,17 @@ start_server {tags {"introspection"}} {
test {MONITOR can log executed commands} {
set rd [redis_deferring_client]
$rd monitor
assert_match {*OK*} [$rd read]
r set foo bar
r get foo
list [$rd read] [$rd read] [$rd read]
} {*OK*"set" "foo"*"get" "foo"*}
list [$rd read] [$rd read]
} {*"set" "foo"*"get" "foo"*}
test {MONITOR can log commands issued by the scripting engine} {
set rd [redis_deferring_client]
$rd monitor
r eval {redis.call('set',KEYS[1],ARGV[1])} 1 foo bar
$rd read ;# Discard the OK
r eval {redis.call('set',KEYS[1],ARGV[1])} 1 foo bar
assert_match {*eval*} [$rd read]
assert_match {*lua*"set"*"foo"*"bar"*} [$rd read]
}
+39
View File
@@ -0,0 +1,39 @@
start_server {tags {"lazyfree"}} {
test "UNLINK can reclaim memory in background" {
set orig_mem [s used_memory]
set args {}
for {set i 0} {$i < 100000} {incr i} {
lappend args $i
}
r sadd myset {*}$args
assert {[r scard myset] == 100000}
set peak_mem [s used_memory]
assert {[r unlink myset] == 1}
assert {$peak_mem > $orig_mem+1000000}
wait_for_condition 50 100 {
[s used_memory] < $peak_mem &&
[s used_memory] < $orig_mem*2
} else {
fail "Memory is not reclaimed by UNLINK"
}
}
test "FLUSHDB ASYNC can reclaim memory in background" {
set orig_mem [s used_memory]
set args {}
for {set i 0} {$i < 100000} {incr i} {
lappend args $i
}
r sadd myset {*}$args
assert {[r scard myset] == 100000}
set peak_mem [s used_memory]
r flushdb async
assert {$peak_mem > $orig_mem+1000000}
wait_for_condition 50 100 {
[s used_memory] < $peak_mem &&
[s used_memory] < $orig_mem*2
} else {
fail "Memory is not reclaimed by FLUSHDB ASYNC"
}
}
}
+2 -2
View File
@@ -24,7 +24,7 @@ start_server {tags {"maxmemory"}} {
}
foreach policy {
allkeys-random allkeys-lru volatile-lru volatile-random volatile-ttl
allkeys-random allkeys-lru allkeys-lfu volatile-lru volatile-lfu volatile-random volatile-ttl
} {
test "maxmemory - is the memory limit honoured? (policy $policy)" {
# make sure to start with a blank instance
@@ -98,7 +98,7 @@ start_server {tags {"maxmemory"}} {
}
foreach policy {
volatile-lru volatile-random volatile-ttl
volatile-lru volatile-lfu volatile-random volatile-ttl
} {
test "maxmemory - policy $policy should only remove volatile keys." {
# make sure to start with a blank instance
+48
View File
@@ -35,3 +35,51 @@ start_server {tags {"memefficiency"}} {
}
}
}
if 0 {
start_server {tags {"defrag"}} {
if {[string match {*jemalloc*} [s mem_allocator]]} {
test "Active defrag" {
r config set activedefrag no
r config set active-defrag-threshold-lower 5
r config set active-defrag-ignore-bytes 2mb
r config set maxmemory 100mb
r config set maxmemory-policy allkeys-lru
r debug populate 700000 asdf 150
r debug populate 170000 asdf 300
set frag [s mem_fragmentation_ratio]
assert {$frag >= 1.7}
r config set activedefrag yes
after 1500 ;# active defrag tests the status once a second.
set hits [s active_defrag_hits]
# wait for the active defrag to stop working
set tries 0
while { True } {
incr tries
after 500
set prev_hits $hits
set hits [s active_defrag_hits]
if {$hits == $prev_hits} {
break
}
assert {$tries < 100}
}
# TODO: we need to expose more accurate fragmentation info
# i.e. the allocator used and active pages
# instead we currently look at RSS so we need to ask for purge
r memory purge
# Test the the fragmentation is lower and that the defragger
# stopped working
set frag [s mem_fragmentation_ratio]
assert {$frag < 1.55}
set misses [s active_defrag_misses]
after 500
set misses2 [s active_defrag_misses]
assert {$misses2 == $misses}
}
}
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ start_server {tags {"other"}} {
test {SELECT an out of range DB} {
catch {r select 1000000} err
set _ $err
} {*invalid*}
} {*index is out of range*}
tags {consistency} {
if {![catch {package require sha1}]} {
+1 -1
View File
@@ -330,7 +330,7 @@ start_server {tags {"scripting"}} {
test {Globals protection reading an undeclared global variable} {
catch {r eval {return a} 0} e
set e
} {*ERR*attempted to access unexisting global*}
} {*ERR*attempted to access * global*}
test {Globals protection setting an undeclared global*} {
catch {r eval {a=10} 0} e
+12 -1
View File
@@ -31,12 +31,14 @@ start_server {tags {"slowlog"} overrides {slowlog-log-slower-than 1000000}} {
} {0}
test {SLOWLOG - logged entry sanity check} {
r client setname foobar
r debug sleep 0.2
set e [lindex [r slowlog get] 0]
assert_equal [llength $e] 4
assert_equal [llength $e] 6
assert_equal [lindex $e 0] 105
assert_equal [expr {[lindex $e 2] > 100000}] 1
assert_equal [lindex $e 3] {debug sleep 0.2}
assert_equal {foobar} [lindex $e 5]
}
test {SLOWLOG - commands with too many arguments are trimmed} {
@@ -67,4 +69,13 @@ start_server {tags {"slowlog"} overrides {slowlog-log-slower-than 1000000}} {
set e [lindex [r slowlog get] 0]
assert_equal [lindex $e 3] {debug sleep 0.2}
}
test {SLOWLOG - can clean older entires} {
r client setname lastentry_client
r config set slowlog-max-len 1
r debug sleep 0.2
assert {[llength [r slowlog get]] == 1}
set e [lindex [r slowlog get] 0]
assert_equal {lastentry_client} [lindex $e 5]
}
}
+44
View File
@@ -13,6 +13,50 @@ start_server {
assert_equal [r lindex l 1] [lindex $mylist 1]
}
test {Regression for quicklist #3343 bug} {
r del mylist
r lpush mylist 401
r lpush mylist 392
r rpush mylist [string repeat x 5105]"799"
r lset mylist -1 [string repeat x 1014]"702"
r lpop mylist
r lset mylist -1 [string repeat x 4149]"852"
r linsert mylist before 401 [string repeat x 9927]"12"
r lrange mylist 0 -1
r ping ; # It's enough if the server is still alive
} {PONG}
test {Stress tester for #3343-alike bugs} {
r del key
for {set j 0} {$j < 10000} {incr j} {
set op [randomInt 6]
set small_signed_count [expr 5-[randomInt 10]]
if {[randomInt 2] == 0} {
set ele [randomInt 1000]
} else {
set ele [string repeat x [randomInt 10000]][randomInt 1000]
}
switch $op {
0 {r lpush key $ele}
1 {r rpush key $ele}
2 {r lpop key}
3 {r rpop key}
4 {
catch {r lset key $small_signed_count $ele}
}
5 {
set otherele [randomInt 1000]
if {[randomInt 2] == 0} {
set where before
} else {
set where after
}
r linsert key $where $otherele $ele
}
}
}
}
tags {slow} {
test {ziplist implementation: value encoding and backlink} {
if {$::accurate} {set iterations 100} else {set iterations 10}
+3 -1
View File
@@ -507,7 +507,9 @@ start_server {
create_list xlist "$large c"
assert_equal 3 [r rpushx xlist d]
assert_equal 4 [r lpushx xlist a]
assert_equal "a $large c d" [r lrange xlist 0 -1]
assert_equal 6 [r rpushx xlist 42 x]
assert_equal 9 [r lpushx xlist y3 y2 y1]
assert_equal "y1 y2 y3 a $large c d 42 x" [r lrange xlist 0 -1]
}
test "LINSERT - $type" {
+2 -6
View File
@@ -722,9 +722,7 @@ start_server {tags {"zset"}} {
assert_encoding $encoding zscoretest
for {set i 0} {$i < $elements} {incr i} {
set x [expr { double([lindex $aux $i]) } ]
set y [expr { double([r zscore zscoretest $i]) } ]
assert_equal $x $y
assert_equal [lindex $aux $i] [r zscore zscoretest $i]
}
}
@@ -740,9 +738,7 @@ start_server {tags {"zset"}} {
r debug reload
assert_encoding $encoding zscoretest
for {set i 0} {$i < $elements} {incr i} {
set x [expr { double([lindex $aux $i]) } ]
set y [expr { double([r zscore zscoretest $i]) } ]
assert_equal $x $y
assert_equal [lindex $aux $i] [r zscore zscoretest $i]
}
}
+42
View File
@@ -0,0 +1,42 @@
start_server {tags {"wait"}} {
start_server {} {
set slave [srv 0 client]
set slave_host [srv 0 host]
set slave_port [srv 0 port]
set master [srv -1 client]
set master_host [srv -1 host]
set master_port [srv -1 port]
test {Setup slave} {
$slave slaveof $master_host $master_port
wait_for_condition 50 100 {
[s 0 master_link_status] eq {up}
} else {
fail "Replication not started."
}
}
test {WAIT should acknowledge 1 additional copy of the data} {
$master set foo 0
$master incr foo
$master incr foo
$master incr foo
assert {[$master wait 1 5000] == 1}
assert {[$slave get foo] == 3}
}
test {WAIT should not acknowledge 2 additional copies of the data} {
$master incr foo
assert {[$master wait 2 1000] <= 1}
}
test {WAIT should not acknowledge 1 additional copy if slave is blocked} {
exec src/redis-cli -h $slave_host -p $slave_port debug sleep 5 > /dev/null 2> /dev/null &
after 1000 ;# Give redis-cli the time to execute the command.
$master set foo 0
$master incr foo
$master incr foo
$master incr foo
assert {[$master wait 1 3000] == 0}
}
}}