diff options
| author | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:40:55 +0100 |
|---|---|---|
| committer | Mitja Felicijan <mitja.felicijan@gmail.com> | 2026-01-21 22:40:55 +0100 |
| commit | 5d8dfe892a2ea89f706ee140c3bdcfd89fe03fda (patch) | |
| tree | 1acdfa5220cd13b7be43a2a01368e80d306473ca /examples/redis-unstable/tests/unit/networking.tcl | |
| parent | c7ab12bba64d9c20ccd79b132dac475f7bc3923e (diff) | |
| download | crep-5d8dfe892a2ea89f706ee140c3bdcfd89fe03fda.tar.gz | |
Add Redis source code for testing
Diffstat (limited to 'examples/redis-unstable/tests/unit/networking.tcl')
| -rw-r--r-- | examples/redis-unstable/tests/unit/networking.tcl | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/examples/redis-unstable/tests/unit/networking.tcl b/examples/redis-unstable/tests/unit/networking.tcl new file mode 100644 index 0000000..6a04c22 --- /dev/null +++ b/examples/redis-unstable/tests/unit/networking.tcl @@ -0,0 +1,446 @@ +# +# Copyright (c) 2009-Present, Redis Ltd. +# All rights reserved. +# +# Copyright (c) 2025-present, Valkey contributors. +# All rights reserved. +# +# Licensed under your choice of (a) the Redis Source Available License 2.0 +# (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the +# GNU Affero General Public License v3 (AGPLv3). +# +# Portions of this file are available under BSD3 terms; see REDISCONTRIBUTIONS for more information. +# + +source tests/support/cli.tcl + +test {CONFIG SET port number} { + start_server {} { + if {$::tls} { set port_cfg tls-port} else { set port_cfg port } + + # available port + set avail_port [find_available_port $::baseport $::portcount] + set rd [redis [srv 0 host] [srv 0 port] 0 $::tls] + $rd CONFIG SET $port_cfg $avail_port + $rd close + set rd [redis [srv 0 host] $avail_port 0 $::tls] + $rd PING + + # already inuse port + catch {$rd CONFIG SET $port_cfg $::test_server_port} e + assert_match {*Unable to listen on this port*} $e + $rd close + + # make sure server still listening on the previous port + set rd [redis [srv 0 host] $avail_port 0 $::tls] + $rd PING + $rd close + } +} {} {external:skip} + +test {CONFIG SET bind address} { + start_server {} { + # non-valid address + catch {r CONFIG SET bind "999.999.999.999"} e + assert_match {*Failed to bind to specified addresses*} $e + + # make sure server still bound to the previous address + set rd [redis [srv 0 host] [srv 0 port] 0 $::tls] + $rd PING + $rd close + } +} {} {external:skip} + +# Attempt to connect to host using a client bound to bindaddr, +# and return a non-zero value if successful within specified +# millisecond timeout, or zero otherwise. +proc test_loopback {host bindaddr timeout} { + if {[exec uname] != {Linux}} { + return 0 + } + + after $timeout set ::test_loopback_state timeout + if {[catch { + set server_sock [socket -server accept 0] + set port [lindex [fconfigure $server_sock -sockname] 2] } err]} { + return 0 + } + + proc accept {channel clientaddr clientport} { + set ::test_loopback_state "connected" + close $channel + } + + if {[catch {set client_sock [socket -async -myaddr $bindaddr $host $port]} err]} { + puts "test_loopback: Client connect failed: $err" + } else { + close $client_sock + } + + vwait ::test_loopback_state + close $server_sock + + return [expr {$::test_loopback_state == {connected}}] +} + +test {CONFIG SET bind-source-addr} { + if {[test_loopback 127.0.0.1 127.0.0.2 1000]} { + start_server {} { + start_server {} { + set replica [srv 0 client] + set master [srv -1 client] + + $master config set protected-mode no + + $replica config set bind-source-addr 127.0.0.2 + $replica replicaof [srv -1 host] [srv -1 port] + + wait_for_condition 50 100 { + [s 0 master_link_status] eq {up} + } else { + fail "Replication not started." + } + + assert_match {*ip=127.0.0.2*} [s -1 slave0] + } + } + } else { + if {$::verbose} { puts "Skipping bind-source-addr test." } + } +} {} {external:skip} + +start_server {config "minimal.conf" tags {"external:skip"}} { + test {Default bind address configuration handling} { + # Default is explicit and sane + assert_equal "* -::*" [lindex [r CONFIG GET bind] 1] + + # CONFIG REWRITE acknowledges this as a default + r CONFIG REWRITE + assert_equal 0 [count_message_lines [srv 0 config_file] bind] + + # Removing the bind address works + r CONFIG SET bind "" + assert_equal "" [lindex [r CONFIG GET bind] 1] + + # No additional clients can connect + catch {redis_client} err + assert_match {*connection refused*} $err + + # CONFIG REWRITE handles empty bindaddr + r CONFIG REWRITE + assert_equal 1 [count_message_lines [srv 0 config_file] bind] + + # Make sure we're able to restart + restart_server 0 0 0 0 + + # Make sure bind parameter is as expected and server handles binding + # accordingly. + # (it seems that rediscli_exec behaves differently in RESP3, possibly + # because CONFIG GET returns a dict instead of a list so redis-cli emits + # it in a single line) + if {$::force_resp3} { + assert_equal {{bind }} [rediscli_exec 0 config get bind] + } else { + assert_equal {bind {}} [rediscli_exec 0 config get bind] + } + catch {reconnect 0} err + assert_match {*connection refused*} $err + + assert_equal {OK} [rediscli_exec 0 config set bind *] + reconnect 0 + r ping + } {PONG} + + test {Protected mode works as expected} { + # Get a non-loopback address of this instance for this test. + set myaddr [get_nonloopback_addr] + if {$myaddr != "" && ![string match {127.*} $myaddr]} { + # Non-loopback client should fail by default + set r2 [get_nonloopback_client] + catch {$r2 ping} err + assert_match {*DENIED*} $err + + # Bind configuration should not matter + assert_equal {OK} [r config set bind "*"] + set r2 [get_nonloopback_client] + catch {$r2 ping} err + assert_match {*DENIED*} $err + + # Setting a password should disable protected mode + assert_equal {OK} [r config set requirepass "secret"] + set r2 [redis $myaddr [srv 0 "port"] 0 $::tls] + assert_equal {OK} [$r2 auth secret] + assert_equal {PONG} [$r2 ping] + + # Clearing the password re-enables protected mode + assert_equal {OK} [r config set requirepass ""] + set r2 [redis $myaddr [srv 0 "port"] 0 $::tls] + assert_match {*DENIED*} $err + + # Explicitly disabling protected-mode works + assert_equal {OK} [r config set protected-mode no] + set r2 [redis $myaddr [srv 0 "port"] 0 $::tls] + assert_equal {PONG} [$r2 ping] + } + } +} + +start_server {config "minimal.conf" tags {"external:skip"} overrides {enable-debug-command {yes} io-threads 2}} { + set server_pid [s process_id] + # Since each thread may perform memory prefetch independently, this test is + # only run when the number of IO threads is 2 to ensure deterministic results. + if {[r config get io-threads] eq "io-threads 2"} { + test {prefetch works as expected when killing a client from the middle of prefetch commands batch} { + # Create 16 (prefetch batch size) +1 clients + for {set i 0} {$i < 16} {incr i} { + set rd$i [redis_deferring_client] + } + + # set a key that will be later be prefetch + r set a 0 + + # Get the client ID of rd4 + $rd4 client id + set rd4_id [$rd4 read] + + # Create a batch of commands by suspending the server for a while + # before responding to the first command + pause_process $server_pid + + # The first client will kill the fourth client + $rd0 client kill id $rd4_id + + # Send set commands for all clients except the first + for {set i 1} {$i < 16} {incr i} { + [set rd$i] set $i $i + [set rd$i] flush + } + + # Resume the server + resume_process $server_pid + + # Read the results + assert_equal {1} [$rd0 read] + catch {$rd4 read} res + if {$res eq "OK"} { + # maybe OK then err, we can not control the order of execution + catch {$rd4 read} err + } else { + set err $res + } + assert_match {I/O error reading reply} $err + + # verify the prefetch stats are as expected + set info [r info stats] + set prefetch_entries [getInfoProperty $info io_threaded_total_prefetch_entries] + assert_range $prefetch_entries 2 15; # With slower machines, the number of prefetch entries can be lower + set prefetch_batches [getInfoProperty $info io_threaded_total_prefetch_batches] + assert_range $prefetch_batches 1 7; # With slower machines, the number of batches can be higher + + # verify other clients are working as expected + for {set i 1} {$i < 16} {incr i} { + if {$i != 4} { ;# 4th client was killed + [set rd$i] get $i + assert_equal {OK} [[set rd$i] read] + assert_equal $i [[set rd$i] read] + } + } + } + + test {prefetch works as expected when changing the batch size while executing the commands batch} { + # Create 16 (default prefetch batch size) clients + for {set i 0} {$i < 16} {incr i} { + set rd$i [redis_deferring_client] + } + + # Create a batch of commands by suspending the server for a while + # before responding to the first command + pause_process $server_pid + + # Send set commands for all clients the 5th client will change the prefetch batch size + for {set i 0} {$i < 16} {incr i} { + if {$i == 4} { + [set rd$i] config set prefetch-batch-max-size 1 + } + [set rd$i] set a $i + [set rd$i] flush + } + # Resume the server + resume_process $server_pid + # Read the results + for {set i 0} {$i < 16} {incr i} { + assert_equal {OK} [[set rd$i] read] + [set rd$i] close + } + + # assert the configured prefetch batch size was changed + assert {[r config get prefetch-batch-max-size] eq "prefetch-batch-max-size 1"} + } + + proc do_prefetch_batch {server_pid batch_size} { + # Create clients + for {set i 0} {$i < $batch_size} {incr i} { + set rd$i [redis_deferring_client] + } + + # Suspend the server to batch the commands + pause_process $server_pid + + # Send commands from all clients + for {set i 0} {$i < $batch_size} {incr i} { + [set rd$i] set a $i + [set rd$i] flush + } + + # Resume the server to process the batch + resume_process $server_pid + + # Verify responses + for {set i 0} {$i < $batch_size} {incr i} { + assert_equal {OK} [[set rd$i] read] + [set rd$i] close + } + } + + test {no prefetch when the batch size is set to 0} { + # set the batch size to 0 + r config set prefetch-batch-max-size 0 + # save the current value of prefetch entries + set info [r info stats] + set prefetch_entries [getInfoProperty $info io_threaded_total_prefetch_entries] + + do_prefetch_batch $server_pid 16 + + # assert the prefetch entries did not change + set info [r info stats] + set new_prefetch_entries [getInfoProperty $info io_threaded_total_prefetch_entries] + assert_equal $prefetch_entries $new_prefetch_entries + } + + test {Prefetch can resume working when the configuration option is set to a non-zero value} { + # save the current value of prefetch entries + set info [r info stats] + set prefetch_entries [getInfoProperty $info io_threaded_total_prefetch_entries] + # set the batch size to 0 + r config set prefetch-batch-max-size 16 + + do_prefetch_batch $server_pid 16 + + # assert the prefetch entries did not change + set info [r info stats] + set new_prefetch_entries [getInfoProperty $info io_threaded_total_prefetch_entries] + # With slower machines, the number of prefetch entries can be lower + assert_range $new_prefetch_entries [expr {$prefetch_entries + 2}] [expr {$prefetch_entries + 16}] + } + + test {Prefetch works with batch size greater than 16 (buffer overflow regression test)} { + # save the current value of prefetch entries + set info [r info stats] + set prefetch_entries [getInfoProperty $info io_threaded_total_prefetch_entries] + # set the batch size to a value greater than the old hardcoded limit of 16 + r config set prefetch-batch-max-size 64 + + # Create a batch with more than 16 clients to trigger the old buffer overflow + do_prefetch_batch $server_pid 64 + + # verify the prefetch entries increased + set info [r info stats] + set new_prefetch_entries [getInfoProperty $info io_threaded_total_prefetch_entries] + # With slower machines, the number of prefetch entries can be lower + assert_range $new_prefetch_entries [expr {$prefetch_entries + 2}] [expr {$prefetch_entries + 64}] + } + + test {Prefetch works with maximum batch size of 128 and client number larger than batch size} { + # save the current value of prefetch entries + set info [r info stats] + set prefetch_entries [getInfoProperty $info io_threaded_total_prefetch_entries] + # set the batch size to the maximum allowed value + r config set prefetch-batch-max-size 128 + + # Create a batch with 300 clients to test the maximum limit + do_prefetch_batch $server_pid 300 + + # verify the prefetch entries increased + set info [r info stats] + set new_prefetch_entries [getInfoProperty $info io_threaded_total_prefetch_entries] + # With slower machines, the number of prefetch entries can be lower + assert_range $new_prefetch_entries [expr {$prefetch_entries + 2}] [expr {$prefetch_entries + 300}] + } + } +} + +start_server {tags {"timeout external:skip"}} { + test {Multiple clients idle timeout test} { + # set client timeout to 1 second + r config set timeout 1 + + # create multiple client connections + set clients {} + set num_clients 10 + + for {set i 0} {$i < $num_clients} {incr i} { + set client [redis_deferring_client] + $client ping + assert_equal "PONG" [$client read] + lappend clients $client + } + assert_equal [llength $clients] $num_clients + + # wait for 2.5 seconds + after 2500 + + # try to send commands to all clients - they should all fail due to timeout + set disconnected_count 0 + foreach client $clients { + $client ping + if {[catch {$client read} err]} { + incr disconnected_count + # expected error patterns for connection timeout + assert_match {*I/O error*} $err + } + catch {$client close} + } + + # all clients should have been disconnected due to timeout + assert_equal $disconnected_count $num_clients + + # redis server still works well + reconnect + assert_equal "PONG" [r ping] + } +} + +test {Pending command pool expansion and shrinking} { + start_server {overrides {loglevel debug io-threads 1} tags {external:skip}} { + set rd1 [redis_deferring_client] + set rd2 [redis_deferring_client] + + # Client1 sends 16 commands in pipeline, and was blocked at the first command + set buf "" + append buf "blpop mylist 0\r\n" + for {set i 1} {$i < 16} {incr i} { + append buf "set key$i value$i\r\n" + } + $rd1 write $buf + $rd1 flush + wait_for_blocked_clients_count 1 + + # Client2 sends 1 command, this will trigger pending command pool expansion + # from 16 to 32 since A client has used up all 16 commands in the command pool. + $rd2 set bkey bvalue + assert_equal {OK} [$rd2 read] + + # Unblock client1, allowing it to return all pending commands back to the pool. + r lpush mylist unblock_value + assert_equal {mylist unblock_value} [$rd1 read] + for {set i 1} {$i < 16} {incr i} { + assert_equal {OK} [$rd1 read] + } + + # Wait for the pending command pool to shrink back to 16 due to low utilization. + wait_for_log_messages 0 {"*Shrunk pending command pool: capacity 32->16*"} 0 10 1000 + + $rd1 close + $rd2 close + } +} |
