summaryrefslogtreecommitdiff
path: root/tests/unit/type
diff options
context:
space:
mode:
authorsundb <sundbcn@gmail.com>2021-11-29 13:57:01 +0800
committerGitHub <noreply@github.com>2021-11-29 07:57:01 +0200
commit494ee2f1fc5cf1687b302a95e49003573dc375d5 (patch)
tree0b7869db90fd5d1d4662bd9913482e8fdb257bed /tests/unit/type
parent8759c1e14bafd026ddc8a097b3fbe2aa914b7578 (diff)
downloadredis-494ee2f1fc5cf1687b302a95e49003573dc375d5.tar.gz
Fix abnormal compression due to out-of-control recompress (#9849)
This pr is following #9779 . ## Describe of feature Now when we turn on the `list-compress-depth` configuration, the list will compress the ziplist between `[list-compress-depth, -list-compress-depth]`. When we need to use the compressed data, we will first decompress it, then use it, and finally compress it again. It's controlled by `quicklistNode->recompress`, which is designed to avoid the need to re-traverse the entire quicklist for compression after each decompression, we only need to recompress the quicklsitNode being used. In order to ensure the correctness of recompressing, we should normally let quicklistDecompressNodeForUse and quicklistCompress appear in pairs, otherwise, it may lead to the head and tail being compressed or the middle ziplist not being compressed correctly, which is exactly the problem this pr needs to solve. ## Solution 1. Reset `quicklistIter` after insert and replace. The quicklist node will be compressed in `quicklistInsertAfter`, `quicklistInsertBefore`, `quicklistReplaceAtIndex`, so we can safely reset the quicklistIter to avoid it being used again 2. `quicklistIndex` will return an iterator that can be used to recompress the current node after use. ## Test 1. In the `Stress Tester for #3343-Similar Errors` test, when the server crashes or when `valgrind` or `asan` error is detected, print violating commands. 2. Add a crash test due to wrongly recompressing after `lrem`. 3. Remove `insert before with 0 elements` and `insert after with 0 elements`, Now we forbid any operation on an NULL quicklistIter.
Diffstat (limited to 'tests/unit/type')
-rw-r--r--tests/unit/type/list-3.tcl117
1 files changed, 88 insertions, 29 deletions
diff --git a/tests/unit/type/list-3.tcl b/tests/unit/type/list-3.tcl
index 3f3ea6cfd..d5e4088c5 100644
--- a/tests/unit/type/list-3.tcl
+++ b/tests/unit/type/list-3.tcl
@@ -1,3 +1,39 @@
+proc generate_cmd_on_list_key {key} {
+ set op [randomInt 7]
+ set small_signed_count [expr 5-[randomInt 10]]
+ if {[randomInt 2] == 0} {
+ set ele [randomInt 1000]
+ } else {
+ set ele [string repeat x [randomInt 10000]][randomInt 1000]
+ }
+ switch $op {
+ 0 {return "lpush $key $ele"}
+ 1 {return "rpush $key $ele"}
+ 2 {return "lpop $key"}
+ 3 {return "rpop $key"}
+ 4 {
+ return "lset $key $small_signed_count $ele"
+ }
+ 5 {
+ set otherele [randomInt 1000]
+ if {[randomInt 2] == 0} {
+ set where before
+ } else {
+ set where after
+ }
+ return "linsert $key $where $otherele $ele"
+ }
+ 6 {
+ set otherele ""
+ catch {
+ set index [randomInt [r llen $key]]
+ set otherele [r lindex $key $index]
+ }
+ return "lrem $key 1 $otherele"
+ }
+ }
+}
+
start_server {
tags {"list ziplist"}
overrides {
@@ -38,7 +74,24 @@ start_server {
r rpush key [string repeat e 5000]
r linsert key before f 1
r rpush key g
- }
+ r ping
+ }
+
+ test {Crash due to wrongly recompress after lrem} {
+ r del key
+ config_set list-compress-depth 2
+ r lpush key a
+ r lpush key [string repeat a 5000]
+ r lpush key [string repeat b 5000]
+ r lpush key [string repeat c 5000]
+ r rpush key [string repeat x 10000]"969"
+ r rpush key b
+ r lrem key 1 a
+ r rpop key
+ r lrem key 1 [string repeat x 10000]"969"
+ r rpush key crash
+ r ping
+ }
foreach comp {2 1 0} {
set cycles 1000
@@ -47,38 +100,44 @@ foreach comp {2 1 0} {
test "Stress tester for #3343-alike bugs comp: $comp" {
r del key
+ set sent {}
for {set j 0} {$j < $cycles} {incr j} {
- set op [randomInt 7]
- set small_signed_count [expr 5-[randomInt 10]]
- if {[randomInt 2] == 0} {
- set ele [randomInt 1000]
- } else {
- set ele [string repeat x [randomInt 10000]][randomInt 1000]
+ catch {
+ set cmd [generate_cmd_on_list_key key]
+ lappend sent $cmd
+
+ # execute the command, we expect commands to fail on syntax errors
+ r {*}$cmd
}
- switch $op {
- 0 {r lpush key $ele}
- 1 {r rpush key $ele}
- 2 {r lpop key}
- 3 {r rpop key}
- 4 {
- catch {r lset key $small_signed_count $ele}
- }
- 5 {
- set otherele [randomInt 1000]
- if {[randomInt 2] == 0} {
- set where before
- } else {
- set where after
- }
- r linsert key $where $otherele $ele
- }
- 6 {
- set index [randomInt [r llen key]]
- set otherele [r lindex key $index]
- r lrem key 1 $otherele
- }
+ }
+
+ set print_commands false
+ set crash false
+ if {[catch {r ping}]} {
+ puts "Server crashed"
+ set print_commands true
+ set crash true
+ }
+
+ if {!$::external} {
+ # check valgrind and asan report for invalid reads after execute
+ # command so that we have a report that is easier to reproduce
+ set valgrind_errors [find_valgrind_errors [srv 0 stderr] false]
+ set asan_errors [sanitizer_errors_from_file [srv 0 stderr]]
+ if {$valgrind_errors != "" || $asan_errors != ""} {
+ puts "valgrind or asan found an issue"
+ set print_commands true
}
}
+
+ if {$print_commands} {
+ puts "violating commands:"
+ foreach cmd $sent {
+ puts $cmd
+ }
+ }
+
+ assert_equal $crash false
}
} ;# foreach comp