obuf-limits.tcl 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. start_server {tags {"obuf-limits external:skip"}} {
  2. test {CONFIG SET client-output-buffer-limit} {
  3. set oldval [lindex [r config get client-output-buffer-limit] 1]
  4. catch {r config set client-output-buffer-limit "wrong number"} e
  5. assert_match {*Wrong*arguments*} $e
  6. catch {r config set client-output-buffer-limit "invalid_class 10mb 10mb 60"} e
  7. assert_match {*Invalid*client*class*} $e
  8. catch {r config set client-output-buffer-limit "master 10mb 10mb 60"} e
  9. assert_match {*Invalid*client*class*} $e
  10. catch {r config set client-output-buffer-limit "normal 10mbs 10mb 60"} e
  11. assert_match {*Error*hard*} $e
  12. catch {r config set client-output-buffer-limit "replica 10mb 10mbs 60"} e
  13. assert_match {*Error*soft*} $e
  14. catch {r config set client-output-buffer-limit "pubsub 10mb 10mb 60s"} e
  15. assert_match {*Error*soft_seconds*} $e
  16. r config set client-output-buffer-limit "normal 1mb 2mb 60 replica 3mb 4mb 70 pubsub 5mb 6mb 80"
  17. set res [lindex [r config get client-output-buffer-limit] 1]
  18. assert_equal $res "normal 1048576 2097152 60 slave 3145728 4194304 70 pubsub 5242880 6291456 80"
  19. # Set back to the original value.
  20. r config set client-output-buffer-limit $oldval
  21. }
  22. test {Client output buffer hard limit is enforced} {
  23. r config set client-output-buffer-limit {pubsub 100000 0 0}
  24. set rd1 [redis_deferring_client]
  25. $rd1 subscribe foo
  26. set reply [$rd1 read]
  27. assert {$reply eq "subscribe foo 1"}
  28. set omem 0
  29. while 1 {
  30. r publish foo bar
  31. set clients [split [r client list] "\r\n"]
  32. set c [split [lindex $clients 1] " "]
  33. if {![regexp {omem=([0-9]+)} $c - omem]} break
  34. if {$omem > 200000} break
  35. }
  36. assert {$omem >= 70000 && $omem < 200000}
  37. $rd1 close
  38. }
  39. foreach {soft_limit_time wait_for_timeout} {3 yes
  40. 4 no } {
  41. if $wait_for_timeout {
  42. set test_name "Client output buffer soft limit is enforced if time is overreached"
  43. } else {
  44. set test_name "Client output buffer soft limit is not enforced too early and is enforced when no traffic"
  45. }
  46. test $test_name {
  47. r config set client-output-buffer-limit "pubsub 0 100000 $soft_limit_time"
  48. set soft_limit_time [expr $soft_limit_time*1000]
  49. set rd1 [redis_deferring_client]
  50. $rd1 client setname test_client
  51. set reply [$rd1 read]
  52. assert {$reply eq "OK"}
  53. $rd1 subscribe foo
  54. set reply [$rd1 read]
  55. assert {$reply eq "subscribe foo 1"}
  56. set omem 0
  57. set start_time 0
  58. set time_elapsed 0
  59. set last_under_limit_time [clock milliseconds]
  60. while 1 {
  61. r publish foo [string repeat "x" 1000]
  62. set clients [split [r client list] "\r\n"]
  63. set c [lsearch -inline $clients *name=test_client*]
  64. if {$start_time != 0} {
  65. set time_elapsed [expr {[clock milliseconds]-$start_time}]
  66. # Make sure test isn't taking too long
  67. assert {$time_elapsed <= [expr $soft_limit_time+3000]}
  68. }
  69. if {$wait_for_timeout && $c == ""} {
  70. # Make sure we're disconnected when we reach the soft limit
  71. assert {$omem >= 100000 && $time_elapsed >= $soft_limit_time}
  72. break
  73. } else {
  74. assert {[regexp {omem=([0-9]+)} $c - omem]}
  75. }
  76. if {$omem > 100000} {
  77. if {$start_time == 0} {set start_time $last_under_limit_time}
  78. if {!$wait_for_timeout && $time_elapsed >= [expr $soft_limit_time-1000]} break
  79. # Slow down loop when omem has reached the limit.
  80. after 10
  81. } else {
  82. # if the OS socket buffers swallowed what we previously filled, reset the start timer.
  83. set start_time 0
  84. set last_under_limit_time [clock milliseconds]
  85. }
  86. }
  87. if {!$wait_for_timeout} {
  88. # After we completely stopped the traffic, wait for soft limit to time out
  89. set timeout [expr {$soft_limit_time+1500 - ([clock milliseconds]-$start_time)}]
  90. wait_for_condition [expr $timeout/10] 10 {
  91. [lsearch [split [r client list] "\r\n"] *name=test_client*] == -1
  92. } else {
  93. fail "Soft limit timed out but client still connected"
  94. }
  95. }
  96. $rd1 close
  97. }
  98. }
  99. test {No response for single command if client output buffer hard limit is enforced} {
  100. r config set client-output-buffer-limit {normal 100000 0 0}
  101. # Total size of all items must be more than 100k
  102. set item [string repeat "x" 1000]
  103. for {set i 0} {$i < 150} {incr i} {
  104. r lpush mylist $item
  105. }
  106. set orig_mem [s used_memory]
  107. # Set client name and get all items
  108. set rd [redis_deferring_client]
  109. $rd client setname mybiglist
  110. assert {[$rd read] eq "OK"}
  111. $rd lrange mylist 0 -1
  112. $rd flush
  113. after 100
  114. # Before we read reply, redis will close this client.
  115. set clients [r client list]
  116. assert_no_match "*name=mybiglist*" $clients
  117. set cur_mem [s used_memory]
  118. # 10k just is a deviation threshold
  119. assert {$cur_mem < 10000 + $orig_mem}
  120. # Read nothing
  121. set fd [$rd channel]
  122. assert_equal {} [read $fd]
  123. }
  124. # Note: This test assumes that what's written with one write, will be read by redis in one read.
  125. # this assumption is wrong, but seem to work empirically (for now)
  126. test {No response for multi commands in pipeline if client output buffer limit is enforced} {
  127. r config set client-output-buffer-limit {normal 100000 0 0}
  128. set value [string repeat "x" 10000]
  129. r set bigkey $value
  130. set rd1 [redis_deferring_client]
  131. set rd2 [redis_deferring_client]
  132. $rd2 client setname multicommands
  133. assert_equal "OK" [$rd2 read]
  134. # Let redis sleep 1s firstly
  135. $rd1 debug sleep 1
  136. $rd1 flush
  137. after 100
  138. # Create a pipeline of commands that will be processed in one socket read.
  139. # It is important to use one write, in TLS mode independent writes seem
  140. # to wait for response from the server.
  141. # Total size should be less than OS socket buffer, redis can
  142. # execute all commands in this pipeline when it wakes up.
  143. set buf ""
  144. for {set i 0} {$i < 15} {incr i} {
  145. append buf "set $i $i\r\n"
  146. append buf "get $i\r\n"
  147. append buf "del $i\r\n"
  148. # One bigkey is 10k, total response size must be more than 100k
  149. append buf "get bigkey\r\n"
  150. }
  151. $rd2 write $buf
  152. $rd2 flush
  153. after 100
  154. # Reds must wake up if it can send reply
  155. assert_equal "PONG" [r ping]
  156. set clients [r client list]
  157. assert_no_match "*name=multicommands*" $clients
  158. set fd [$rd2 channel]
  159. assert_equal {} [read $fd]
  160. }
  161. test {Execute transactions completely even if client output buffer limit is enforced} {
  162. r config set client-output-buffer-limit {normal 100000 0 0}
  163. # Total size of all items must be more than 100k
  164. set item [string repeat "x" 1000]
  165. for {set i 0} {$i < 150} {incr i} {
  166. r lpush mylist2 $item
  167. }
  168. # Output buffer limit is enforced during executing transaction
  169. r client setname transactionclient
  170. r set k1 v1
  171. r multi
  172. r set k2 v2
  173. r get k2
  174. r lrange mylist2 0 -1
  175. r set k3 v3
  176. r del k1
  177. catch {[r exec]} e
  178. assert_match "*I/O error*" $e
  179. reconnect
  180. set clients [r client list]
  181. assert_no_match "*name=transactionclient*" $clients
  182. # Transactions should be executed completely
  183. assert_equal {} [r get k1]
  184. assert_equal "v2" [r get k2]
  185. assert_equal "v3" [r get k3]
  186. }
  187. }