2024-03-21 09:30:47 -04:00
|
|
|
# SPDX-FileCopyrightText: 2024 Redict Contributors
|
|
|
|
# SPDX-FileCopyrightText: 2024 Salvatore Sanfilippo <antirez at gmail dot com>
|
|
|
|
#
|
|
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
2024-03-21 15:11:44 -04:00
|
|
|
# SPDX-License-Identifier: LGPL-3.0-only
|
2024-03-21 09:30:47 -04:00
|
|
|
|
2010-08-21 04:55:53 -04:00
|
|
|
start_server {
|
|
|
|
tags {"sort"}
|
|
|
|
overrides {
|
Add listpack encoding for list (#11303)
Improve memory efficiency of list keys
## Description of the feature
The new listpack encoding uses the old `list-max-listpack-size` config
to perform the conversion, which we can think it of as a node inside a
quicklist, but without 80 bytes overhead (internal fragmentation included)
of quicklist and quicklistNode structs.
For example, a list key with 5 items of 10 chars each, now takes 128 bytes
instead of 208 it used to take.
## Conversion rules
* Convert listpack to quicklist
When the listpack length or size reaches the `list-max-listpack-size` limit,
it will be converted to a quicklist.
* Convert quicklist to listpack
When a quicklist has only one node, and its length or size is reduced to half
of the `list-max-listpack-size` limit, it will be converted to a listpack.
This is done to avoid frequent conversions when we add or remove at the bounding size or length.
## Interface changes
1. add list entry param to listTypeSetIteratorDirection
When list encoding is listpack, `listTypeIterator->lpi` points to the next entry of current entry,
so when changing the direction, we need to use the current node (listTypeEntry->p) to
update `listTypeIterator->lpi` to the next node in the reverse direction.
## Benchmark
### Listpack VS Quicklist with one node
* LPUSH - roughly 0.3% improvement
* LRANGE - roughly 13% improvement
### Both are quicklist
* LRANGE - roughly 3% improvement
* LRANGE without pipeline - roughly 3% improvement
From the benchmark, as we can see from the results
1. When list is quicklist encoding, LRANGE improves performance by <5%.
2. When list is listpack encoding, LRANGE improves performance by ~13%,
the main enhancement is brought by `addListListpackRangeReply()`.
## Memory usage
1M lists(key:0~key:1000000) with 5 items of 10 chars ("hellohello") each.
shows memory usage down by 35.49%, from 214MB to 138MB.
## Note
1. Add conversion callback to support doing some work before conversion
Since the quicklist iterator decompresses the current node when it is released, we can
no longer decompress the quicklist after we convert the list.
2022-11-16 13:29:46 -05:00
|
|
|
"list-max-ziplist-size" 16
|
2010-08-21 05:03:56 -04:00
|
|
|
"set-max-intset-entries" 32
|
2010-08-21 04:55:53 -04:00
|
|
|
}
|
|
|
|
} {
|
|
|
|
proc create_random_dataset {num cmd} {
|
|
|
|
set tosort {}
|
|
|
|
set result {}
|
|
|
|
array set seenrand {}
|
|
|
|
r del tosort
|
|
|
|
for {set i 0} {$i < $num} {incr i} {
|
|
|
|
# Make sure all the weights are different because
|
2024-03-21 05:56:59 -04:00
|
|
|
# Redict does not use a stable sort but Tcl does.
|
2010-08-21 04:55:53 -04:00
|
|
|
while 1 {
|
|
|
|
randpath {
|
|
|
|
set rint [expr int(rand()*1000000)]
|
|
|
|
} {
|
|
|
|
set rint [expr rand()]
|
|
|
|
}
|
|
|
|
if {![info exists seenrand($rint)]} break
|
|
|
|
}
|
|
|
|
set seenrand($rint) x
|
|
|
|
r $cmd tosort $i
|
|
|
|
r set weight_$i $rint
|
|
|
|
r hset wobj_$i weight $rint
|
|
|
|
lappend tosort [list $i $rint]
|
|
|
|
}
|
|
|
|
set sorted [lsort -index 1 -real $tosort]
|
|
|
|
for {set i 0} {$i < $num} {incr i} {
|
|
|
|
lappend result [lindex $sorted $i 0]
|
|
|
|
}
|
|
|
|
set _ $result
|
|
|
|
}
|
|
|
|
|
Add listpack encoding for list (#11303)
Improve memory efficiency of list keys
## Description of the feature
The new listpack encoding uses the old `list-max-listpack-size` config
to perform the conversion, which we can think it of as a node inside a
quicklist, but without 80 bytes overhead (internal fragmentation included)
of quicklist and quicklistNode structs.
For example, a list key with 5 items of 10 chars each, now takes 128 bytes
instead of 208 it used to take.
## Conversion rules
* Convert listpack to quicklist
When the listpack length or size reaches the `list-max-listpack-size` limit,
it will be converted to a quicklist.
* Convert quicklist to listpack
When a quicklist has only one node, and its length or size is reduced to half
of the `list-max-listpack-size` limit, it will be converted to a listpack.
This is done to avoid frequent conversions when we add or remove at the bounding size or length.
## Interface changes
1. add list entry param to listTypeSetIteratorDirection
When list encoding is listpack, `listTypeIterator->lpi` points to the next entry of current entry,
so when changing the direction, we need to use the current node (listTypeEntry->p) to
update `listTypeIterator->lpi` to the next node in the reverse direction.
## Benchmark
### Listpack VS Quicklist with one node
* LPUSH - roughly 0.3% improvement
* LRANGE - roughly 13% improvement
### Both are quicklist
* LRANGE - roughly 3% improvement
* LRANGE without pipeline - roughly 3% improvement
From the benchmark, as we can see from the results
1. When list is quicklist encoding, LRANGE improves performance by <5%.
2. When list is listpack encoding, LRANGE improves performance by ~13%,
the main enhancement is brought by `addListListpackRangeReply()`.
## Memory usage
1M lists(key:0~key:1000000) with 5 items of 10 chars ("hellohello") each.
shows memory usage down by 35.49%, from 214MB to 138MB.
## Note
1. Add conversion callback to support doing some work before conversion
Since the quicklist iterator decompresses the current node when it is released, we can
no longer decompress the quicklist after we convert the list.
2022-11-16 13:29:46 -05:00
|
|
|
proc check_sort_store_encoding {key} {
|
|
|
|
set listpack_max_size [lindex [r config get list-max-ziplist-size] 1]
|
|
|
|
|
|
|
|
# When the length or size of quicklist is less than the limit,
|
|
|
|
# it will be converted to listpack.
|
|
|
|
if {[r llen $key] <= $listpack_max_size} {
|
|
|
|
assert_encoding listpack $key
|
|
|
|
} else {
|
|
|
|
assert_encoding quicklist $key
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-21 05:02:22 -04:00
|
|
|
foreach {num cmd enc title} {
|
Add listpack encoding for list (#11303)
Improve memory efficiency of list keys
## Description of the feature
The new listpack encoding uses the old `list-max-listpack-size` config
to perform the conversion, which we can think it of as a node inside a
quicklist, but without 80 bytes overhead (internal fragmentation included)
of quicklist and quicklistNode structs.
For example, a list key with 5 items of 10 chars each, now takes 128 bytes
instead of 208 it used to take.
## Conversion rules
* Convert listpack to quicklist
When the listpack length or size reaches the `list-max-listpack-size` limit,
it will be converted to a quicklist.
* Convert quicklist to listpack
When a quicklist has only one node, and its length or size is reduced to half
of the `list-max-listpack-size` limit, it will be converted to a listpack.
This is done to avoid frequent conversions when we add or remove at the bounding size or length.
## Interface changes
1. add list entry param to listTypeSetIteratorDirection
When list encoding is listpack, `listTypeIterator->lpi` points to the next entry of current entry,
so when changing the direction, we need to use the current node (listTypeEntry->p) to
update `listTypeIterator->lpi` to the next node in the reverse direction.
## Benchmark
### Listpack VS Quicklist with one node
* LPUSH - roughly 0.3% improvement
* LRANGE - roughly 13% improvement
### Both are quicklist
* LRANGE - roughly 3% improvement
* LRANGE without pipeline - roughly 3% improvement
From the benchmark, as we can see from the results
1. When list is quicklist encoding, LRANGE improves performance by <5%.
2. When list is listpack encoding, LRANGE improves performance by ~13%,
the main enhancement is brought by `addListListpackRangeReply()`.
## Memory usage
1M lists(key:0~key:1000000) with 5 items of 10 chars ("hellohello") each.
shows memory usage down by 35.49%, from 214MB to 138MB.
## Note
1. Add conversion callback to support doing some work before conversion
Since the quicklist iterator decompresses the current node when it is released, we can
no longer decompress the quicklist after we convert the list.
2022-11-16 13:29:46 -05:00
|
|
|
16 lpush listpack "Listpack"
|
|
|
|
1000 lpush quicklist "Quicklist"
|
|
|
|
10000 lpush quicklist "Big Quicklist"
|
2010-08-21 05:15:31 -04:00
|
|
|
16 sadd intset "Intset"
|
2010-08-26 11:29:13 -04:00
|
|
|
1000 sadd hashtable "Hash table"
|
|
|
|
10000 sadd hashtable "Big Hash table"
|
2010-08-21 05:02:22 -04:00
|
|
|
} {
|
|
|
|
set result [create_random_dataset $num $cmd]
|
|
|
|
assert_encoding $enc tosort
|
|
|
|
|
|
|
|
test "$title: SORT BY key" {
|
2010-10-15 11:25:20 -04:00
|
|
|
assert_equal $result [r sort tosort BY weight_*]
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2010-08-21 04:55:53 -04:00
|
|
|
|
2011-12-19 06:45:57 -05:00
|
|
|
test "$title: SORT BY key with limit" {
|
|
|
|
assert_equal [lrange $result 5 9] [r sort tosort BY weight_* LIMIT 5 5]
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2011-12-19 06:45:57 -05:00
|
|
|
|
2010-08-21 05:02:22 -04:00
|
|
|
test "$title: SORT BY hash field" {
|
2010-10-15 11:25:20 -04:00
|
|
|
assert_equal $result [r sort tosort BY wobj_*->weight]
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2010-08-21 04:55:53 -04:00
|
|
|
}
|
|
|
|
|
2010-08-21 05:02:22 -04:00
|
|
|
set result [create_random_dataset 16 lpush]
|
2010-08-21 04:55:53 -04:00
|
|
|
test "SORT GET #" {
|
|
|
|
assert_equal [lsort -integer $result] [r sort tosort GET #]
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2010-08-21 04:55:53 -04:00
|
|
|
|
2023-08-30 23:36:35 -04:00
|
|
|
foreach command {SORT SORT_RO} {
|
|
|
|
test "$command GET <const>" {
|
2010-08-21 04:55:53 -04:00
|
|
|
r del foo
|
2023-08-30 23:36:35 -04:00
|
|
|
set res [r $command tosort GET foo]
|
2010-08-21 04:55:53 -04:00
|
|
|
assert_equal 16 [llength $res]
|
|
|
|
foreach item $res { assert_equal {} $item }
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2023-08-30 23:36:35 -04:00
|
|
|
}
|
2010-08-21 04:55:53 -04:00
|
|
|
|
|
|
|
test "SORT GET (key and hash) with sanity check" {
|
|
|
|
set l1 [r sort tosort GET # GET weight_*]
|
|
|
|
set l2 [r sort tosort GET # GET wobj_*->weight]
|
|
|
|
foreach {id1 w1} $l1 {id2 w2} $l2 {
|
|
|
|
assert_equal $id1 $id2
|
|
|
|
assert_equal $w1 [r get weight_$id1]
|
|
|
|
assert_equal $w2 [r get weight_$id1]
|
|
|
|
}
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2010-08-21 04:55:53 -04:00
|
|
|
|
|
|
|
test "SORT BY key STORE" {
|
2010-10-15 11:25:20 -04:00
|
|
|
r sort tosort BY weight_* store sort-res
|
2010-08-21 04:55:53 -04:00
|
|
|
assert_equal $result [r lrange sort-res 0 -1]
|
|
|
|
assert_equal 16 [r llen sort-res]
|
Add listpack encoding for list (#11303)
Improve memory efficiency of list keys
## Description of the feature
The new listpack encoding uses the old `list-max-listpack-size` config
to perform the conversion, which we can think it of as a node inside a
quicklist, but without 80 bytes overhead (internal fragmentation included)
of quicklist and quicklistNode structs.
For example, a list key with 5 items of 10 chars each, now takes 128 bytes
instead of 208 it used to take.
## Conversion rules
* Convert listpack to quicklist
When the listpack length or size reaches the `list-max-listpack-size` limit,
it will be converted to a quicklist.
* Convert quicklist to listpack
When a quicklist has only one node, and its length or size is reduced to half
of the `list-max-listpack-size` limit, it will be converted to a listpack.
This is done to avoid frequent conversions when we add or remove at the bounding size or length.
## Interface changes
1. add list entry param to listTypeSetIteratorDirection
When list encoding is listpack, `listTypeIterator->lpi` points to the next entry of current entry,
so when changing the direction, we need to use the current node (listTypeEntry->p) to
update `listTypeIterator->lpi` to the next node in the reverse direction.
## Benchmark
### Listpack VS Quicklist with one node
* LPUSH - roughly 0.3% improvement
* LRANGE - roughly 13% improvement
### Both are quicklist
* LRANGE - roughly 3% improvement
* LRANGE without pipeline - roughly 3% improvement
From the benchmark, as we can see from the results
1. When list is quicklist encoding, LRANGE improves performance by <5%.
2. When list is listpack encoding, LRANGE improves performance by ~13%,
the main enhancement is brought by `addListListpackRangeReply()`.
## Memory usage
1M lists(key:0~key:1000000) with 5 items of 10 chars ("hellohello") each.
shows memory usage down by 35.49%, from 214MB to 138MB.
## Note
1. Add conversion callback to support doing some work before conversion
Since the quicklist iterator decompresses the current node when it is released, we can
no longer decompress the quicklist after we convert the list.
2022-11-16 13:29:46 -05:00
|
|
|
check_sort_store_encoding sort-res
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2010-08-21 04:55:53 -04:00
|
|
|
|
|
|
|
test "SORT BY hash field STORE" {
|
2010-10-15 11:25:20 -04:00
|
|
|
r sort tosort BY wobj_*->weight store sort-res
|
2010-08-21 04:55:53 -04:00
|
|
|
assert_equal $result [r lrange sort-res 0 -1]
|
|
|
|
assert_equal 16 [r llen sort-res]
|
Add listpack encoding for list (#11303)
Improve memory efficiency of list keys
## Description of the feature
The new listpack encoding uses the old `list-max-listpack-size` config
to perform the conversion, which we can think it of as a node inside a
quicklist, but without 80 bytes overhead (internal fragmentation included)
of quicklist and quicklistNode structs.
For example, a list key with 5 items of 10 chars each, now takes 128 bytes
instead of 208 it used to take.
## Conversion rules
* Convert listpack to quicklist
When the listpack length or size reaches the `list-max-listpack-size` limit,
it will be converted to a quicklist.
* Convert quicklist to listpack
When a quicklist has only one node, and its length or size is reduced to half
of the `list-max-listpack-size` limit, it will be converted to a listpack.
This is done to avoid frequent conversions when we add or remove at the bounding size or length.
## Interface changes
1. add list entry param to listTypeSetIteratorDirection
When list encoding is listpack, `listTypeIterator->lpi` points to the next entry of current entry,
so when changing the direction, we need to use the current node (listTypeEntry->p) to
update `listTypeIterator->lpi` to the next node in the reverse direction.
## Benchmark
### Listpack VS Quicklist with one node
* LPUSH - roughly 0.3% improvement
* LRANGE - roughly 13% improvement
### Both are quicklist
* LRANGE - roughly 3% improvement
* LRANGE without pipeline - roughly 3% improvement
From the benchmark, as we can see from the results
1. When list is quicklist encoding, LRANGE improves performance by <5%.
2. When list is listpack encoding, LRANGE improves performance by ~13%,
the main enhancement is brought by `addListListpackRangeReply()`.
## Memory usage
1M lists(key:0~key:1000000) with 5 items of 10 chars ("hellohello") each.
shows memory usage down by 35.49%, from 214MB to 138MB.
## Note
1. Add conversion callback to support doing some work before conversion
Since the quicklist iterator decompresses the current node when it is released, we can
no longer decompress the quicklist after we convert the list.
2022-11-16 13:29:46 -05:00
|
|
|
check_sort_store_encoding sort-res
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2010-08-21 04:55:53 -04:00
|
|
|
|
2014-07-21 17:31:21 -04:00
|
|
|
test "SORT extracts STORE correctly" {
|
|
|
|
r command getkeys sort abc store def
|
|
|
|
} {abc def}
|
2023-08-30 15:00:02 -04:00
|
|
|
|
|
|
|
test "SORT_RO get keys" {
|
|
|
|
r command getkeys sort_ro abc
|
|
|
|
} {abc}
|
2014-07-21 17:31:21 -04:00
|
|
|
|
|
|
|
test "SORT extracts multiple STORE correctly" {
|
|
|
|
r command getkeys sort abc store invalid store stillbad store def
|
|
|
|
} {abc def}
|
|
|
|
|
2010-08-21 04:55:53 -04:00
|
|
|
test "SORT DESC" {
|
2010-10-15 11:25:20 -04:00
|
|
|
assert_equal [lsort -decreasing -integer $result] [r sort tosort DESC]
|
2010-08-21 04:55:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
test "SORT ALPHA against integer encoded strings" {
|
2010-05-14 11:31:11 -04:00
|
|
|
r del mylist
|
|
|
|
r lpush mylist 2
|
|
|
|
r lpush mylist 1
|
|
|
|
r lpush mylist 3
|
|
|
|
r lpush mylist 10
|
|
|
|
r sort mylist alpha
|
|
|
|
} {1 10 2 3}
|
|
|
|
|
2010-08-21 04:55:53 -04:00
|
|
|
test "SORT sorted set" {
|
|
|
|
r del zset
|
|
|
|
r zadd zset 1 a
|
|
|
|
r zadd zset 5 b
|
|
|
|
r zadd zset 2 c
|
|
|
|
r zadd zset 10 d
|
|
|
|
r zadd zset 3 e
|
|
|
|
r sort zset alpha desc
|
|
|
|
} {e d c b a}
|
2010-05-14 11:31:11 -04:00
|
|
|
|
2012-10-03 05:41:08 -04:00
|
|
|
test "SORT sorted set BY nosort should retain ordering" {
|
|
|
|
r del zset
|
|
|
|
r zadd zset 1 a
|
|
|
|
r zadd zset 5 b
|
|
|
|
r zadd zset 2 c
|
|
|
|
r zadd zset 10 d
|
|
|
|
r zadd zset 3 e
|
|
|
|
r multi
|
|
|
|
r sort zset by nosort asc
|
|
|
|
r sort zset by nosort desc
|
|
|
|
r exec
|
|
|
|
} {{a c e b d} {d b e c a}}
|
|
|
|
|
|
|
|
test "SORT sorted set BY nosort + LIMIT" {
|
|
|
|
r del zset
|
|
|
|
r zadd zset 1 a
|
|
|
|
r zadd zset 5 b
|
|
|
|
r zadd zset 2 c
|
|
|
|
r zadd zset 10 d
|
|
|
|
r zadd zset 3 e
|
|
|
|
assert_equal [r sort zset by nosort asc limit 0 1] {a}
|
|
|
|
assert_equal [r sort zset by nosort desc limit 0 1] {d}
|
|
|
|
assert_equal [r sort zset by nosort asc limit 0 2] {a c}
|
|
|
|
assert_equal [r sort zset by nosort desc limit 0 2] {d b}
|
|
|
|
assert_equal [r sort zset by nosort limit 5 10] {}
|
|
|
|
assert_equal [r sort zset by nosort limit -10 100] {a c e b d}
|
|
|
|
}
|
|
|
|
|
|
|
|
test "SORT sorted set BY nosort works as expected from scripts" {
|
|
|
|
r del zset
|
|
|
|
r zadd zset 1 a
|
|
|
|
r zadd zset 5 b
|
|
|
|
r zadd zset 2 c
|
|
|
|
r zadd zset 10 d
|
|
|
|
r zadd zset 3 e
|
|
|
|
r eval {
|
2014-04-06 10:20:01 -04:00
|
|
|
return {redis.call('sort',KEYS[1],'by','nosort','asc'),
|
|
|
|
redis.call('sort',KEYS[1],'by','nosort','desc')}
|
|
|
|
} 1 zset
|
2012-10-03 05:41:08 -04:00
|
|
|
} {{a c e b d} {d b e c a}}
|
|
|
|
|
2010-08-21 04:55:53 -04:00
|
|
|
test "SORT sorted set: +inf and -inf handling" {
|
|
|
|
r del zset
|
|
|
|
r zadd zset -100 a
|
|
|
|
r zadd zset 200 b
|
|
|
|
r zadd zset -300 c
|
|
|
|
r zadd zset 1000000 d
|
|
|
|
r zadd zset +inf max
|
|
|
|
r zadd zset -inf min
|
|
|
|
r zrange zset 0 -1
|
|
|
|
} {min c a b d max}
|
2010-05-14 11:31:11 -04:00
|
|
|
|
2010-08-21 04:55:53 -04:00
|
|
|
test "SORT regression for issue #19, sorting floats" {
|
|
|
|
r flushdb
|
|
|
|
set floats {1.1 5.10 3.10 7.44 2.1 5.75 6.12 0.25 1.15}
|
|
|
|
foreach x $floats {
|
|
|
|
r lpush mylist $x
|
|
|
|
}
|
|
|
|
assert_equal [lsort -real $floats] [r sort mylist]
|
|
|
|
}
|
2010-05-14 11:31:11 -04:00
|
|
|
|
2014-07-31 14:33:50 -04:00
|
|
|
test "SORT with STORE returns zero if result is empty (github issue 224)" {
|
2011-12-01 10:07:55 -05:00
|
|
|
r flushdb
|
2021-06-09 08:13:24 -04:00
|
|
|
r sort foo{t} store bar{t}
|
2011-12-01 10:07:55 -05:00
|
|
|
} {0}
|
|
|
|
|
|
|
|
test "SORT with STORE does not create empty lists (github issue 224)" {
|
|
|
|
r flushdb
|
2021-06-09 08:13:24 -04:00
|
|
|
r lpush foo{t} bar
|
|
|
|
r sort foo{t} alpha limit 10 10 store zap{t}
|
|
|
|
r exists zap{t}
|
2011-12-01 10:07:55 -05:00
|
|
|
} {0}
|
|
|
|
|
2012-01-30 01:36:49 -05:00
|
|
|
test "SORT with STORE removes key if result is empty (github issue 227)" {
|
|
|
|
r flushdb
|
2021-06-09 08:13:24 -04:00
|
|
|
r lpush foo{t} bar
|
|
|
|
r sort emptylist{t} store foo{t}
|
|
|
|
r exists foo{t}
|
2012-01-30 01:36:49 -05:00
|
|
|
} {0}
|
|
|
|
|
2012-02-01 11:05:45 -05:00
|
|
|
test "SORT with BY <constant> and STORE should still order output" {
|
|
|
|
r del myset mylist
|
2012-02-01 11:37:48 -05:00
|
|
|
r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz
|
2012-02-01 11:05:45 -05:00
|
|
|
r sort myset alpha by _ store mylist
|
|
|
|
r lrange mylist 0 -1
|
2021-06-09 08:13:24 -04:00
|
|
|
} {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} {cluster:skip}
|
2012-02-01 11:05:45 -05:00
|
|
|
|
2012-02-01 11:17:52 -05:00
|
|
|
test "SORT will complain with numerical sorting and bad doubles (1)" {
|
|
|
|
r del myset
|
|
|
|
r sadd myset 1 2 3 4 not-a-double
|
|
|
|
set e {}
|
|
|
|
catch {r sort myset} e
|
|
|
|
set e
|
|
|
|
} {*ERR*double*}
|
|
|
|
|
|
|
|
test "SORT will complain with numerical sorting and bad doubles (2)" {
|
|
|
|
r del myset
|
|
|
|
r sadd myset 1 2 3 4
|
|
|
|
r mset score:1 10 score:2 20 score:3 30 score:4 not-a-double
|
|
|
|
set e {}
|
|
|
|
catch {r sort myset by score:*} e
|
|
|
|
set e
|
2021-06-09 08:13:24 -04:00
|
|
|
} {*ERR*double*} {cluster:skip}
|
2012-02-01 11:17:52 -05:00
|
|
|
|
|
|
|
test "SORT BY sub-sorts lexicographically if score is the same" {
|
|
|
|
r del myset
|
2012-02-01 11:37:48 -05:00
|
|
|
r sadd myset a b c d e f g h i l m n o p q r s t u v z aa aaa azz
|
|
|
|
foreach ele {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} {
|
2012-02-01 11:17:52 -05:00
|
|
|
set score:$ele 100
|
|
|
|
}
|
|
|
|
r sort myset by score:*
|
2021-06-09 08:13:24 -04:00
|
|
|
} {a aa aaa azz b c d e f g h i l m n o p q r s t u v z} {cluster:skip}
|
2012-02-01 11:17:52 -05:00
|
|
|
|
2012-04-17 10:28:50 -04:00
|
|
|
test "SORT GET with pattern ending with just -> does not get hash field" {
|
|
|
|
r del mylist
|
|
|
|
r lpush mylist a
|
|
|
|
r set x:a-> 100
|
|
|
|
r sort mylist by num get x:*->
|
2021-06-09 08:13:24 -04:00
|
|
|
} {100} {cluster:skip}
|
2012-04-17 10:28:50 -04:00
|
|
|
|
2014-10-23 11:52:35 -04:00
|
|
|
test "SORT by nosort retains native order for lists" {
|
|
|
|
r del testa
|
|
|
|
r lpush testa 2 1 4 3 5
|
|
|
|
r sort testa by nosort
|
2021-06-09 08:13:24 -04:00
|
|
|
} {5 3 4 1 2} {cluster:skip}
|
2014-10-23 11:52:35 -04:00
|
|
|
|
|
|
|
test "SORT by nosort plus store retains native order for lists" {
|
|
|
|
r del testa
|
|
|
|
r lpush testa 2 1 4 3 5
|
|
|
|
r sort testa by nosort store testb
|
|
|
|
r lrange testb 0 -1
|
2021-06-09 08:13:24 -04:00
|
|
|
} {5 3 4 1 2} {cluster:skip}
|
2014-10-23 11:52:35 -04:00
|
|
|
|
|
|
|
test "SORT by nosort with limit returns based on original list order" {
|
|
|
|
r sort testa by nosort limit 0 3 store testb
|
|
|
|
r lrange testb 0 -1
|
2021-06-09 08:13:24 -04:00
|
|
|
} {5 3 4} {cluster:skip}
|
2014-10-23 11:52:35 -04:00
|
|
|
|
2021-08-09 02:40:29 -04:00
|
|
|
test "SORT_RO - Successful case" {
|
|
|
|
r del mylist
|
|
|
|
r lpush mylist a
|
|
|
|
r set x:a 100
|
|
|
|
r sort_ro mylist by nosort get x:*->
|
|
|
|
} {100} {cluster:skip}
|
|
|
|
|
|
|
|
test "SORT_RO - Cannot run with STORE arg" {
|
|
|
|
catch {r sort_ro foolist STORE bar} e
|
|
|
|
set e
|
|
|
|
} {ERR syntax error}
|
|
|
|
|
2010-08-21 04:55:53 -04:00
|
|
|
tags {"slow"} {
|
|
|
|
set num 100
|
|
|
|
set res [create_random_dataset $num lpush]
|
2010-05-14 11:31:11 -04:00
|
|
|
|
2010-08-21 04:55:53 -04:00
|
|
|
test "SORT speed, $num element list BY key, 100 times" {
|
2010-06-02 17:14:55 -04:00
|
|
|
set start [clock clicks -milliseconds]
|
|
|
|
for {set i 0} {$i < 100} {incr i} {
|
2010-10-15 11:25:20 -04:00
|
|
|
set sorted [r sort tosort BY weight_* LIMIT 0 10]
|
2010-06-02 17:14:55 -04:00
|
|
|
}
|
|
|
|
set elapsed [expr [clock clicks -milliseconds]-$start]
|
2010-12-10 10:13:21 -05:00
|
|
|
if {$::verbose} {
|
|
|
|
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
|
|
|
|
flush stdout
|
|
|
|
}
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2010-05-14 11:31:11 -04:00
|
|
|
|
2010-08-21 04:55:53 -04:00
|
|
|
test "SORT speed, $num element list BY hash field, 100 times" {
|
2010-06-02 17:14:55 -04:00
|
|
|
set start [clock clicks -milliseconds]
|
|
|
|
for {set i 0} {$i < 100} {incr i} {
|
2010-10-15 11:25:20 -04:00
|
|
|
set sorted [r sort tosort BY wobj_*->weight LIMIT 0 10]
|
2010-06-02 17:14:55 -04:00
|
|
|
}
|
|
|
|
set elapsed [expr [clock clicks -milliseconds]-$start]
|
2010-12-10 10:13:21 -05:00
|
|
|
if {$::verbose} {
|
|
|
|
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
|
|
|
|
flush stdout
|
|
|
|
}
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2010-05-14 11:31:11 -04:00
|
|
|
|
2010-08-21 04:55:53 -04:00
|
|
|
test "SORT speed, $num element list directly, 100 times" {
|
2010-06-02 17:14:55 -04:00
|
|
|
set start [clock clicks -milliseconds]
|
|
|
|
for {set i 0} {$i < 100} {incr i} {
|
2010-10-15 11:25:20 -04:00
|
|
|
set sorted [r sort tosort LIMIT 0 10]
|
2010-06-02 17:14:55 -04:00
|
|
|
}
|
|
|
|
set elapsed [expr [clock clicks -milliseconds]-$start]
|
2010-12-10 10:13:21 -05:00
|
|
|
if {$::verbose} {
|
|
|
|
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
|
|
|
|
flush stdout
|
|
|
|
}
|
2010-08-21 04:55:53 -04:00
|
|
|
}
|
2010-05-14 11:31:11 -04:00
|
|
|
|
2010-08-21 04:55:53 -04:00
|
|
|
test "SORT speed, $num element list BY <const>, 100 times" {
|
2010-06-02 17:14:55 -04:00
|
|
|
set start [clock clicks -milliseconds]
|
|
|
|
for {set i 0} {$i < 100} {incr i} {
|
2010-10-15 11:25:20 -04:00
|
|
|
set sorted [r sort tosort BY nokey LIMIT 0 10]
|
2010-06-02 17:14:55 -04:00
|
|
|
}
|
|
|
|
set elapsed [expr [clock clicks -milliseconds]-$start]
|
2010-12-10 10:13:21 -05:00
|
|
|
if {$::verbose} {
|
|
|
|
puts -nonewline "\n Average time to sort: [expr double($elapsed)/100] milliseconds "
|
|
|
|
flush stdout
|
|
|
|
}
|
2021-06-09 08:13:24 -04:00
|
|
|
} {} {cluster:skip}
|
2010-08-21 04:55:53 -04:00
|
|
|
}
|
2023-01-16 06:49:30 -05:00
|
|
|
|
|
|
|
test {SETRANGE with huge offset} {
|
|
|
|
r lpush L 2 1 0
|
|
|
|
# expecting a different outcome on 32 and 64 bit systems
|
|
|
|
foreach value {9223372036854775807 2147483647} {
|
|
|
|
catch {[r sort_ro L by a limit 2 $value]} res
|
|
|
|
if {![string match "2" $res] && ![string match "*out of range*" $res]} {
|
|
|
|
assert_not_equal $res "expecting an error or 2"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-02-08 07:36:11 -05:00
|
|
|
|
|
|
|
test {SORT STORE quicklist with the right options} {
|
|
|
|
set origin_config [config_get_set list-max-listpack-size -1]
|
|
|
|
r del lst{t} lst_dst{t}
|
|
|
|
r config set list-max-listpack-size -1
|
|
|
|
r config set list-compress-depth 12
|
|
|
|
r lpush lst{t} {*}[split [string repeat "1" 6000] ""]
|
|
|
|
r sort lst{t} store lst_dst{t}
|
|
|
|
assert_encoding quicklist lst_dst{t}
|
|
|
|
assert_match "*ql_listpack_max:-1 ql_compressed:1*" [r debug object lst_dst{t}]
|
|
|
|
config_set list-max-listpack-size $origin_config
|
|
|
|
} {} {needs:debug}
|
2010-05-14 11:31:11 -04:00
|
|
|
}
|
2023-12-13 14:16:36 -05:00
|
|
|
|
|
|
|
start_cluster 1 0 {tags {"external:skip cluster sort"}} {
|
|
|
|
|
|
|
|
r flushall
|
|
|
|
r lpush "{a}mylist" 1 2 3
|
|
|
|
r set "{a}by1" 20
|
|
|
|
r set "{a}by2" 30
|
|
|
|
r set "{a}by3" 0
|
|
|
|
r set "{a}get1" 200
|
|
|
|
r set "{a}get2" 100
|
|
|
|
r set "{a}get3" 30
|
|
|
|
|
|
|
|
test "sort by in cluster mode" {
|
|
|
|
catch {r sort "{a}mylist" by by*} e
|
|
|
|
assert_match {ERR BY option of SORT denied in Cluster mode when *} $e
|
|
|
|
r sort "{a}mylist" by "{a}by*"
|
|
|
|
} {3 1 2}
|
|
|
|
|
|
|
|
test "sort get in cluster mode" {
|
|
|
|
catch {r sort "{a}mylist" by "{a}by*" get get*} e
|
|
|
|
assert_match {ERR GET option of SORT denied in Cluster mode when *} $e
|
|
|
|
r sort "{a}mylist" by "{a}by*" get "{a}get*"
|
|
|
|
} {30 200 100}
|
|
|
|
|
|
|
|
test "sort_ro by in cluster mode" {
|
|
|
|
catch {r sort_ro "{a}mylist" by by*} e
|
|
|
|
assert_match {ERR BY option of SORT denied in Cluster mode when *} $e
|
|
|
|
r sort_ro "{a}mylist" by "{a}by*"
|
|
|
|
} {3 1 2}
|
|
|
|
|
|
|
|
test "sort_ro get in cluster mode" {
|
|
|
|
catch {r sort_ro "{a}mylist" by "{a}by*" get get*} e
|
|
|
|
assert_match {ERR GET option of SORT denied in Cluster mode when *} $e
|
|
|
|
r sort_ro "{a}mylist" by "{a}by*" get "{a}get*"
|
|
|
|
} {30 200 100}
|
|
|
|
}
|