From 8403010e55b3ce603734205f45cef3a5b3cc007e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Wed, 27 Nov 2024 18:08:33 +0100 Subject: [PATCH] Add replication test between old and new version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This includes a way to run two versions of the server from the TCL test framework. The runtest script accepts a new parameter --old-server-path path/to/valkey-server and a new tag "needs:old-server" for test cases and start_server. Includes an attempt to run it in CI as well. Signed-off-by: Viktor Söderqvist --- .github/workflows/ci.yml | 7 ++- .../integration/cross-version-replication.tcl | 34 ++++++++++++++ tests/support/server.tcl | 46 +++++++++++++------ tests/test_helper.tcl | 7 +++ 4 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 tests/integration/cross-version-replication.tcl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fec424cee..6928fb0f13 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,10 +18,15 @@ jobs: # Fail build if there are warnings # build with TLS just for compilation coverage run: make -j4 all-with-unit-tests SERVER_CFLAGS='-Werror' BUILD_TLS=yes USE_FAST_FLOAT=yes + - name: install old server for compatibility testing + run: | + cd tests/tmp + wget --progress=none https://download.valkey.io/releases/valkey-7.2.7-focal-x86_64.tar.gz + tar -xvf valkey-7.2.7-focal-x86_64.tar.gz - name: test run: | sudo apt-get install tcl8.6 tclx - ./runtest --verbose --tags -slow --dump-logs + ./runtest --verbose --tags -slow --dump-logs --old-server-path tests/tmp/valkey-7.2.7-focal-x86_64/bin/valkey-server - name: module api test run: CFLAGS='-Werror' ./runtest-moduleapi --verbose --dump-logs - name: validate commands.def up to date diff --git a/tests/integration/cross-version-replication.tcl b/tests/integration/cross-version-replication.tcl new file mode 100644 index 0000000000..cae3be2b11 --- /dev/null +++ b/tests/integration/cross-version-replication.tcl @@ -0,0 +1,34 @@ +# Test replication from an older version primary. +# +# Use minimal.conf to make sure we don't use any configs not supported on the old version. + +proc server_name_and_version {} { + set server_name [s server_name] + if {$server_name eq {}} { + set server_name redis + } + set server_version [s "${server_name}_version"] + return "$server_name $server_version" +} + +start_server {tags {"repl needs:old-server external:skip"} start-old-server 1 config "minimal.conf"} { + set primary_name_and_version [server_name_and_version] + r set foo bar + + start_server {} { + test "Start replication from $primary_name_and_version" { + r replicaof [srv -1 host] [srv -1 port] + wait_for_sync r + # The key has been transferred. + assert_equal bar [r get foo] + assert_equal up [s master_link_status] + } + + test "Replicate a SET command from $primary_name_and_version" { + r -1 set baz quux + wait_for_ofs_sync [srv 0 client] [srv -1 client] + set reply [r get baz] + assert_equal $reply quux + } + } +} diff --git a/tests/support/server.tcl b/tests/support/server.tcl index 7257339042..4a7ca03653 100644 --- a/tests/support/server.tcl +++ b/tests/support/server.tcl @@ -2,9 +2,9 @@ set ::global_overrides {} set ::tags {} set ::valgrind_errors {} -proc start_server_error {config_file error} { +proc start_server_error {executable config_file error} { set err {} - append err "Can't start the Valkey server\n" + append err "Can't start $executable\n" append err "CONFIGURATION:\n" append err [exec cat $config_file] append err "\nERROR:\n" @@ -216,6 +216,11 @@ proc tags_acceptable {tags err_return} { return 0 } + if {$::old_server_path eq {} && [lsearch $tags "needs:old-server"] >= 0} { + set err "Old server path not provided" + return 0 + } + if {$::external && [lsearch $tags "external:skip"] >= 0} { set err "Not supported on external server" return 0 @@ -284,8 +289,8 @@ proc create_server_config_file {filename config config_lines} { close $fp } -proc spawn_server {config_file stdout stderr args} { - set cmd [list src/valkey-server $config_file] +proc spawn_server {executable config_file stdout stderr args} { + set cmd [list $executable $config_file] set args {*}$args if {[llength $args] > 0} { lappend cmd {*}$args @@ -314,7 +319,7 @@ proc spawn_server {config_file stdout stderr args} { } # Wait for actual startup, return 1 if port is busy, 0 otherwise -proc wait_server_started {config_file stdout stderr pid} { +proc wait_server_started {executable config_file stdout stderr pid} { set checkperiod 100; # Milliseconds set maxiter [expr {120*1000/$checkperiod}] ; # Wait up to 2 minutes. set port_busy 0 @@ -325,7 +330,7 @@ proc wait_server_started {config_file stdout stderr pid} { after $checkperiod incr maxiter -1 if {$maxiter == 0} { - start_server_error $config_file "No PID detected in log $stdout" + start_server_error $executable $config_file "No PID detected in log $stdout" puts "--- LOG CONTENT ---" puts [exec cat $stdout] puts "-------------------" @@ -342,7 +347,7 @@ proc wait_server_started {config_file stdout stderr pid} { # Configuration errors are unexpected, but it's helpful to fail fast # to give the feedback to the test runner. if {[regexp {FATAL CONFIG FILE ERROR} [exec cat $stderr]]} { - start_server_error $config_file "Configuration issue prevented Valkey startup" + start_server_error $executable $config_file "Configuration issue prevented Valkey startup" break } } @@ -436,6 +441,7 @@ proc start_server {options {code undefined}} { set args {} set keep_persistence false set config_lines {} + set start_old_server 0 # Wait for the server to be ready and check for server liveness/client connectivity before starting the test. set wait_ready true @@ -443,6 +449,9 @@ proc start_server {options {code undefined}} { # parse options foreach {option value} $options { switch $option { + "start-old-server" { + set start_old_server $value + } "config" { set baseconfig $value } @@ -493,6 +502,15 @@ proc start_server {options {code undefined}} { return } + if {$start_old_server} { + set executable $::old_server_path + if {![file executable $executable]} { + error "File not found or not executable: $executable" + } + } else { + set executable "src/valkey-server" + } + set data [split [exec cat "tests/assets/$baseconfig"] "\n"] set config {} if {$::tls} { @@ -583,15 +601,15 @@ proc start_server {options {code undefined}} { set server_started 0 while {$server_started == 0} { if {$::verbose} { - puts -nonewline "=== ($tags) Starting server ${::host}:${port} " + puts -nonewline "=== ($tags) Starting server on ${::host}:${port} " } send_data_packet $::test_server_fd "server-spawning" "port $port" - set pid [spawn_server $config_file $stdout $stderr $args] + set pid [spawn_server $executable $config_file $stdout $stderr $args] # check that the server actually started - set port_busy [wait_server_started $config_file $stdout $stderr $pid] + set port_busy [wait_server_started $executable $config_file $stdout $stderr $pid] # Sometimes we have to try a different port, even if we checked # for availability. Other test clients may grab the port before we @@ -629,7 +647,7 @@ proc start_server {options {code undefined}} { if {!$serverisup} { set err {} append err [exec cat $stdout] "\n" [exec cat $stderr] - start_server_error $config_file $err + start_server_error $executable $config_file $err return } set server_started 1 @@ -642,6 +660,7 @@ proc start_server {options {code undefined}} { if {[dict exists $config $port_param]} { set port [dict get $config $port_param] } # setup config dict + dict set srv "executable" $executable dict set srv "config_file" $config_file dict set srv "config" $config dict set srv "pid" $pid @@ -796,12 +815,13 @@ proc restart_server {level wait_ready rotate_logs {reconnect 1} {shutdown sigter close $fd } + set executable [dict get $srv "executable"] set config_file [dict get $srv "config_file"] - set pid [spawn_server $config_file $stdout $stderr {}] + set pid [spawn_server $executable $config_file $stdout $stderr {}] # check that the server actually started - wait_server_started $config_file $stdout $stderr $pid + wait_server_started $executable $config_file $stdout $stderr $pid # update the pid in the servers list dict set srv "pid" $pid diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index 1f0658071a..134ca82697 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -69,6 +69,7 @@ set ::single_tests {} set ::run_solo_tests {} set ::skip_till "" set ::external 0; # If "1" this means, we are running against external instance +set ::old_server_path {}; # Used in upgrade and inter-version tests set ::file ""; # If set, runs only the tests in this comma separated list set ::curfile ""; # Hold the filename of the current suite set ::accurate 0; # If true runs fuzz tests with more iterations @@ -600,6 +601,9 @@ proc print_help_screen {} { "--tls-module Run tests in TLS mode with Valkey module." "--host Run tests against an external host." "--port TCP port to use against external host." + "--old-server-path " + " Path to another version of valkey-server, used for inter-version" + " compatibility testing." "--baseport Initial port number for spawned valkey servers." "--portcount Port range for spawned valkey servers." "--singledb Use a single database, avoid SELECT." @@ -673,6 +677,9 @@ for {set j 0} {$j < [llength $argv]} {incr j} { } elseif {$opt eq {--port}} { set ::port $arg incr j + } elseif {$opt eq {--old-server-path}} { + set ::old_server_path $arg + incr j } elseif {$opt eq {--baseport}} { set ::baseport $arg incr j