summaryrefslogtreecommitdiff
path: root/examples/redis-unstable/tests/unit/client-eviction.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/tests/unit/client-eviction.tcl')
-rw-r--r--examples/redis-unstable/tests/unit/client-eviction.tcl623
1 files changed, 0 insertions, 623 deletions
diff --git a/examples/redis-unstable/tests/unit/client-eviction.tcl b/examples/redis-unstable/tests/unit/client-eviction.tcl
deleted file mode 100644
index ac2860f..0000000
--- a/examples/redis-unstable/tests/unit/client-eviction.tcl
+++ /dev/null
@@ -1,623 +0,0 @@
-tags {"external:skip logreqres:skip"} {
-
-# Get info about a redis client connection:
-# name - name of client we want to query
-# f - field name from "CLIENT LIST" we want to get
-proc client_field {name f} {
- set clients [split [string trim [r client list]] "\r\n"]
- set c [lsearch -inline $clients *name=$name*]
- if {![regexp $f=(\[a-zA-Z0-9-\]+) $c - res]} {
- error "no client named $name found with field $f"
- }
- return $res
-}
-
-proc client_exists {name} {
- if {[catch { client_field $name tot-mem } e]} {
- return false
- }
- return true
-}
-
-proc gen_client {} {
- set rr [redis_client]
- set name "tst_[randstring 4 4 simplealpha]"
- $rr client setname $name
- assert {[client_exists $name]}
- return [list $rr $name]
-}
-
-# Sum a value across all redis client connections:
-# f - the field name from "CLIENT LIST" we want to sum
-proc clients_sum {f} {
- set sum 0
- set clients [split [string trim [r client list]] "\r\n"]
- foreach c $clients {
- if {![regexp $f=(\[a-zA-Z0-9-\]+) $c - res]} {
- error "field $f not found in $c"
- }
- incr sum $res
- }
- return $sum
-}
-
-proc mb {v} {
- return [expr $v * 1024 * 1024]
-}
-
-proc kb {v} {
- return [expr $v * 1024]
-}
-
-start_server {} {
- set maxmemory_clients 3000000
- r config set maxmemory-clients $maxmemory_clients
- r debug reply-copy-avoidance 0 ;# Disable copy avoidance because it affects memory usage
-
- test "client evicted due to large argv" {
- r flushdb
- lassign [gen_client] rr cname
- # Attempt a large multi-bulk command under eviction limit
- $rr mset k v k2 [string repeat v 1000000]
- assert_equal [$rr get k] v
- # Attempt another command, now causing client eviction
- catch { $rr mset k v k2 [string repeat v $maxmemory_clients] } e
- assert {![client_exists $cname]}
- $rr close
- }
-
- test "client evicted due to large query buf" {
- r flushdb
- lassign [gen_client] rr cname
- # Attempt to fill the query buff without completing the argument above the limit, causing client eviction
- catch {
- $rr write [join [list "*1\r\n\$$maxmemory_clients\r\n" [string repeat v $maxmemory_clients]] ""]
- $rr flush
- $rr read
- } e
- assert {![client_exists $cname]}
- $rr close
- }
-
- test "client evicted due to percentage of maxmemory" {
- set maxmemory [mb 6]
- r config set maxmemory $maxmemory
- # Set client eviction threshold to 7% of maxmemory
- set maxmemory_clients_p 7
- r config set maxmemory-clients $maxmemory_clients_p%
- r flushdb
-
- set maxmemory_clients_actual [expr $maxmemory * $maxmemory_clients_p / 100]
-
- lassign [gen_client] rr cname
- # Attempt to fill the query buff with only half the percentage threshold verify we're not disconnected
- set n [expr $maxmemory_clients_actual / 2]
- $rr write [join [list "*1\r\n\$$n\r\n" [string repeat v $n]] ""]
- $rr flush
- wait_for_condition 100 10 {
- [client_field $cname tot-mem] >= $n
- } else {
- fail "Failed to fill qbuf for test"
- }
- set tot_mem [client_field $cname tot-mem]
- assert {$tot_mem >= $n && $tot_mem < $maxmemory_clients_actual}
-
- # Attempt to fill the query buff with the percentage threshold of maxmemory and verify we're evicted
- $rr close
- lassign [gen_client] rr cname
- catch {
- $rr write [join [list "*1\r\n\$$maxmemory_clients_actual\r\n" [string repeat v $maxmemory_clients_actual]] ""]
- $rr flush
- } e
- wait_for_condition 100 10 {
- ![client_exists $cname]
- } else {
- fail "Failed to evict client"
- }
- $rr close
-
- # Restore settings
- r config set maxmemory 0
- r config set maxmemory-clients $maxmemory_clients
- }
-
- test "client evicted due to large multi buf" {
- r flushdb
- lassign [gen_client] rr cname
-
- # Attempt a multi-exec where sum of commands is less than maxmemory_clients
- $rr multi
- $rr set k [string repeat v [expr $maxmemory_clients / 4]]
- $rr set k [string repeat v [expr $maxmemory_clients / 4]]
- assert_equal [$rr exec] {OK OK}
-
- # Attempt a multi-exec where sum of commands is more than maxmemory_clients, causing client eviction
- $rr multi
- catch {
- for {set j 0} {$j < 5} {incr j} {
- $rr set k [string repeat v [expr $maxmemory_clients / 4]]
- }
- } e
- assert {![client_exists $cname]}
- $rr close
- }
-
- test "client evicted due to watched key list" {
- r flushdb
- set rr [redis_client]
-
- # Since watched key list is a small overhead this test uses a minimal maxmemory-clients config
- set temp_maxmemory_clients 200000
- r config set maxmemory-clients $temp_maxmemory_clients
-
- # Append watched keys until list maxes out maxmemory clients and causes client eviction
- catch {
- for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
- $rr watch $j
- }
- } e
- assert_match {I/O error reading reply} $e
- $rr close
-
- # Restore config for next tests
- r config set maxmemory-clients $maxmemory_clients
- }
-
- test "client evicted due to pubsub subscriptions" {
- r flushdb
-
- # Since pubsub subscriptions cause a small overhead this test uses a minimal maxmemory-clients config
- set temp_maxmemory_clients 200000
- r config set maxmemory-clients $temp_maxmemory_clients
-
- # Test eviction due to pubsub patterns
- set rr [redis_client]
- # Add patterns until list maxes out maxmemory clients and causes client eviction
- catch {
- for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
- $rr psubscribe $j
- }
- } e
- assert_match {I/O error reading reply} $e
- $rr close
-
- # Test eviction due to pubsub channels
- set rr [redis_client]
- # Subscribe to global channels until list maxes out maxmemory clients and causes client eviction
- catch {
- for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
- $rr subscribe $j
- }
- } e
- assert_match {I/O error reading reply} $e
- $rr close
-
- # Test eviction due to sharded pubsub channels
- set rr [redis_client]
- # Subscribe to sharded pubsub channels until list maxes out maxmemory clients and causes client eviction
- catch {
- for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
- $rr ssubscribe $j
- }
- } e
- assert_match {I/O error reading reply} $e
- $rr close
-
- # Restore config for next tests
- r config set maxmemory-clients $maxmemory_clients
- }
-
- test "client evicted due to tracking redirection" {
- r flushdb
- set rr [redis_client]
- set redirected_c [redis_client]
- $redirected_c client setname redirected_client
- set redir_id [$redirected_c client id]
- $redirected_c SUBSCRIBE __redis__:invalidate
- $rr client tracking on redirect $redir_id bcast
- # Use a big key name to fill the redirected tracking client's buffer quickly
- set key_length [expr 1024*200]
- set long_key [string repeat k $key_length]
- # Use a script so we won't need to pass the long key name when dirtying it in the loop
- set script_sha [$rr script load "redis.call('incr', '$long_key')"]
-
- # Pause serverCron so it won't update memory usage since we're testing the update logic when
- # writing tracking redirection output
- r debug pause-cron 1
-
- # Read and write to same (long) key until redirected_client's buffers cause it to be evicted
- catch {
- while true {
- set mem [client_field redirected_client tot-mem]
- assert {$mem < $maxmemory_clients}
- $rr evalsha $script_sha 0
- }
- } e
- assert_match {no client named redirected_client found*} $e
-
- r debug pause-cron 0
- $rr close
- $redirected_c close
- } {0} {needs:debug}
-
- test "client evicted due to client tracking prefixes" {
- r flushdb
- set rr [redis_client]
-
- # Since tracking prefixes list is a small overhead this test uses a minimal maxmemory-clients config
- set temp_maxmemory_clients 200000
- r config set maxmemory-clients $temp_maxmemory_clients
-
- # Append tracking prefixes until list maxes out maxmemory clients and causes client eviction
- # Combine more prefixes in each command to speed up the test. Because we did not actually count
- # the memory usage of all prefixes, see getClientMemoryUsage, so we can not use larger prefixes
- # to speed up the test here.
- catch {
- for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
- $rr client tracking on prefix [format a%09s $j] prefix [format b%09s $j] prefix [format c%09s $j] bcast
- }
- } e
- assert_match {I/O error reading reply} $e
- $rr close
-
- # Restore config for next tests
- r config set maxmemory-clients $maxmemory_clients
- }
-
- test "client evicted due to output buf" {
- r flushdb
- r setrange k 200000 v
- set rr [redis_deferring_client]
- $rr client setname test_client
- $rr flush
- assert {[$rr read] == "OK"}
- # Attempt a large response under eviction limit
- $rr get k
- $rr flush
- assert {[string length [$rr read]] == 200001}
- set mem [client_field test_client tot-mem]
- assert {$mem < $maxmemory_clients}
-
- # Fill output buff in loop without reading it and make sure
- # we're eventually disconnected, but before reaching maxmemory_clients
- while true {
- if { [catch {
- set mem [client_field test_client tot-mem]
- assert {$mem < $maxmemory_clients}
- $rr get k
- $rr flush
- } e]} {
- assert {![client_exists test_client]}
- break
- }
- }
- $rr close
- }
-
- foreach {no_evict} {on off} {
- test "client no-evict $no_evict" {
- r flushdb
- r client setname control
- r client no-evict on ;# Avoid evicting the main connection
- lassign [gen_client] rr cname
- $rr client no-evict $no_evict
-
- # Overflow maxmemory-clients
- set qbsize [expr {$maxmemory_clients + 1}]
- if {[catch {
- $rr write [join [list "*1\r\n\$$qbsize\r\n" [string repeat v $qbsize]] ""]
- $rr flush
- wait_for_condition 200 10 {
- [client_field $cname qbuf] == $qbsize
- } else {
- fail "Failed to fill qbuf for test"
- }
- } e] && $no_evict == off} {
- assert {![client_exists $cname]}
- } elseif {$no_evict == on} {
- assert {[client_field $cname tot-mem] > $maxmemory_clients}
- }
- $rr close
- }
- }
-}
-
-start_server {} {
- set server_pid [s process_id]
- set maxmemory_clients [mb 10]
- set obuf_limit [mb 3]
- r config set maxmemory-clients $maxmemory_clients
- r config set client-output-buffer-limit "normal $obuf_limit 0 0"
- r debug reply-copy-avoidance 0 ;# Disable copy avoidance because it affects memory usage
-
- test "avoid client eviction when client is freed by output buffer limit" {
- r flushdb
- set obuf_size [expr {$obuf_limit + [mb 1]}]
- r setrange k $obuf_size v
- set rr1 [redis_client]
- $rr1 client setname "qbuf-client"
- set rr2 [redis_deferring_client]
- $rr2 client setname "obuf-client1"
- assert_equal [$rr2 read] OK
- set rr3 [redis_deferring_client]
- $rr3 client setname "obuf-client2"
- assert_equal [$rr3 read] OK
-
- # Occupy client's query buff with less than output buffer limit left to exceed maxmemory-clients
- set qbsize [expr {$maxmemory_clients - $obuf_size}]
- $rr1 write [join [list "*1\r\n\$$qbsize\r\n" [string repeat v $qbsize]] ""]
- $rr1 flush
- # Wait for qbuff to be as expected
- wait_for_condition 200 10 {
- [client_field qbuf-client qbuf] == $qbsize
- } else {
- fail "Failed to fill qbuf for test"
- }
-
- # Make the other two obuf-clients pass obuf limit and also pass maxmemory-clients
- # We use two obuf-clients to make sure that even if client eviction is attempted
- # between two command processing (with no sleep) we don't perform any client eviction
- # because the obuf limit is enforced with precedence.
- pause_process $server_pid
- $rr2 get k
- $rr2 flush
- $rr3 get k
- $rr3 flush
- resume_process $server_pid
- r ping ;# make sure a full event loop cycle is processed before issuing CLIENT LIST
-
- # wait for get commands to be processed
- wait_for_condition 100 10 {
- [expr {[regexp {calls=(\d+)} [cmdrstat get r] -> calls] ? $calls : 0}] >= 2
- } else {
- fail "get did not arrive"
- }
-
- # Validate obuf-clients were disconnected (because of obuf limit)
- catch {client_field obuf-client1 name} e
- assert_match {no client named obuf-client1 found*} $e
- catch {client_field obuf-client2 name} e
- assert_match {no client named obuf-client2 found*} $e
-
- # Validate qbuf-client is still connected and wasn't evicted
- if {[lindex [r config get io-threads] 1] == 1} {
- assert_equal [client_field qbuf-client name] {qbuf-client}
- }
-
- $rr1 close
- $rr2 close
- $rr3 close
- }
-}
-
-start_server {} {
- r debug reply-copy-avoidance 0 ;# Disable copy avoidance because it affects memory usage
-
- test "decrease maxmemory-clients causes client eviction" {
- set maxmemory_clients [mb 4]
- set client_count 10
- set qbsize [expr ($maxmemory_clients - [mb 1]) / $client_count]
- r config set maxmemory-clients $maxmemory_clients
-
-
- # Make multiple clients consume together roughly 1mb less than maxmemory_clients
- set rrs {}
- for {set j 0} {$j < $client_count} {incr j} {
- set rr [redis_client]
- lappend rrs $rr
- $rr client setname client$j
- $rr write [join [list "*2\r\n\$$qbsize\r\n" [string repeat v $qbsize]] ""]
- $rr flush
- wait_for_condition 200 10 {
- [client_field client$j qbuf] >= $qbsize
- } else {
- fail "Failed to fill qbuf for test"
- }
- }
-
- # Make sure all clients are still connected
- set connected_clients [llength [lsearch -all [split [string trim [r client list]] "\r\n"] *name=client*]]
- assert {$connected_clients == $client_count}
-
- # Decrease maxmemory_clients and expect client eviction
- r config set maxmemory-clients [expr $maxmemory_clients / 2]
- wait_for_condition 200 10 {
- [llength [regexp -all -inline {name=client} [r client list]]] < $client_count
- } else {
- fail "Failed to evict clients"
- }
-
- foreach rr $rrs {$rr close}
- }
-}
-
-start_server {} {
- r debug reply-copy-avoidance 0 ;# Disable copy avoidance because it affects memory usage
-
- test "evict clients only until below limit" {
- set client_count 10
- set client_mem [mb 1]
- r debug replybuffer resizing 0
- r config set maxmemory-clients 0
- r client setname control
- r client no-evict on
-
- # Make multiple clients consume together roughly 1mb less than maxmemory_clients
- set total_client_mem 0
- set max_client_mem 0
- set rrs {}
- for {set j 0} {$j < $client_count} {incr j} {
- set rr [redis_client]
- lappend rrs $rr
- $rr client setname client$j
- $rr write [join [list "*2\r\n\$$client_mem\r\n" [string repeat v $client_mem]] ""]
- $rr flush
- wait_for_condition 200 10 {
- [client_field client$j tot-mem] >= $client_mem
- } else {
- fail "Failed to fill qbuf for test"
- }
- # In theory all these clients should use the same amount of memory (~1mb). But in practice
- # some allocators (libc) can return different allocation sizes for the same malloc argument causing
- # some clients to use slightly more memory than others. We find the largest client and make sure
- # all clients are roughly the same size (+-1%). Then we can safely set the client eviction limit and
- # expect consistent results in the test.
- set cmem [client_field client$j tot-mem]
- if {$max_client_mem > 0} {
- set size_ratio [expr $max_client_mem.0/$cmem.0]
- assert_range $size_ratio 0.99 1.01
- }
- if {$cmem > $max_client_mem} {
- set max_client_mem $cmem
- }
- }
-
- # Make sure all clients are still connected
- set connected_clients [llength [lsearch -all [split [string trim [r client list]] "\r\n"] *name=client*]]
- assert {$connected_clients == $client_count}
-
- # Set maxmemory-clients to accommodate half our clients (taking into account the control client)
- set maxmemory_clients [expr ($max_client_mem * $client_count) / 2 + [client_field control tot-mem]]
- r config set maxmemory-clients $maxmemory_clients
-
- # Make sure total used memory is below maxmemory_clients
- set total_client_mem [clients_sum tot-mem]
- assert {$total_client_mem <= $maxmemory_clients}
-
- # Make sure we have only half of our clients now
- wait_for_condition 200 100 {
- ([lindex [r config get io-threads] 1] == 1) ?
- ([llength [regexp -all -inline {name=client} [r client list]]] == $client_count / 2) :
- ([llength [regexp -all -inline {name=client} [r client list]]] <= $client_count / 2)
- } else {
- fail "Failed to evict clients"
- }
-
- # Restore the reply buffer resize to default
- r debug replybuffer resizing 1
-
- foreach rr $rrs {$rr close}
- } {} {needs:debug}
-}
-
-start_server {} {
- r debug reply-copy-avoidance 0 ;# Disable copy avoidance because it affects memory usage
-
- test "evict clients in right order (large to small)" {
- # Note that each size step needs to be at least x2 larger than previous step
- # because of how the client-eviction size bucketing works
- set sizes [list [kb 128] [mb 1] [mb 3]]
- set clients_per_size 3
- r client setname control
- r client no-evict on
- r config set maxmemory-clients 0
- r debug replybuffer resizing 0
-
- # Run over all sizes and create some clients using up that size
- set total_client_mem 0
- set rrs {}
- for {set i 0} {$i < [llength $sizes]} {incr i} {
- set size [lindex $sizes $i]
-
- for {set j 0} {$j < $clients_per_size} {incr j} {
- set rr [redis_client]
- lappend rrs $rr
- $rr client setname client-$i
- $rr write [join [list "*2\r\n\$$size\r\n" [string repeat v $size]] ""]
- $rr flush
- }
- set client_mem [client_field client-$i tot-mem]
-
- # Update our size list based on actual used up size (this is usually
- # slightly more than expected because of allocator bins
- assert {$client_mem >= $size}
- set sizes [lreplace $sizes $i $i $client_mem]
-
- # Account total client memory usage
- incr total_mem [expr $clients_per_size * $client_mem]
- }
-
- # Make sure all clients are connected
- set clients [split [string trim [r client list]] "\r\n"]
- for {set i 0} {$i < [llength $sizes]} {incr i} {
- assert_equal [llength [lsearch -all $clients "*name=client-$i *"]] $clients_per_size
- }
-
- # For each size reduce maxmemory-clients so relevant clients should be evicted
- # do this from largest to smallest
- foreach size [lreverse $sizes] {
- set control_mem [client_field control tot-mem]
- set total_mem [expr $total_mem - $clients_per_size * $size]
- # allow some tolerance when using io threads
- r config set maxmemory-clients [expr $total_mem + $control_mem + 1000]
- set clients [split [string trim [r client list]] "\r\n"]
- # Verify only relevant clients were evicted
- for {set i 0} {$i < [llength $sizes]} {incr i} {
- set verify_size [lindex $sizes $i]
- set count [llength [lsearch -all $clients "*name=client-$i *"]]
- if {$verify_size < $size} {
- assert_equal $count $clients_per_size
- } else {
- assert_equal $count 0
- }
- }
- }
-
- # Restore the reply buffer resize to default
- r debug replybuffer resizing 1
-
- foreach rr $rrs {$rr close}
- } {} {needs:debug}
-}
-
-start_server {} {
- r debug reply-copy-avoidance 0 ;# Disable copy avoidance because it affects memory usage
-
- foreach type {"client no-evict" "maxmemory-clients disabled"} {
- r flushall
- r client no-evict on
- r config set maxmemory-clients 0
-
- test "client total memory grows during $type" {
- r setrange k [mb 1] v
- set rr [redis_client]
- $rr client setname test_client
- if {$type eq "client no-evict"} {
- $rr client no-evict on
- r config set maxmemory-clients 1
- }
- $rr deferred 1
-
- # Fill output buffer in loop without reading it and make sure
- # the tot-mem of client has increased (OS buffers didn't swallow it)
- # and eviction not occurring.
- while {true} {
- $rr get k
- $rr flush
- after 10
- if {[client_field test_client tot-mem] > [mb 10]} {
- break
- }
- }
-
- # Trigger the client eviction, by flipping the no-evict flag to off
- if {$type eq "client no-evict"} {
- $rr client no-evict off
- } else {
- r config set maxmemory-clients 1
- }
-
- # wait for the client to be disconnected
- wait_for_condition 5000 50 {
- ![client_exists test_client]
- } else {
- puts [r client list]
- fail "client was not disconnected"
- }
- $rr close
- }
- }
-}
-
-} ;# tags
-