Merge branch 'unstable' of github.com:/antirez/redis into unstable

This commit is contained in:
antirez 2020-05-28 10:08:29 +02:00
commit 6f1ddda9b2
8 changed files with 287 additions and 33 deletions

View File

@ -47,7 +47,9 @@ jobs:
sudo apt-get install tcl8.5 sudo apt-get install tcl8.5
./runtest --accurate --verbose ./runtest --accurate --verbose
- name: module api test - name: module api test
run: ./runtest-moduleapi --verbose run: |
make -C tests/modules 32bit # the script below doesn't have an argument, we must build manually ahead of time
./runtest-moduleapi --verbose
test-tls: test-tls:
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@ -25,6 +25,7 @@ set ::sentinel_instances {}
set ::redis_instances {} set ::redis_instances {}
set ::sentinel_base_port 20000 set ::sentinel_base_port 20000
set ::redis_base_port 30000 set ::redis_base_port 30000
set ::redis_port_count 1024
set ::pids {} ; # We kill everything at exit set ::pids {} ; # We kill everything at exit
set ::dirs {} ; # We remove all the temp dirs at exit set ::dirs {} ; # We remove all the temp dirs at exit
set ::run_matching {} ; # If non empty, only tests matching pattern are run. set ::run_matching {} ; # If non empty, only tests matching pattern are run.
@ -57,7 +58,7 @@ proc exec_instance {type cfgfile} {
# Spawn a redis or sentinel instance, depending on 'type'. # Spawn a redis or sentinel instance, depending on 'type'.
proc spawn_instance {type base_port count {conf {}}} { proc spawn_instance {type base_port count {conf {}}} {
for {set j 0} {$j < $count} {incr j} { for {set j 0} {$j < $count} {incr j} {
set port [find_available_port $base_port] set port [find_available_port $base_port $::redis_port_count]
incr base_port incr base_port
puts "Starting $type #$j at port $port" puts "Starting $type #$j at port $port"

View File

@ -0,0 +1,231 @@
# These tests were added together with the meaningful offset implementation
# in redis 6.0.0, which was later abandoned in 6.0.4, they used to test that
# servers are able to PSYNC with replicas even if the replication stream has
# PINGs at the end which present in one sever and missing on another.
# We keep these tests just because they reproduce edge cases in the replication
# logic in hope they'll be able to spot some problem in the future.
start_server {tags {"psync2"}} {
start_server {} {
# Config
set debug_msg 0 ; # Enable additional debug messages
for {set j 0} {$j < 2} {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]
$R($j) CONFIG SET repl-ping-replica-period 1
if {$debug_msg} {puts "Log file: [srv [expr 0-$j] stdout]"}
}
# Setup replication
test "PSYNC2 pingoff: setup" {
$R(1) replicaof $R_host(0) $R_port(0)
$R(0) set foo bar
wait_for_condition 50 1000 {
[status $R(1) master_link_status] == "up" &&
[$R(0) dbsize] == 1 && [$R(1) dbsize] == 1
} else {
fail "Replicas not replicating from master"
}
}
test "PSYNC2 pingoff: write and wait replication" {
$R(0) INCR counter
$R(0) INCR counter
$R(0) INCR counter
wait_for_condition 50 1000 {
[$R(0) GET counter] eq [$R(1) GET counter]
} else {
fail "Master and replica don't agree about counter"
}
}
# In this test we'll make sure the replica will get stuck, but with
# an active connection: this way the master will continue to send PINGs
# every second (we modified the PING period earlier)
test "PSYNC2 pingoff: pause replica and promote it" {
$R(1) MULTI
$R(1) DEBUG SLEEP 5
$R(1) SLAVEOF NO ONE
$R(1) EXEC
$R(1) ping ; # Wait for it to return back available
}
test "Make the old master a replica of the new one and check conditions" {
assert_equal [status $R(1) sync_full] 0
$R(0) REPLICAOF $R_host(1) $R_port(1)
wait_for_condition 50 1000 {
[status $R(1) sync_full] == 1
} else {
fail "The new master was not able to sync"
}
# make sure replication is still alive and kicking
$R(1) incr x
wait_for_condition 50 1000 {
[$R(0) get x] == 1
} else {
fail "replica didn't get incr"
}
assert_equal [status $R(0) master_repl_offset] [status $R(1) master_repl_offset]
}
}}
start_server {tags {"psync2"}} {
start_server {} {
start_server {} {
start_server {} {
start_server {} {
test {test various edge cases of repl topology changes with missing pings at the end} {
set master [srv -4 client]
set master_host [srv -4 host]
set master_port [srv -4 port]
set replica1 [srv -3 client]
set replica2 [srv -2 client]
set replica3 [srv -1 client]
set replica4 [srv -0 client]
$replica1 replicaof $master_host $master_port
$replica2 replicaof $master_host $master_port
$replica3 replicaof $master_host $master_port
$replica4 replicaof $master_host $master_port
wait_for_condition 50 1000 {
[status $master connected_slaves] == 4
} else {
fail "replicas didn't connect"
}
$master incr x
wait_for_condition 50 1000 {
[$replica1 get x] == 1 && [$replica2 get x] == 1 &&
[$replica3 get x] == 1 && [$replica4 get x] == 1
} else {
fail "replicas didn't get incr"
}
# disconnect replica1 and replica2
# and wait for the master to send a ping to replica3 and replica4
$replica1 replicaof no one
$replica2 replicaof 127.0.0.1 1 ;# we can't promote it to master since that will cycle the replication id
$master config set repl-ping-replica-period 1
after 1500
# make everyone sync from the replica1 that didn't get the last ping from the old master
# replica4 will keep syncing from the old master which now syncs from replica1
# and replica2 will re-connect to the old master (which went back in time)
set new_master_host [srv -3 host]
set new_master_port [srv -3 port]
$replica3 replicaof $new_master_host $new_master_port
$master replicaof $new_master_host $new_master_port
$replica2 replicaof $master_host $master_port
wait_for_condition 50 1000 {
[status $replica2 master_link_status] == "up" &&
[status $replica3 master_link_status] == "up" &&
[status $replica4 master_link_status] == "up" &&
[status $master master_link_status] == "up"
} else {
fail "replicas didn't connect"
}
# make sure replication is still alive and kicking
$replica1 incr x
wait_for_condition 50 1000 {
[$replica2 get x] == 2 &&
[$replica3 get x] == 2 &&
[$replica4 get x] == 2 &&
[$master get x] == 2
} else {
fail "replicas didn't get incr"
}
# make sure we have the right amount of full syncs
assert_equal [status $master sync_full] 6
assert_equal [status $replica1 sync_full] 2
assert_equal [status $replica2 sync_full] 0
assert_equal [status $replica3 sync_full] 0
assert_equal [status $replica4 sync_full] 0
# force psync
$master client kill type master
$replica2 client kill type master
$replica3 client kill type master
$replica4 client kill type master
# make sure replication is still alive and kicking
$replica1 incr x
wait_for_condition 50 1000 {
[$replica2 get x] == 3 &&
[$replica3 get x] == 3 &&
[$replica4 get x] == 3 &&
[$master get x] == 3
} else {
fail "replicas didn't get incr"
}
# make sure we have the right amount of full syncs
assert_equal [status $master sync_full] 6
assert_equal [status $replica1 sync_full] 2
assert_equal [status $replica2 sync_full] 0
assert_equal [status $replica3 sync_full] 0
assert_equal [status $replica4 sync_full] 0
}
}}}}}
start_server {tags {"psync2"}} {
start_server {} {
start_server {} {
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]
$R($j) CONFIG SET repl-ping-replica-period 1
}
test "Chained replicas disconnect when replica re-connect with the same master" {
# Add a second replica as a chained replica of the current replica
$R(1) replicaof $R_host(0) $R_port(0)
$R(2) replicaof $R_host(1) $R_port(1)
wait_for_condition 50 1000 {
[status $R(2) master_link_status] == "up"
} else {
fail "Chained replica not replicating from its master"
}
# Do a write on the master, and wait for 3 seconds for the master to
# send some PINGs to its replica
$R(0) INCR counter2
after 2000
set sync_partial_master [status $R(0) sync_partial_ok]
set sync_partial_replica [status $R(1) sync_partial_ok]
$R(0) CONFIG SET repl-ping-replica-period 100
# Disconnect the master's direct replica
$R(0) client kill type replica
wait_for_condition 50 1000 {
[status $R(1) master_link_status] == "up" &&
[status $R(2) master_link_status] == "up" &&
[status $R(0) sync_partial_ok] == $sync_partial_master + 1 &&
[status $R(1) sync_partial_ok] == $sync_partial_replica
} else {
fail "Disconnected replica failed to PSYNC with master"
}
# Verify that the replica and its replica's meaningful and real
# offsets match with the master
assert_equal [status $R(0) master_repl_offset] [status $R(1) master_repl_offset]
assert_equal [status $R(0) master_repl_offset] [status $R(2) master_repl_offset]
# make sure replication is still alive and kicking
$R(0) incr counter2
wait_for_condition 50 1000 {
[$R(1) get counter2] == 2 && [$R(2) get counter2] == 2
} else {
fail "replicas didn't get incr"
}
assert_equal [status $R(0) master_repl_offset] [status $R(1) master_repl_offset]
assert_equal [status $R(0) master_repl_offset] [status $R(2) master_repl_offset]
}
}}}

View File

@ -28,11 +28,14 @@ TEST_MODULES = \
all: $(TEST_MODULES) all: $(TEST_MODULES)
32bit:
$(MAKE) CFLAGS="-m32" LDFLAGS="-melf_i386"
%.xo: %.c ../../src/redismodule.h %.xo: %.c ../../src/redismodule.h
$(CC) -I../../src $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@ $(CC) -I../../src $(CFLAGS) $(SHOBJ_CFLAGS) -fPIC -c $< -o $@
%.so: %.xo %.so: %.xo
$(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LIBS) -lc $(LD) -o $@ $< $(SHOBJ_LDFLAGS) $(LDFLAGS) $(LIBS) -lc
.PHONY: clean .PHONY: clean

View File

@ -214,14 +214,14 @@ proc start_server {options {code undefined}} {
dict set config dir [tmpdir server] dict set config dir [tmpdir server]
# start every server on a different port # start every server on a different port
set ::port [find_available_port [expr {$::port+1}]] set port [find_available_port $::baseport $::portcount]
if {$::tls} { if {$::tls} {
dict set config "port" 0 dict set config "port" 0
dict set config "tls-port" $::port dict set config "tls-port" $port
dict set config "tls-cluster" "yes" dict set config "tls-cluster" "yes"
dict set config "tls-replication" "yes" dict set config "tls-replication" "yes"
} else { } else {
dict set config port $::port dict set config port $port
} }
set unixsocket [file normalize [format "%s/%s" [dict get $config "dir"] "socket"]] set unixsocket [file normalize [format "%s/%s" [dict get $config "dir"] "socket"]]
@ -243,10 +243,10 @@ proc start_server {options {code undefined}} {
set server_started 0 set server_started 0
while {$server_started == 0} { while {$server_started == 0} {
if {$::verbose} { if {$::verbose} {
puts -nonewline "=== ($tags) Starting server ${::host}:${::port} " puts -nonewline "=== ($tags) Starting server ${::host}:${port} "
} }
send_data_packet $::test_server_fd "server-spawning" "port $::port" send_data_packet $::test_server_fd "server-spawning" "port $port"
if {$::valgrind} { if {$::valgrind} {
set pid [exec valgrind --track-origins=yes --suppressions=src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/redis-server $config_file > $stdout 2> $stderr &] set pid [exec valgrind --track-origins=yes --suppressions=src/valgrind.sup --show-reachable=no --show-possibly-lost=no --leak-check=full src/redis-server $config_file > $stdout 2> $stderr &]
@ -291,19 +291,19 @@ proc start_server {options {code undefined}} {
# for availability. Other test clients may grab the port before we # for availability. Other test clients may grab the port before we
# are able to do it for example. # are able to do it for example.
if {$port_busy} { if {$port_busy} {
puts "Port $::port was already busy, trying another port..." puts "Port $port was already busy, trying another port..."
set ::port [find_available_port [expr {$::port+1}]] set port [find_available_port $::baseport $::portcount]
if {$::tls} { if {$::tls} {
dict set config "tls-port" $::port dict set config "tls-port" $port
} else { } else {
dict set config port $::port dict set config port $port
} }
create_server_config_file $config_file $config create_server_config_file $config_file $config
continue; # Try again continue; # Try again
} }
if {$code ne "undefined"} { if {$code ne "undefined"} {
set serverisup [server_is_up $::host $::port $retrynum] set serverisup [server_is_up $::host $port $retrynum]
} else { } else {
set serverisup 1 set serverisup 1
} }
@ -324,7 +324,6 @@ proc start_server {options {code undefined}} {
# setup properties to be able to initialize a client object # setup properties to be able to initialize a client object
set port_param [expr $::tls ? {"tls-port"} : {"port"}] set port_param [expr $::tls ? {"tls-port"} : {"port"}]
set host $::host set host $::host
set port $::port
if {[dict exists $config bind]} { set host [dict get $config bind] } if {[dict exists $config bind]} { set host [dict get $config bind] }
if {[dict exists $config $port_param]} { set port [dict get $config $port_param] } if {[dict exists $config $port_param]} { set port [dict get $config $port_param] }

View File

@ -344,21 +344,26 @@ proc roundFloat f {
format "%.10g" $f format "%.10g" $f
} }
proc find_available_port start { set ::last_port_attempted 0
for {set j $start} {$j < $start+1024} {incr j} { proc find_available_port {start count} {
if {[catch {set fd1 [socket 127.0.0.1 $j]}] && set port [expr $::last_port_attempted + 1]
[catch {set fd2 [socket 127.0.0.1 [expr $j+10000]]}]} { for {set attempts 0} {$attempts < $count} {incr attempts} {
return $j if {$port < $start || $port >= $start+$count} {
set port $start
}
if {[catch {set fd1 [socket 127.0.0.1 $port]}] &&
[catch {set fd2 [socket 127.0.0.1 [expr $port+10000]]}]} {
set ::last_port_attempted $port
return $port
} else { } else {
catch { catch {
close $fd1 close $fd1
close $fd2 close $fd2
} }
} }
incr port
} }
if {$j == $start+1024} { error "Can't find a non busy port in the $start-[expr {$start+$count-1}] range."
error "Can't find a non busy port in the $start-[expr {$start+1023}] range."
}
} }
# Test if TERM looks like to support colors # Test if TERM looks like to support colors

View File

@ -47,6 +47,7 @@ set ::all_tests {
integration/logging integration/logging
integration/psync2 integration/psync2
integration/psync2-reg integration/psync2-reg
integration/psync2-pingoff
unit/pubsub unit/pubsub
unit/slowlog unit/slowlog
unit/scripting unit/scripting
@ -69,7 +70,9 @@ set ::all_tests {
set ::next_test 0 set ::next_test 0
set ::host 127.0.0.1 set ::host 127.0.0.1
set ::port 21111 set ::port 6379; # port for external server
set ::baseport 21111; # initial port for spawned redis servers
set ::portcount 8000; # we don't wanna use more than 10000 to avoid collision with cluster bus ports
set ::traceleaks 0 set ::traceleaks 0
set ::valgrind 0 set ::valgrind 0
set ::tls 0 set ::tls 0
@ -227,26 +230,26 @@ proc test_server_main {} {
set tclsh [info nameofexecutable] set tclsh [info nameofexecutable]
# Open a listening socket, trying different ports in order to find a # Open a listening socket, trying different ports in order to find a
# non busy one. # non busy one.
set port [find_available_port 11111] set clientport [find_available_port 11111 32]
if {!$::quiet} { if {!$::quiet} {
puts "Starting test server at port $port" puts "Starting test server at port $clientport"
} }
socket -server accept_test_clients -myaddr 127.0.0.1 $port socket -server accept_test_clients -myaddr 127.0.0.1 $clientport
# Start the client instances # Start the client instances
set ::clients_pids {} set ::clients_pids {}
if {$::external} { if {$::external} {
set p [exec $tclsh [info script] {*}$::argv \ set p [exec $tclsh [info script] {*}$::argv \
--client $port --port $::port &] --client $clientport &]
lappend ::clients_pids $p lappend ::clients_pids $p
} else { } else {
set start_port [expr {$::port+100}] set start_port $::baseport
set port_count [expr {$::portcount / $::numclients}]
for {set j 0} {$j < $::numclients} {incr j} { for {set j 0} {$j < $::numclients} {incr j} {
set start_port [find_available_port $start_port]
set p [exec $tclsh [info script] {*}$::argv \ set p [exec $tclsh [info script] {*}$::argv \
--client $port --port $start_port &] --client $clientport --baseport $start_port --portcount $port_count &]
lappend ::clients_pids $p lappend ::clients_pids $p
incr start_port 10 incr start_port $port_count
} }
} }
@ -509,6 +512,10 @@ proc print_help_screen {} {
"--loop Execute the specified set of tests forever." "--loop Execute the specified set of tests forever."
"--wait-server Wait after server is started (so that you can attach a debugger)." "--wait-server Wait after server is started (so that you can attach a debugger)."
"--tls Run tests in TLS mode." "--tls Run tests in TLS mode."
"--host <addr> Run tests against an external host."
"--port <port> TCP port to use against external host."
"--baseport <port> Initial port number for spawned redis servers."
"--portcount <num> Port range for spawned redis servers."
"--help Print this help screen." "--help Print this help screen."
} "\n"] } "\n"]
} }
@ -559,6 +566,12 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
} elseif {$opt eq {--port}} { } elseif {$opt eq {--port}} {
set ::port $arg set ::port $arg
incr j incr j
} elseif {$opt eq {--baseport}} {
set ::baseport $arg
incr j
} elseif {$opt eq {--portcount}} {
set ::portcount $arg
incr j
} elseif {$opt eq {--accurate}} { } elseif {$opt eq {--accurate}} {
set ::accurate 1 set ::accurate 1
} elseif {$opt eq {--force-failure}} { } elseif {$opt eq {--force-failure}} {

View File

@ -167,9 +167,9 @@ start_server {tags {"other"}} {
tags {protocol} { tags {protocol} {
test {PIPELINING stresser (also a regression for the old epoll bug)} { test {PIPELINING stresser (also a regression for the old epoll bug)} {
if {$::tls} { if {$::tls} {
set fd2 [::tls::socket $::host $::port] set fd2 [::tls::socket [srv host] [srv port]]
} else { } else {
set fd2 [socket $::host $::port] set fd2 [socket [srv host] [srv port]]
} }
fconfigure $fd2 -encoding binary -translation binary fconfigure $fd2 -encoding binary -translation binary
puts -nonewline $fd2 "SELECT 9\r\n" puts -nonewline $fd2 "SELECT 9\r\n"