summaryrefslogtreecommitdiffstats
path: root/tests/unit/moduleapi/blockedclient.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit/moduleapi/blockedclient.tcl')
-rw-r--r--tests/unit/moduleapi/blockedclient.tcl287
1 files changed, 287 insertions, 0 deletions
diff --git a/tests/unit/moduleapi/blockedclient.tcl b/tests/unit/moduleapi/blockedclient.tcl
new file mode 100644
index 0000000..9d475eb
--- /dev/null
+++ b/tests/unit/moduleapi/blockedclient.tcl
@@ -0,0 +1,287 @@
+set testmodule [file normalize tests/modules/blockedclient.so]
+
+start_server {tags {"modules"}} {
+ r module load $testmodule
+
+ test {Locked GIL acquisition} {
+ assert_match "OK" [r acquire_gil]
+ }
+
+ test {Locked GIL acquisition during multi} {
+ r multi
+ r acquire_gil
+ assert_equal {{Blocked client is not supported inside multi}} [r exec]
+ }
+
+ test {Locked GIL acquisition from RM_Call} {
+ assert_equal {Blocked client is not allowed} [r do_rm_call acquire_gil]
+ }
+
+ test {Blocking command are not block the client on RM_Call} {
+ r lpush l test
+ assert_equal [r do_rm_call blpop l 0] {l test}
+
+ r lpush l test
+ assert_equal [r do_rm_call brpop l 0] {l test}
+
+ r lpush l1 test
+ assert_equal [r do_rm_call brpoplpush l1 l2 0] {test}
+ assert_equal [r do_rm_call brpop l2 0] {l2 test}
+
+ r lpush l1 test
+ assert_equal [r do_rm_call blmove l1 l2 LEFT LEFT 0] {test}
+ assert_equal [r do_rm_call brpop l2 0] {l2 test}
+
+ r ZADD zset1 0 a 1 b 2 c
+ assert_equal [r do_rm_call bzpopmin zset1 0] {zset1 a 0}
+ assert_equal [r do_rm_call bzpopmax zset1 0] {zset1 c 2}
+
+ r xgroup create s g $ MKSTREAM
+ r xadd s * foo bar
+ assert {[r do_rm_call xread BLOCK 0 STREAMS s 0-0] ne {}}
+ assert {[r do_rm_call xreadgroup group g c BLOCK 0 STREAMS s >] ne {}}
+
+ assert {[r do_rm_call blpop empty_list 0] eq {}}
+ assert {[r do_rm_call brpop empty_list 0] eq {}}
+ assert {[r do_rm_call brpoplpush empty_list1 empty_list2 0] eq {}}
+ assert {[r do_rm_call blmove empty_list1 empty_list2 LEFT LEFT 0] eq {}}
+
+ assert {[r do_rm_call bzpopmin empty_zset 0] eq {}}
+ assert {[r do_rm_call bzpopmax empty_zset 0] eq {}}
+
+ r xgroup create empty_stream g $ MKSTREAM
+ assert {[r do_rm_call xread BLOCK 0 STREAMS empty_stream $] eq {}}
+ assert {[r do_rm_call xreadgroup group g c BLOCK 0 STREAMS empty_stream >] eq {}}
+
+ }
+
+ test {Monitor disallow inside RM_Call} {
+ set e {}
+ catch {
+ r do_rm_call monitor
+ } e
+ set e
+ } {*ERR*DENY BLOCKING*}
+
+ test {subscribe disallow inside RM_Call} {
+ set e {}
+ catch {
+ r do_rm_call subscribe x
+ } e
+ set e
+ } {*ERR*DENY BLOCKING*}
+
+ test {RM_Call from blocked client} {
+ r hset hash foo bar
+ r do_bg_rm_call hgetall hash
+ } {foo bar}
+
+ test {RM_Call from blocked client with script mode} {
+ r do_bg_rm_call_format S hset k foo bar
+ } {1}
+
+ test {RM_Call from blocked client with oom mode} {
+ r config set maxmemory 1
+ # will set server.pre_command_oom_state to 1
+ assert_error {OOM command not allowed*} {r hset hash foo bar}
+ r config set maxmemory 0
+ # now its should be OK to call OOM commands
+ r do_bg_rm_call_format M hset k1 foo bar
+ } {1} {needs:config-maxmemory}
+
+ test {RESP version carries through to blocked client} {
+ for {set client_proto 2} {$client_proto <= 3} {incr client_proto} {
+ if {[lsearch $::denytags "resp3"] >= 0} {
+ if {$client_proto == 3} {continue}
+ } elseif {$::force_resp3} {
+ if {$client_proto == 2} {continue}
+ }
+ r hello $client_proto
+ r readraw 1
+ set ret [r do_fake_bg_true]
+ if {$client_proto == 2} {
+ assert_equal $ret {:1}
+ } else {
+ assert_equal $ret "#t"
+ }
+ r readraw 0
+ r hello 2
+ }
+ }
+
+foreach call_type {nested normal} {
+ test "Busy module command - $call_type" {
+ set busy_time_limit 50
+ set old_time_limit [lindex [r config get busy-reply-threshold] 1]
+ r config set busy-reply-threshold $busy_time_limit
+ set rd [redis_deferring_client]
+
+ # run command that blocks until released
+ set start [clock clicks -milliseconds]
+ if {$call_type == "nested"} {
+ $rd do_rm_call slow_fg_command 0
+ } else {
+ $rd slow_fg_command 0
+ }
+ $rd flush
+
+ # send another command after the blocked one, to make sure we don't attempt to process it
+ $rd ping
+ $rd flush
+
+ # make sure we get BUSY error, and that we didn't get it too early
+ assert_error {*BUSY Slow module operation*} {r ping}
+ assert_morethan_equal [expr [clock clicks -milliseconds]-$start] $busy_time_limit
+
+ # abort the blocking operation
+ r stop_slow_fg_command
+ wait_for_condition 50 100 {
+ [catch {r ping} e] == 0
+ } else {
+ fail "Failed waiting for busy command to end"
+ }
+ assert_equal [$rd read] "1"
+ assert_equal [$rd read] "PONG"
+
+ # run command that blocks for 200ms
+ set start [clock clicks -milliseconds]
+ if {$call_type == "nested"} {
+ $rd do_rm_call slow_fg_command 200000
+ } else {
+ $rd slow_fg_command 200000
+ }
+ $rd flush
+ after 10 ;# try to make sure redis started running the command before we proceed
+
+ # make sure we didn't get BUSY error, it simply blocked till the command was done
+ r ping
+ assert_morethan_equal [expr [clock clicks -milliseconds]-$start] 200
+ $rd read
+
+ $rd close
+ r config set busy-reply-threshold $old_time_limit
+ }
+}
+
+ test {RM_Call from blocked client} {
+ set busy_time_limit 50
+ set old_time_limit [lindex [r config get busy-reply-threshold] 1]
+ r config set busy-reply-threshold $busy_time_limit
+
+ # trigger slow operation
+ r set_slow_bg_operation 1
+ r hset hash foo bar
+ set rd [redis_deferring_client]
+ set start [clock clicks -milliseconds]
+ $rd do_bg_rm_call hgetall hash
+
+ # send another command after the blocked one, to make sure we don't attempt to process it
+ $rd ping
+ $rd flush
+
+ # wait till we know we're blocked inside the module
+ wait_for_condition 50 100 {
+ [r is_in_slow_bg_operation] eq 1
+ } else {
+ fail "Failed waiting for slow operation to start"
+ }
+
+ # make sure we get BUSY error, and that we didn't get here too early
+ assert_error {*BUSY Slow module operation*} {r ping}
+ assert_morethan [expr [clock clicks -milliseconds]-$start] $busy_time_limit
+ # abort the blocking operation
+ r set_slow_bg_operation 0
+
+ wait_for_condition 50 100 {
+ [r is_in_slow_bg_operation] eq 0
+ } else {
+ fail "Failed waiting for slow operation to stop"
+ }
+ assert_equal [r ping] {PONG}
+
+ r config set busy-reply-threshold $old_time_limit
+ assert_equal [$rd read] {foo bar}
+ assert_equal [$rd read] {PONG}
+ $rd close
+ }
+
+ test {blocked client reaches client output buffer limit} {
+ r hset hash big [string repeat x 50000]
+ r hset hash bada [string repeat x 50000]
+ r hset hash boom [string repeat x 50000]
+ r config set client-output-buffer-limit {normal 100000 0 0}
+ r client setname myclient
+ catch {r do_bg_rm_call hgetall hash} e
+ assert_match "*I/O error*" $e
+ reconnect
+ set clients [r client list]
+ assert_no_match "*name=myclient*" $clients
+ }
+
+ test {module client error stats} {
+ r config resetstat
+
+ # simple module command that replies with string error
+ assert_error "ERR unknown command 'hgetalllll', with args beginning with:" {r do_rm_call hgetalllll}
+ assert_equal [errorrstat ERR r] {count=1}
+
+ # simple module command that replies with string error
+ assert_error "ERR unknown subcommand 'bla'. Try CONFIG HELP." {r do_rm_call config bla}
+ assert_equal [errorrstat ERR r] {count=2}
+
+ # module command that replies with string error from bg thread
+ assert_error "NULL reply returned" {r do_bg_rm_call hgetalllll}
+ assert_equal [errorrstat NULL r] {count=1}
+
+ # module command that returns an arity error
+ r do_rm_call set x x
+ assert_error "ERR wrong number of arguments for 'do_rm_call' command" {r do_rm_call}
+ assert_equal [errorrstat ERR r] {count=3}
+
+ # RM_Call that propagates an error
+ assert_error "WRONGTYPE*" {r do_rm_call hgetall x}
+ assert_equal [errorrstat WRONGTYPE r] {count=1}
+ assert_match {*calls=1,*,rejected_calls=0,failed_calls=1} [cmdrstat hgetall r]
+
+ # RM_Call from bg thread that propagates an error
+ assert_error "WRONGTYPE*" {r do_bg_rm_call hgetall x}
+ assert_equal [errorrstat WRONGTYPE r] {count=2}
+ assert_match {*calls=2,*,rejected_calls=0,failed_calls=2} [cmdrstat hgetall r]
+
+ assert_equal [s total_error_replies] 6
+ assert_match {*calls=5,*,rejected_calls=0,failed_calls=4} [cmdrstat do_rm_call r]
+ assert_match {*calls=2,*,rejected_calls=0,failed_calls=2} [cmdrstat do_bg_rm_call r]
+ }
+
+ set master [srv 0 client]
+ set master_host [srv 0 host]
+ set master_port [srv 0 port]
+ start_server [list overrides [list loadmodule "$testmodule"]] {
+ set replica [srv 0 client]
+ set replica_host [srv 0 host]
+ set replica_port [srv 0 port]
+
+ # Start the replication process...
+ $replica replicaof $master_host $master_port
+ wait_for_sync $replica
+
+ test {WAIT command on module blocked client} {
+ pause_process [srv 0 pid]
+
+ $master do_bg_rm_call_format ! hset bk1 foo bar
+
+ assert_equal [$master wait 1 1000] 0
+ resume_process [srv 0 pid]
+ assert_equal [$master wait 1 1000] 1
+ assert_equal [$replica hget bk1 foo] bar
+ }
+ }
+
+ test {Unblock by timer} {
+ assert_match "OK" [r unblock_by_timer 100]
+ }
+
+ test "Unload the module - blockedclient" {
+ assert_equal {OK} [r module unload blockedclient]
+ }
+}