summaryrefslogtreecommitdiff
path: root/tests/unit/maxmemory.tcl
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2018-02-21 20:18:34 +0200
committerOran Agra <oran@redislabs.com>2018-07-16 16:43:42 +0300
commitbf680b6f8cdaee2c5588c5c8932a7f3b7fa70b15 (patch)
treebc0c6ef60ab8748cf1585406c4d27fb4e3083187 /tests/unit/maxmemory.tcl
parentab33bcd34640306cdf70fd4fda0af41d93c687bf (diff)
downloadredis-bf680b6f8cdaee2c5588c5c8932a7f3b7fa70b15.tar.gz
slave buffers were wasteful and incorrectly counted causing eviction
A) slave buffers didn't count internal fragmentation and sds unused space, this caused them to induce eviction although we didn't mean for it. B) slave buffers were consuming about twice the memory of what they actually needed. - this was mainly due to sdsMakeRoomFor growing to twice as much as needed each time but networking.c not storing more than 16k (partially fixed recently in 237a38737). - besides it wasn't able to store half of the new string into one buffer and the other half into the next (so the above mentioned fix helped mainly for small items). - lastly, the sds buffers had up to 30% internal fragmentation that was wasted, consumed but not used. C) inefficient performance due to starting from a small string and reallocing many times. what i changed: - creating dedicated buffers for reply list, counting their size with zmalloc_size - when creating a new reply node from, preallocate it to at least 16k. - when appending a new reply to the buffer, first fill all the unused space of the previous node before starting a new one. other changes: - expose mem_not_counted_for_evict info field for the benefit of the test suite - add a test to make sure slave buffers are counted correctly and that they don't cause eviction
Diffstat (limited to 'tests/unit/maxmemory.tcl')
-rw-r--r--tests/unit/maxmemory.tcl92
1 files changed, 92 insertions, 0 deletions
diff --git a/tests/unit/maxmemory.tcl b/tests/unit/maxmemory.tcl
index 0c3f6b32c..e3cd11114 100644
--- a/tests/unit/maxmemory.tcl
+++ b/tests/unit/maxmemory.tcl
@@ -142,3 +142,95 @@ start_server {tags {"maxmemory"}} {
}
}
}
+
+proc test_slave_buffers {cmd_count payload_len limit_memory pipeline} {
+ start_server {tags {"maxmemory"}} {
+ 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]
+
+ # add 100 keys of 100k (10MB total)
+ for {set j 0} {$j < 100} {incr j} {
+ $master setrange "key:$j" 100000 asdf
+ }
+
+ $master config set maxmemory-policy allkeys-random
+ $master config set client-output-buffer-limit "slave 100000000 100000000 60"
+ $master config set repl-backlog-size [expr {10*1024}]
+
+ $slave slaveof $master_host $master_port
+ wait_for_condition 50 100 {
+ [s 0 master_link_status] eq {up}
+ } else {
+ fail "Replication not started."
+ }
+
+ # measure used memory after the slave connected and set maxmemory
+ set orig_used [s -1 used_memory]
+ set orig_client_buf [s -1 mem_clients_normal]
+ set orig_mem_not_counted_for_evict [s -1 mem_not_counted_for_evict]
+ set orig_used_no_repl [expr {$orig_used - $orig_mem_not_counted_for_evict}]
+ set limit [expr {$orig_used - $orig_mem_not_counted_for_evict + 20*1024}]
+
+ if {$limit_memory==1} {
+ $master config set maxmemory $limit
+ }
+
+ # put the slave to sleep
+ set rd_slave [redis_deferring_client]
+ $rd_slave debug sleep 60
+
+ # send some 10mb woth of commands that don't increase the memory usage
+ if {$pipeline == 1} {
+ set rd_master [redis_deferring_client -1]
+ for {set k 0} {$k < $cmd_count} {incr k} {
+ $rd_master setrange key:0 0 [string repeat A $payload_len]
+ }
+ for {set k 0} {$k < $cmd_count} {incr k} {
+ #$rd_master read
+ }
+ } else {
+ for {set k 0} {$k < $cmd_count} {incr k} {
+ $master setrange key:0 0 [string repeat A $payload_len]
+ }
+ }
+
+ set new_used [s -1 used_memory]
+ set slave_buf [s -1 mem_clients_slaves]
+ set client_buf [s -1 mem_clients_normal]
+ set mem_not_counted_for_evict [s -1 mem_not_counted_for_evict]
+ set used_no_repl [expr {$new_used - $mem_not_counted_for_evict}]
+ set delta [expr {($used_no_repl - $client_buf) - ($orig_used_no_repl - $orig_client_buf)}]
+
+ assert {[$master dbsize] == 100}
+ assert {$slave_buf > 2*1024*1024} ;# some of the data may have been pushed to the OS buffers
+ assert {$delta < 50*1024 && $delta > -50*1024} ;# 1 byte unaccounted for, with 1M commands will consume some 1MB
+
+ $master client kill type slave
+ set killed_used [s -1 used_memory]
+ set killed_slave_buf [s -1 mem_clients_slaves]
+ set killed_mem_not_counted_for_evict [s -1 mem_not_counted_for_evict]
+ set killed_used_no_repl [expr {$killed_used - $killed_mem_not_counted_for_evict}]
+ set delta_no_repl [expr {$killed_used_no_repl - $used_no_repl}]
+ assert {$killed_slave_buf == 0}
+ assert {$delta_no_repl > -50*1024 && $delta_no_repl < 50*1024} ;# 1 byte unaccounted for, with 1M commands will consume some 1MB
+ }
+ }
+}
+
+test {slave buffer are counted correctly} {
+ # we wanna use many small commands, and we don't wanna wait long
+ # so we need to use a pipeline (redis_deferring_client)
+ # that may cause query buffer to fill and induce eviction, so we disable it
+ test_slave_buffers 1000000 10 0 1
+}
+
+test {slave buffer don't induce eviction} {
+ # test again with fewer (and bigger) commands without pipeline, but with eviction
+ test_slave_buffers 100000 100 1 0
+}
+