Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
20618c71
Commit
20618c71
authored
Apr 27, 2022
by
Oran Agra
Browse files
Merge remote-tracking branch 'origin/unstable' into 7.0
parents
fb4e0d40
89772ed8
Changes
122
Show whitespace changes
Inline
Side-by-side
tests/modules/mallocsize.c
0 → 100644
View file @
20618c71
#include "redismodule.h"
#include <string.h>
#include <assert.h>
#include <unistd.h>
#define UNUSED(V) ((void) V)
/* Registered type */
RedisModuleType
*
mallocsize_type
=
NULL
;
typedef
enum
{
UDT_RAW
,
UDT_STRING
,
UDT_DICT
}
udt_type_t
;
typedef
struct
{
void
*
ptr
;
size_t
len
;
}
raw_t
;
typedef
struct
{
udt_type_t
type
;
union
{
raw_t
raw
;
RedisModuleString
*
str
;
RedisModuleDict
*
dict
;
}
data
;
}
udt_t
;
void
udt_free
(
void
*
value
)
{
udt_t
*
udt
=
value
;
switch
(
udt
->
type
)
{
case
(
UDT_RAW
):
{
RedisModule_Free
(
udt
->
data
.
raw
.
ptr
);
break
;
}
case
(
UDT_STRING
):
{
RedisModule_FreeString
(
NULL
,
udt
->
data
.
str
);
break
;
}
case
(
UDT_DICT
):
{
RedisModuleString
*
dk
,
*
dv
;
RedisModuleDictIter
*
iter
=
RedisModule_DictIteratorStartC
(
udt
->
data
.
dict
,
"^"
,
NULL
,
0
);
while
((
dk
=
RedisModule_DictNext
(
NULL
,
iter
,
(
void
**
)
&
dv
))
!=
NULL
)
{
RedisModule_FreeString
(
NULL
,
dk
);
RedisModule_FreeString
(
NULL
,
dv
);
}
RedisModule_DictIteratorStop
(
iter
);
RedisModule_FreeDict
(
NULL
,
udt
->
data
.
dict
);
break
;
}
}
RedisModule_Free
(
udt
);
}
void
udt_rdb_save
(
RedisModuleIO
*
rdb
,
void
*
value
)
{
udt_t
*
udt
=
value
;
RedisModule_SaveUnsigned
(
rdb
,
udt
->
type
);
switch
(
udt
->
type
)
{
case
(
UDT_RAW
):
{
RedisModule_SaveStringBuffer
(
rdb
,
udt
->
data
.
raw
.
ptr
,
udt
->
data
.
raw
.
len
);
break
;
}
case
(
UDT_STRING
):
{
RedisModule_SaveString
(
rdb
,
udt
->
data
.
str
);
break
;
}
case
(
UDT_DICT
):
{
RedisModule_SaveUnsigned
(
rdb
,
RedisModule_DictSize
(
udt
->
data
.
dict
));
RedisModuleString
*
dk
,
*
dv
;
RedisModuleDictIter
*
iter
=
RedisModule_DictIteratorStartC
(
udt
->
data
.
dict
,
"^"
,
NULL
,
0
);
while
((
dk
=
RedisModule_DictNext
(
NULL
,
iter
,
(
void
**
)
&
dv
))
!=
NULL
)
{
RedisModule_SaveString
(
rdb
,
dk
);
RedisModule_SaveString
(
rdb
,
dv
);
RedisModule_FreeString
(
NULL
,
dk
);
/* Allocated by RedisModule_DictNext */
}
RedisModule_DictIteratorStop
(
iter
);
break
;
}
}
}
void
*
udt_rdb_load
(
RedisModuleIO
*
rdb
,
int
encver
)
{
if
(
encver
!=
0
)
return
NULL
;
udt_t
*
udt
=
RedisModule_Alloc
(
sizeof
(
*
udt
));
udt
->
type
=
RedisModule_LoadUnsigned
(
rdb
);
switch
(
udt
->
type
)
{
case
(
UDT_RAW
):
{
udt
->
data
.
raw
.
ptr
=
RedisModule_LoadStringBuffer
(
rdb
,
&
udt
->
data
.
raw
.
len
);
break
;
}
case
(
UDT_STRING
):
{
udt
->
data
.
str
=
RedisModule_LoadString
(
rdb
);
break
;
}
case
(
UDT_DICT
):
{
long
long
dict_len
=
RedisModule_LoadUnsigned
(
rdb
);
udt
->
data
.
dict
=
RedisModule_CreateDict
(
NULL
);
for
(
int
i
=
0
;
i
<
dict_len
;
i
+=
2
)
{
RedisModuleString
*
key
=
RedisModule_LoadString
(
rdb
);
RedisModuleString
*
val
=
RedisModule_LoadString
(
rdb
);
RedisModule_DictSet
(
udt
->
data
.
dict
,
key
,
val
);
}
break
;
}
}
return
udt
;
}
size_t
udt_mem_usage
(
RedisModuleKeyOptCtx
*
ctx
,
const
void
*
value
,
size_t
sample_size
)
{
UNUSED
(
ctx
);
UNUSED
(
sample_size
);
const
udt_t
*
udt
=
value
;
size_t
size
=
sizeof
(
*
udt
);
switch
(
udt
->
type
)
{
case
(
UDT_RAW
):
{
size
+=
RedisModule_MallocSize
(
udt
->
data
.
raw
.
ptr
);
break
;
}
case
(
UDT_STRING
):
{
size
+=
RedisModule_MallocSizeString
(
udt
->
data
.
str
);
break
;
}
case
(
UDT_DICT
):
{
void
*
dk
;
size_t
keylen
;
RedisModuleString
*
dv
;
RedisModuleDictIter
*
iter
=
RedisModule_DictIteratorStartC
(
udt
->
data
.
dict
,
"^"
,
NULL
,
0
);
while
((
dk
=
RedisModule_DictNextC
(
iter
,
&
keylen
,
(
void
**
)
&
dv
))
!=
NULL
)
{
size
+=
keylen
;
size
+=
RedisModule_MallocSizeString
(
dv
);
}
RedisModule_DictIteratorStop
(
iter
);
break
;
}
}
return
size
;
}
/* MALLOCSIZE.SETRAW key len */
int
cmd_setraw
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
3
)
return
RedisModule_WrongArity
(
ctx
);
RedisModuleKey
*
key
=
RedisModule_OpenKey
(
ctx
,
argv
[
1
],
REDISMODULE_WRITE
);
udt_t
*
udt
=
RedisModule_Alloc
(
sizeof
(
*
udt
));
udt
->
type
=
UDT_RAW
;
long
long
raw_len
;
RedisModule_StringToLongLong
(
argv
[
2
],
&
raw_len
);
udt
->
data
.
raw
.
ptr
=
RedisModule_Alloc
(
raw_len
);
udt
->
data
.
raw
.
len
=
raw_len
;
RedisModule_ModuleTypeSetValue
(
key
,
mallocsize_type
,
udt
);
RedisModule_CloseKey
(
key
);
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
}
/* MALLOCSIZE.SETSTR key string */
int
cmd_setstr
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
3
)
return
RedisModule_WrongArity
(
ctx
);
RedisModuleKey
*
key
=
RedisModule_OpenKey
(
ctx
,
argv
[
1
],
REDISMODULE_WRITE
);
udt_t
*
udt
=
RedisModule_Alloc
(
sizeof
(
*
udt
));
udt
->
type
=
UDT_STRING
;
udt
->
data
.
str
=
argv
[
2
];
RedisModule_RetainString
(
ctx
,
argv
[
2
]);
RedisModule_ModuleTypeSetValue
(
key
,
mallocsize_type
,
udt
);
RedisModule_CloseKey
(
key
);
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
}
/* MALLOCSIZE.SETDICT key field value [field value ...] */
int
cmd_setdict
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
<
4
||
argc
%
2
)
return
RedisModule_WrongArity
(
ctx
);
RedisModuleKey
*
key
=
RedisModule_OpenKey
(
ctx
,
argv
[
1
],
REDISMODULE_WRITE
);
udt_t
*
udt
=
RedisModule_Alloc
(
sizeof
(
*
udt
));
udt
->
type
=
UDT_DICT
;
udt
->
data
.
dict
=
RedisModule_CreateDict
(
ctx
);
for
(
int
i
=
2
;
i
<
argc
;
i
+=
2
)
{
RedisModule_DictSet
(
udt
->
data
.
dict
,
argv
[
i
],
argv
[
i
+
1
]);
/* No need to retain argv[i], it is copied as the rax key */
RedisModule_RetainString
(
ctx
,
argv
[
i
+
1
]);
}
RedisModule_ModuleTypeSetValue
(
key
,
mallocsize_type
,
udt
);
RedisModule_CloseKey
(
key
);
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
UNUSED
(
argv
);
UNUSED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"mallocsize"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
RedisModuleTypeMethods
tm
=
{
.
version
=
REDISMODULE_TYPE_METHOD_VERSION
,
.
rdb_load
=
udt_rdb_load
,
.
rdb_save
=
udt_rdb_save
,
.
free
=
udt_free
,
.
mem_usage2
=
udt_mem_usage
,
};
mallocsize_type
=
RedisModule_CreateDataType
(
ctx
,
"allocsize"
,
0
,
&
tm
);
if
(
mallocsize_type
==
NULL
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"mallocsize.setraw"
,
cmd_setraw
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"mallocsize.setstr"
,
cmd_setstr
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"mallocsize.setdict"
,
cmd_setdict
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
tests/modules/moduleconfigs.c
View file @
20618c71
...
...
@@ -6,6 +6,7 @@ long long longval;
long
long
memval
;
RedisModuleString
*
strval
=
NULL
;
int
enumval
;
int
flagsval
;
/* Series of get and set callbacks for each type of config, these rely on the privdata ptr
* to point to the config, and they register the configs as such. Note that one could also just
...
...
@@ -68,6 +69,20 @@ int setEnumConfigCommand(const char *name, int val, void *privdata, RedisModuleS
return
REDISMODULE_OK
;
}
int
getFlagsConfigCommand
(
const
char
*
name
,
void
*
privdata
)
{
REDISMODULE_NOT_USED
(
name
);
REDISMODULE_NOT_USED
(
privdata
);
return
flagsval
;
}
int
setFlagsConfigCommand
(
const
char
*
name
,
int
val
,
void
*
privdata
,
RedisModuleString
**
err
)
{
REDISMODULE_NOT_USED
(
name
);
REDISMODULE_NOT_USED
(
err
);
REDISMODULE_NOT_USED
(
privdata
);
flagsval
=
val
;
return
REDISMODULE_OK
;
}
int
boolApplyFunc
(
RedisModuleCtx
*
ctx
,
void
*
privdata
,
RedisModuleString
**
err
)
{
REDISMODULE_NOT_USED
(
ctx
);
REDISMODULE_NOT_USED
(
privdata
);
...
...
@@ -106,10 +121,13 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
}
/* On the stack to make sure we're copying them. */
const
char
*
enum_vals
[
3
]
=
{
"one"
,
"two"
,
"three"
};
const
int
int_vals
[
3
]
=
{
0
,
2
,
4
};
const
char
*
enum_vals
[]
=
{
"none"
,
"one"
,
"two"
,
"three"
};
const
int
int_vals
[]
=
{
0
,
1
,
2
,
4
};
if
(
RedisModule_RegisterEnumConfig
(
ctx
,
"enum"
,
0
,
REDISMODULE_CONFIG_DEFAULT
,
enum_vals
,
int_vals
,
3
,
getEnumConfigCommand
,
setEnumConfigCommand
,
NULL
,
NULL
)
==
REDISMODULE_ERR
)
{
if
(
RedisModule_RegisterEnumConfig
(
ctx
,
"enum"
,
1
,
REDISMODULE_CONFIG_DEFAULT
,
enum_vals
,
int_vals
,
4
,
getEnumConfigCommand
,
setEnumConfigCommand
,
NULL
,
NULL
)
==
REDISMODULE_ERR
)
{
return
REDISMODULE_ERR
;
}
if
(
RedisModule_RegisterEnumConfig
(
ctx
,
"flags"
,
3
,
REDISMODULE_CONFIG_DEFAULT
|
REDISMODULE_CONFIG_BITFLAGS
,
enum_vals
,
int_vals
,
4
,
getFlagsConfigCommand
,
setFlagsConfigCommand
,
NULL
,
NULL
)
==
REDISMODULE_ERR
)
{
return
REDISMODULE_ERR
;
}
/* Memory config here. */
...
...
tests/modules/publish.c
0 → 100644
View file @
20618c71
#include "redismodule.h"
#include <string.h>
#include <assert.h>
#include <unistd.h>
#define UNUSED(V) ((void) V)
int
cmd_publish_classic
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
3
)
return
RedisModule_WrongArity
(
ctx
);
int
receivers
=
RedisModule_PublishMessage
(
ctx
,
argv
[
1
],
argv
[
2
]);
RedisModule_ReplyWithLongLong
(
ctx
,
receivers
);
return
REDISMODULE_OK
;
}
int
cmd_publish_shard
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
3
)
return
RedisModule_WrongArity
(
ctx
);
int
receivers
=
RedisModule_PublishMessageShard
(
ctx
,
argv
[
1
],
argv
[
2
]);
RedisModule_ReplyWithLongLong
(
ctx
,
receivers
);
return
REDISMODULE_OK
;
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
UNUSED
(
argv
);
UNUSED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"publish"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"publish.classic"
,
cmd_publish_classic
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"publish.shard"
,
cmd_publish_shard
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
tests/modules/subcommands.c
View file @
20618c71
...
...
@@ -11,7 +11,10 @@ int cmd_set(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
int
cmd_get
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
UNUSED
(
argv
);
UNUSED
(
argc
);
if
(
argc
>
4
)
/* For testing */
return
RedisModule_WrongArity
(
ctx
);
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
return
REDISMODULE_OK
;
}
...
...
tests/sentinel/tests/05-manual.tcl
View file @
20618c71
...
...
@@ -3,9 +3,9 @@
source
"../tests/includes/init-tests.tcl"
foreach_sentinel_id id
{
S $id sentinel debug info-period
1
000
S $id sentinel debug default-down-after
3
000
S $id sentinel debug publish-period
5
00
S $id sentinel debug info-period
2
000
S $id sentinel debug default-down-after
6
000
S $id sentinel debug publish-period
10
00
}
test
"Manual failover works"
{
...
...
tests/sentinel/tests/includes/init-tests.tcl
View file @
20618c71
...
...
@@ -28,7 +28,8 @@ test "(init) Sentinels can start monitoring a master" {
foreach_sentinel_id id
{
assert
{[
S $id sentinel master mymaster
]
ne
{}}
S $id SENTINEL SET mymaster down-after-milliseconds 2000
S $id SENTINEL SET mymaster failover-timeout 20000
S $id SENTINEL SET mymaster failover-timeout 10000
S $id SENTINEL debug tilt-period 5000
S $id SENTINEL SET mymaster parallel-syncs 10
if
{
$::leaked
_fds_file !=
""
&&
[
exec uname
]
==
"Linux"
}
{
S $id SENTINEL SET mymaster notification-script ../../tests/helpers/check_leaked_fds.tcl
...
...
tests/support/util.tcl
View file @
20618c71
...
...
@@ -77,6 +77,12 @@ proc getInfoProperty {infostr property} {
}
}
proc cluster_info
{
r field
}
{
if
{[
regexp
"^
$field:
(.*?)
\r\n
"
[
$r
cluster info
]
_ value
]}
{
set _ $value
}
}
# Return value for INFO property
proc status
{
r property
}
{
set _
[
getInfoProperty
[
{*}
$r
info
]
$property
]
...
...
@@ -823,11 +829,21 @@ proc subscribe {client channels} {
consume_subscribe_messages $client subscribe $channels
}
proc ssubscribe
{
client channels
}
{
$client ssubscribe
{*}
$channels
consume_subscribe_messages $client ssubscribe $channels
}
proc unsubscribe
{
client
{
channels
{}}}
{
$client unsubscribe
{*}
$channels
consume_subscribe_messages $client unsubscribe $channels
}
proc sunsubscribe
{
client
{
channels
{}}}
{
$client sunsubscribe
{*}
$channels
consume_subscribe_messages $client sunsubscribe $channels
}
proc psubscribe
{
client channels
}
{
$client psubscribe
{*}
$channels
consume_subscribe_messages $client psubscribe $channels
...
...
tests/test_helper.tcl
View file @
20618c71
...
...
@@ -94,6 +94,7 @@ set ::all_tests {
unit/client-eviction
unit/violations
unit/replybufsize
unit/cluster-scripting
}
# Index to the next test to run in the ::all_tests list.
set ::next_test 0
...
...
@@ -274,6 +275,16 @@ proc s {args} {
status
[
srv $level
"client"
]
[
lindex $args 0
]
}
# Provide easy access to CLUSTER INFO properties. Same semantic as
"proc s"
.
proc csi
{
args
}
{
set level 0
if
{[
string is integer
[
lindex $args 0
]]}
{
set level
[
lindex $args 0
]
set args
[
lrange $args 1 end
]
}
cluster_info
[
srv $level
"client"
]
[
lindex $args 0
]
}
# Test wrapped into run_solo are sent back from the client to the
# test server, so that the test server will send them again to
# clients once the clients are idle.
...
...
tests/unit/acl-v2.tcl
View file @
20618c71
...
...
@@ -173,7 +173,7 @@ start_server {tags {"acl external:skip"}} {
assert_equal PONG
[
$r2
PING
]
assert_equal
{}
[
$r2
get readwrite_str
]
assert_error
{
ERR* not an integer *
}
{
$r2
set readwrite_str bar ex get
}
assert_error
{
ERR
* not an integer *
}
{
$r2
set readwrite_str bar ex get
}
assert_equal
{
OK
}
[
$r2
set readwrite_str bar
]
assert_equal
{
bar
}
[
$r2
get readwrite_str
]
...
...
tests/unit/acl.tcl
View file @
20618c71
...
...
@@ -511,7 +511,7 @@ start_server {tags {"acl external:skip"}} {
test
"ACL CAT with illegal arguments"
{
assert_error
{
*Unknown category 'NON_EXISTS'
}
{
r ACL CAT NON_EXISTS
}
assert_error
{
*
U
nknown subcommand or wrong number of arguments for 'CAT'*
}
{
r ACL CAT NON_EXISTS NON_EXISTS2
}
assert_error
{
*
u
nknown subcommand or wrong number of arguments for 'CAT'*
}
{
r ACL CAT NON_EXISTS NON_EXISTS2
}
}
test
"ACL CAT without category - list all categories"
{
...
...
tests/unit/auth.tcl
View file @
20618c71
...
...
@@ -2,7 +2,7 @@ start_server {tags {"auth external:skip"}} {
test
{
AUTH fails if there is no password configured server side
}
{
catch
{
r auth foo
}
err
set _ $err
}
{
ERR*any password*
}
}
{
ERR
*any password*
}
test
{
Arity check for auth command
}
{
catch
{
r auth a b c
}
err
...
...
tests/unit/bitops.tcl
View file @
20618c71
...
...
@@ -133,12 +133,12 @@ start_server {tags {"bitops"}} {
test
{
BITCOUNT syntax error #1
}
{
catch
{
r bitcount s 0
}
e
set e
}
{
ERR*syntax*
}
}
{
ERR
*syntax*
}
test
{
BITCOUNT syntax error #2
}
{
catch
{
r bitcount s 0 1 hello
}
e
set e
}
{
ERR*syntax*
}
}
{
ERR
*syntax*
}
test
{
BITCOUNT regression test for github issue #582
}
{
r del foo
...
...
@@ -546,7 +546,10 @@ start_server {tags {"bitops"}} {
}
}
}
}
run_solo
{
bitops-large-memory
}
{
start_server
{
tags
{
"bitops"
}}
{
test
"BIT pos larger than UINT_MAX"
{
set bytes
[
expr
(
1 << 29
)
+ 1
]
set bitpos
[
expr
(
1 << 32
)]
...
...
@@ -587,3 +590,4 @@ start_server {tags {"bitops"}} {
r del mykey
}
{
1
}
{
large-memory needs:debug
}
}
}
;
#run_solo
tests/unit/cluster-scripting.tcl
0 → 100644
View file @
20618c71
# make sure the test infra won't use SELECT
set old_singledb $::singledb
set ::singledb 1
start_server
{
overrides
{
cluster-enabled yes
}
tags
{
external:skip cluster
}}
{
r 0 cluster addslotsrange 0 16383
wait_for_condition 50 100
{
[
csi 0 cluster_state
]
eq
"ok"
}
else
{
fail
"Cluster never became 'ok'"
}
test
{
Eval scripts with shebangs and functions default to no cross slots
}
{
# Test that scripts with shebang block cross slot operations
assert_error
"ERR Script attempted to access keys that do not hash to the same slot*"
{
r 0 eval
{
#!lua
redis.call
(
'set', 'foo', 'bar'
)
redis.call
(
'set', 'bar', 'foo'
)
return 'OK'
}
0
}
# Test the functions by default block cross slot operations
r 0 function load REPLACE
{
#!lua name=crossslot
local function test_cross_slot
(
keys, args
)
redis.call
(
'set', 'foo', 'bar'
)
redis.call
(
'set', 'bar', 'foo'
)
return 'OK'
end
redis.register_function
(
'test_cross_slot', test_cross_slot
)}
assert_error
"ERR Script attempted to access keys that do not hash to the same slot*"
{
r FCALL test_cross_slot 0
}
}
test
{
Cross slot commands are allowed by default for eval scripts and with allow-cross-slot-keys flag
}
{
# Old style lua scripts are allowed to access cross slot operations
r 0 eval
"redis.call('set', 'foo', 'bar'); redis.call('set', 'bar', 'foo')"
0
# scripts with allow-cross-slot-keys flag are allowed
r 0 eval
{
#!lua flags=allow-cross-slot-keys
redis.call
(
'set', 'foo', 'bar'
);
redis.call
(
'set', 'bar', 'foo'
)
}
0
# Functions with allow-cross-slot-keys flag are allowed
r 0 function load REPLACE
{
#!lua name=crossslot
local function test_cross_slot
(
keys, args
)
redis.call
(
'set', 'foo', 'bar'
)
redis.call
(
'set', 'bar', 'foo'
)
return 'OK'
end
redis.register_function
{
function_name='test_cross_slot', callback=test_cross_slot, flags=
{
'allow-cross-slot-keys'
}}}
r FCALL test_cross_slot 0
}
test
{
Cross slot commands are also blocked if they disagree with pre-declared keys
}
{
assert_error
"ERR Script attempted to access keys that do not hash to the same slot*"
{
r 0 eval
{
#!lua
redis.call
(
'set', 'foo', 'bar'
)
return 'OK'
}
1 bar
}
}
}
set ::singledb $old_singledb
tests/unit/functions.tcl
View file @
20618c71
...
...
@@ -117,7 +117,7 @@ start_server {tags {"scripting"}} {
r function bad_subcommand
}
e
set _ $e
}
{
*
U
nknown subcommand*
}
}
{
*
u
nknown subcommand*
}
test
{
FUNCTION - test loading from rdb
}
{
r debug reload
...
...
@@ -205,7 +205,7 @@ start_server {tags {"scripting"}} {
test
{
FUNCTION - test function restore with wrong number of arguments
}
{
catch
{
r function restore arg1 args2 arg3
}
e
set _ $e
}
{
*
U
nknown subcommand or wrong number of arguments for 'restore'. Try FUNCTION HELP.
}
}
{
*
u
nknown subcommand or wrong number of arguments for 'restore'. Try FUNCTION HELP.
}
test
{
FUNCTION - test fcall_ro with write command
}
{
r function load REPLACE
[
get_no_writes_function_code lua test test
{
return redis.call
(
'set', 'x', '1'
)}]
...
...
@@ -298,7 +298,7 @@ start_server {tags {"scripting"}} {
assert_match
{
*only supports SYNC|ASYNC*
}
$e
catch
{
r function flush sync extra_arg
}
e
assert_match
{
*
U
nknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.
}
$e
assert_match
{
*
u
nknown subcommand or wrong number of arguments for 'flush'. Try FUNCTION HELP.
}
$e
}
}
...
...
@@ -624,16 +624,16 @@ start_server {tags {"scripting"}} {
}
}
e
set _ $e
}
{
*attempt to
call field 'call'
(
a nil value
)
*
}
}
{
*attempt
ed
to
access nonexistent global variable 'call'
*
}
test
{
LIBRARIES - redis.
call
from function load
}
{
test
{
LIBRARIES - redis.
setresp
from function load
}
{
catch
{
r function load replace
{
#!lua name=lib2
return redis.setresp
(
3
)
}
}
e
set _ $e
}
{
*attempt to
call field 'setresp'
(
a nil value
)
*
}
}
{
*attempt
ed
to
access nonexistent global variable 'setresp'
*
}
test
{
LIBRARIES - redis.set_repl from function load
}
{
catch
{
...
...
@@ -642,7 +642,7 @@ start_server {tags {"scripting"}} {
}
}
e
set _ $e
}
{
*attempt to
call field 'set_repl'
(
a nil value
)
*
}
}
{
*attempt
ed
to
access nonexistent global variable 'set_repl'
*
}
test
{
LIBRARIES - malicious access test
}
{
# the 'library' API is not exposed inside a
...
...
@@ -669,37 +669,18 @@ start_server {tags {"scripting"}} {
end
)
end
)
}
assert_equal
{
OK
}
[
r fcall f1 0
]
catch
{[
r fcall f1 0
]}
e
assert_match
{
*Attempt to modify a readonly table*
}
$e
catch
{[
r function load
{
#!lua name=lib2
redis.math.random
()
}]}
e
assert_match
{
*can only be called inside a script invocation*
}
$e
catch
{[
r function load
{
#!lua name=lib2
redis.math.randomseed
()
}]}
e
assert_match
{
*can only be called inside a script invocation*
}
$e
assert_match
{
*Script attempted to access nonexistent global variable 'math'*
}
$e
catch
{[
r function load
{
#!lua name=lib2
redis.redis.call
(
'ping'
)
}]}
e
assert_match
{
*can only be called inside a script invocation*
}
$e
catch
{[
r function load
{
#!lua name=lib2
redis.redis.pcall
(
'ping'
)
}]}
e
assert_match
{
*can only be called inside a script invocation*
}
$e
catch
{[
r function load
{
#!lua name=lib2
redis.redis.setresp
(
3
)
}]}
e
assert_match
{
*can only be called inside a script invocation*
}
$e
catch
{[
r function load
{
#!lua name=lib2
redis.redis.set_repl
(
redis.redis.REPL_NONE
)
}]}
e
assert_match
{
*can only be called inside a script invocation*
}
$e
assert_match
{
*Script attempted to access nonexistent global variable 'redis'*
}
$e
catch
{[
r fcall f2 0
]}
e
assert_match
{
*can only be called on FUNCTION LOAD command*
}
$e
...
...
@@ -756,7 +737,7 @@ start_server {tags {"scripting"}} {
}
}
e
set _ $e
}
{
*
a
ttempt
ed
to
create global variable 'a'
*
}
}
{
*
A
ttempt to
modify a readonly table
*
}
test
{
LIBRARIES - named arguments
}
{
r function load
{
#!lua name=lib
...
...
@@ -986,7 +967,7 @@ start_server {tags {"scripting"}} {
assert_match
{
*command not allowed when used memory*
}
$e
r config set maxmemory 0
}
}
{
OK
}
{
needs:config-maxmemory
}
test
{
FUNCTION - verify allow-omm allows running any command
}
{
r FUNCTION load replace
{
#!lua name=f1
...
...
@@ -999,11 +980,11 @@ start_server {tags {"scripting"}} {
r config set maxmemory 1
assert_match
{
OK
}
[
r fcall f1 1
k
]
assert_match
{
OK
}
[
r fcall f1 1
x
]
assert_match
{
1
}
[
r get x
]
r config set maxmemory 0
}
}
{
OK
}
{
needs:config-maxmemory
}
}
start_server
{
tags
{
"scripting"
}}
{
...
...
@@ -1074,7 +1055,7 @@ start_server {tags {"scripting"}} {
assert_match
{
*can not run it when used memory > 'maxmemory'*
}
$e
r config set maxmemory 0
}
}
{
OK
}
{
needs:config-maxmemory
}
test
{
FUNCTION - deny oom on no-writes function
}
{
r FUNCTION load replace
{
#!lua name=test
...
...
@@ -1090,7 +1071,7 @@ start_server {tags {"scripting"}} {
assert_match
{
*can not run it when used memory > 'maxmemory'*
}
$e
r config set maxmemory 0
}
}
{
OK
}
{
needs:config-maxmemory
}
test
{
FUNCTION - allow stale
}
{
r FUNCTION load replace
{
#!lua name=test
...
...
@@ -1198,4 +1179,32 @@ start_server {tags {"scripting"}} {
redis.register_function
(
'foo', function
()
return 1 end
)
}
}
{
foo
}
test
{
FUNCTION - trick global protection 1
}
{
r FUNCTION FLUSH
r FUNCTION load
{
#!lua name=test1
redis.register_function
(
'f1', function
()
mt = getmetatable
(
_G
)
original_globals = mt.__index
original_globals
[
'redis'
]
= function
()
return 1 end
end
)
}
catch
{[
r fcall f1 0
]}
e
set _ $e
}
{
*Attempt to modify a readonly table*
}
test
{
FUNCTION - test getmetatable on script load
}
{
r FUNCTION FLUSH
catch
{
r FUNCTION load
{
#!lua name=test1
mt = getmetatable
(
_G
)
}
}
e
set _ $e
}
{
*Script attempted to access nonexistent global variable 'getmetatable'*
}
}
tests/unit/geo.tcl
View file @
20618c71
...
...
@@ -193,14 +193,14 @@ start_server {tags {"geo"}} {
r geoadd nyc xx nx -73.9454966 40.747533
"lic market"
}
err
set err
}
{
ERR*syntax*
}
}
{
ERR
*syntax*
}
test
{
GEOADD update with invalid option
}
{
catch
{
r geoadd nyc ch xx foo -73.9454966 40.747533
"lic market"
}
err
set err
}
{
ERR*syntax*
}
}
{
ERR
*syntax*
}
test
{
GEOADD invalid coordinates
}
{
catch
{
...
...
@@ -229,27 +229,27 @@ start_server {tags {"geo"}} {
test
{
GEOSEARCH FROMLONLAT and FROMMEMBER cannot exist at the same time
}
{
catch
{
r geosearch nyc fromlonlat -73.9798091 40.7598464 frommember xxx bybox 6 6 km asc
}
e
set e
}
{
ERR*syntax*
}
}
{
ERR
*syntax*
}
test
{
GEOSEARCH FROMLONLAT and FROMMEMBER one must exist
}
{
catch
{
r geosearch nyc bybox 3 3 km asc desc withhash withdist withcoord
}
e
set e
}
{
ERR*exactly one of FROMMEMBER or FROMLONLAT*
}
}
{
ERR
*exactly one of FROMMEMBER or FROMLONLAT*
}
test
{
GEOSEARCH BYRADIUS and BYBOX cannot exist at the same time
}
{
catch
{
r geosearch nyc fromlonlat -73.9798091 40.7598464 byradius 3 km bybox 3 3 km asc
}
e
set e
}
{
ERR*syntax*
}
}
{
ERR
*syntax*
}
test
{
GEOSEARCH BYRADIUS and BYBOX one must exist
}
{
catch
{
r geosearch nyc fromlonlat -73.9798091 40.7598464 asc desc withhash withdist withcoord
}
e
set e
}
{
ERR*exactly one of BYRADIUS and BYBOX*
}
}
{
ERR
*exactly one of BYRADIUS and BYBOX*
}
test
{
GEOSEARCH with STOREDIST option
}
{
catch
{
r geosearch nyc fromlonlat -73.9798091 40.7598464 bybox 6 6 km asc storedist
}
e
set e
}
{
ERR*syntax*
}
}
{
ERR
*syntax*
}
test
{
GEORADIUS withdist
(
sorted
)}
{
r georadius nyc -73.9798091 40.7598464 3 km withdist asc
...
...
@@ -274,12 +274,12 @@ start_server {tags {"geo"}} {
test
{
GEORADIUS with ANY but no COUNT
}
{
catch
{
r georadius nyc -73.9798091 40.7598464 10 km ANY ASC
}
e
set e
}
{
ERR*ANY*requires*COUNT*
}
}
{
ERR
*ANY*requires*COUNT*
}
test
{
GEORADIUS with COUNT but missing integer argument
}
{
catch
{
r georadius nyc -73.9798091 40.7598464 10 km COUNT
}
e
set e
}
{
ERR*syntax*
}
}
{
ERR
*syntax*
}
test
{
GEORADIUS with COUNT DESC
}
{
r georadius nyc -73.9798091 40.7598464 10 km COUNT 2 DESC
...
...
tests/unit/introspection.tcl
View file @
20618c71
...
...
@@ -23,9 +23,9 @@ start_server {tags {"introspection"}} {
assert_error
"ERR wrong number of arguments for 'client|kill' command"
{
r client kill
}
assert_error
"ERR syntax error*"
{
r client kill id 10 wrong_arg
}
assert_error
"ERR*greater than 0*"
{
r client kill id str
}
assert_error
"ERR*greater than 0*"
{
r client kill id -1
}
assert_error
"ERR*greater than 0*"
{
r client kill id 0
}
assert_error
"ERR
*greater than 0*"
{
r client kill id str
}
assert_error
"ERR
*greater than 0*"
{
r client kill id -1
}
assert_error
"ERR
*greater than 0*"
{
r client kill id 0
}
assert_error
"ERR Unknown client type*"
{
r client kill type wrong_type
}
...
...
@@ -215,6 +215,7 @@ start_server {tags {"introspection"}} {
dbfilename
logfile
dir
socket-mark-id
}
if
{
!$::tls
}
{
...
...
@@ -285,16 +286,22 @@ start_server {tags {"introspection"}} {
}
}
{}
{
external:skip
}
test
{
CONFIG REWRITE handles save properly
}
{
test
{
CONFIG REWRITE handles save
and shutdown
properly
}
{
r config set save
"3600 1 300 100 60 10000"
r config set shutdown-on-sigterm
"nosave now"
r config set shutdown-on-sigint
"save"
r config rewrite
restart_server 0 true false
assert_equal
[
r config get save
]
{
save
{
3600 1 300 100 60 10000
}}
assert_equal
[
r config get shutdown-on-sigterm
]
{
shutdown-on-sigterm
{
nosave now
}}
assert_equal
[
r config get shutdown-on-sigint
]
{
shutdown-on-sigint save
}
r config set save
""
r config set shutdown-on-sigterm
"default"
r config rewrite
restart_server 0 true false
assert_equal
[
r config get save
]
{
save
{}}
assert_equal
[
r config get shutdown-on-sigterm
]
{
shutdown-on-sigterm default
}
start_server
{
config
"minimal.conf"
}
{
assert_equal
[
r config get save
]
{
save
{
3600 1 300 100 60 10000
}}
...
...
@@ -409,11 +416,11 @@ start_server {tags {"introspection"}} {
}
test
{
CONFIG SET duplicate configs
}
{
assert_error
"ERR*duplicate*"
{
r config set maxmemory 10000001 maxmemory 10000002
}
assert_error
"ERR
*duplicate*"
{
r config set maxmemory 10000001 maxmemory 10000002
}
}
test
{
CONFIG SET set immutable
}
{
assert_error
"ERR*immutable*"
{
r config set daemonize yes
}
assert_error
"ERR
*immutable*"
{
r config set daemonize yes
}
}
test
{
CONFIG GET hidden configs
}
{
...
...
@@ -448,8 +455,8 @@ start_server {tags {"introspection"}} {
start_server
{
tags
{
"introspection external:skip"
}
overrides
{
enable-protected-configs
{
no
}
enable-debug-command
{
no
}}}
{
test
{
cannot modify protected configuration - no
}
{
assert_error
"ERR*protected*"
{
r config set dir somedir
}
assert_error
"ERR*DEBUG command not allowed*"
{
r DEBUG HELP
}
assert_error
"ERR
*protected*"
{
r config set dir somedir
}
assert_error
"ERR
*DEBUG command not allowed*"
{
r DEBUG HELP
}
}
{}
{
needs:debug
}
}
...
...
@@ -464,8 +471,8 @@ start_server {config "minimal.conf" tags {"introspection external:skip"} overrid
if
{
$myaddr
!=
""
&& !
[
string match
{
127.*
}
$myaddr
]}
{
# Non-loopback client should fail
set r2
[
get_nonloopback_client
]
assert_error
"ERR*protected*"
{
$r2
config set dir somedir
}
assert_error
"ERR*DEBUG command not allowed*"
{
$r2
DEBUG HELP
}
assert_error
"ERR
*protected*"
{
$r2
config set dir somedir
}
assert_error
"ERR
*DEBUG command not allowed*"
{
$r2
DEBUG HELP
}
}
}
{}
{
needs:debug
}
}
...
...
tests/unit/moduleapi/aclcheck.tcl
View file @
20618c71
...
...
@@ -16,6 +16,7 @@ start_server {tags {"modules acl"}} {
assert
{[
dict get $entry username
]
eq
{
default
}}
assert
{[
dict get $entry context
]
eq
{
module
}}
assert
{[
dict get $entry object
]
eq
{
set
}}
assert
{[
dict get $entry reason
]
eq
{
command
}}
}
test
{
test module check acl for key perm
}
{
...
...
@@ -75,6 +76,7 @@ start_server {tags {"modules acl"}} {
assert
{[
dict get $entry username
]
eq
{
default
}}
assert
{[
dict get $entry context
]
eq
{
module
}}
assert
{[
dict get $entry object
]
eq
{
z
}}
assert
{[
dict get $entry reason
]
eq
{
key
}}
# rm call check for command permission
r acl setuser default -set
...
...
@@ -88,6 +90,7 @@ start_server {tags {"modules acl"}} {
assert
{[
dict get $entry username
]
eq
{
default
}}
assert
{[
dict get $entry context
]
eq
{
module
}}
assert
{[
dict get $entry object
]
eq
{
set
}}
assert
{[
dict get $entry reason
]
eq
{
command
}}
}
test
"Unload the module - aclcheck"
{
...
...
tests/unit/moduleapi/basics.tcl
View file @
20618c71
...
...
@@ -36,6 +36,6 @@ start_server {tags {"modules"}} {
start_server
{
tags
{
"modules external:skip"
}
overrides
{
enable-module-command no
}}
{
test
{
module command disabled
}
{
assert_error
"ERR*MODULE command not allowed*"
{
r module load $testmodule
}
assert_error
"ERR
*MODULE command not allowed*"
{
r module load $testmodule
}
}
}
\ No newline at end of file
tests/unit/moduleapi/blockedclient.tcl
View file @
20618c71
...
...
@@ -90,7 +90,8 @@ start_server {tags {"modules"}} {
}
}
test
{
Busy module command
}
{
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
...
...
@@ -98,7 +99,15 @@ start_server {tags {"modules"}} {
# 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
...
...
@@ -112,11 +121,16 @@ start_server {tags {"modules"}} {
}
else
{
fail
"Failed waiting for busy command to end"
}
$rd read
assert_equal
[
$rd
read
]
"1"
assert_equal
[
$rd
read
]
"PONG"
#run command that blocks for 200ms
#
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
...
...
@@ -128,6 +142,7 @@ start_server {tags {"modules"}} {
$rd close
r config set busy-reply-threshold $old_time_limit
}
}
test
{
RM_Call from blocked client
}
{
set busy_time_limit 50
...
...
@@ -141,6 +156,10 @@ start_server {tags {"modules"}} {
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
...
...
@@ -162,10 +181,10 @@ start_server {tags {"modules"}} {
assert_equal
[
r ping
]
{
PONG
}
r config set busy-reply-threshold $old_time_limit
set res
[
$rd
read
]
assert_equal
[
$rd
read
]
{
foo bar
}
assert_equal
[
$rd
read
]
{
PONG
}
$rd close
set _ $res
}
{
foo bar
}
}
test
{
blocked client reaches client output buffer limit
}
{
r hset hash big
[
string repeat x 50000
]
...
...
@@ -184,9 +203,13 @@ start_server {tags {"modules"}} {
r config resetstat
# simple module command that replies with string error
assert_error
"ERR
U
nknown
Redis
command 'hgetalllll'
.
"
{
r do_rm_call hgetalllll
}
assert_error
"ERR
u
nknown 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
}
...
...
@@ -194,7 +217,7 @@ start_server {tags {"modules"}} {
# 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=
2
}
assert_equal
[
errorrstat ERR r
]
{
count=
3
}
# RM_Call that propagates an error
assert_error
"WRONGTYPE*"
{
r do_rm_call hgetall x
}
...
...
@@ -206,8 +229,8 @@ start_server {tags {"modules"}} {
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
]
5
assert_match
{
*calls=
4
,*,rejected_calls=0,failed_calls=
3
}
[
cmdrstat do_rm_call 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
]
}
...
...
tests/unit/moduleapi/cluster.tcl
View file @
20618c71
...
...
@@ -2,22 +2,6 @@
source tests/support/cli.tcl
proc cluster_info
{
r field
}
{
if
{[
regexp
"^
$field:
(.*?)
\r\n
"
[
$r
cluster info
]
_ value
]}
{
set _ $value
}
}
# Provide easy access to CLUSTER INFO properties. Same semantic as
"proc s"
.
proc csi
{
args
}
{
set level 0
if
{[
string is integer
[
lindex $args 0
]]}
{
set level
[
lindex $args 0
]
set args
[
lrange $args 1 end
]
}
cluster_info
[
srv $level
"client"
]
[
lindex $args 0
]
}
set testmodule
[
file normalize tests/modules/blockonkeys.so
]
set testmodule_nokey
[
file normalize tests/modules/blockonbackground.so
]
set testmodule_blockedclient
[
file normalize tests/modules/blockedclient.so
]
...
...
Prev
1
2
3
4
5
6
7
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment