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
d7971f96
Commit
d7971f96
authored
Apr 05, 2022
by
Oran Agra
Browse files
Merge remote-tracking branch 'origin/unstable' into 7.0
parents
d2b5a579
acfb4f7a
Changes
169
Show whitespace changes
Inline
Side-by-side
tests/cluster/tests/27-endpoints.tcl
View file @
d7971f96
source
"../tests/includes/init-tests.tcl"
# Check if cluster's view of hostnames is consistent
proc are_hostnames_propagated
{
match_string
}
{
for
{
set j 0
}
{
$j
< $::cluster_master_nodes + $::cluster_replica_nodes
}
{
incr j
}
{
set cfg
[
R $j cluster slots
]
foreach node $cfg
{
for
{
set i 2
}
{
$i
<
[
llength $node
]}
{
incr i
}
{
if
{
!
[
string match $match_string
[
lindex
[
lindex
[
lindex $node $i
]
3
]
1
]]
}
{
return 0
}
}
}
}
return 1
}
# Isolate a node from the cluster and give it a new nodeid
proc isolate_node
{
id
}
{
set node_id
[
R $id CLUSTER MYID
]
...
...
@@ -212,6 +197,9 @@ test "Verify the nodes configured with prefer hostname only show hostname for ne
test
"Test restart will keep hostname information"
{
# Set a new hostname, reboot and make sure it sticks
R 0 config set cluster-announce-hostname
"restart-1.com"
# Store the hostname in the config
R 0 config rewrite
kill_instance redis 0
restart_instance redis 0
set slot_result
[
R 0 CLUSTER SLOTS
]
assert_equal
[
lindex
[
get_slot_field $slot_result 0 2 3
]
1
]
"restart-1.com"
...
...
tests/cluster/tests/28-cluster-shards.tcl
0 → 100644
View file @
d7971f96
source
"../tests/includes/init-tests.tcl"
# Initial slot distribution.
set ::slot0
[
list 0 1000 1002 5459 5461 5461 10926 10926
]
set ::slot1
[
list 5460 5460 5462 10922 10925 10925
]
set ::slot2
[
list 10923 10924 10927 16383
]
set ::slot3
[
list 1001 1001
]
proc cluster_create_with_split_slots
{
masters replicas
}
{
for
{
set j 0
}
{
$j
< $masters
}
{
incr j
}
{
R $j cluster ADDSLOTSRANGE
{*}
[
set ::slot$
{
j
}]
}
if
{
$replicas
}
{
cluster_allocate_slaves $masters $replicas
}
set ::cluster_master_nodes $masters
set ::cluster_replica_nodes $replicas
}
# Get the node info with the specific node_id from the
# given reference node. Valid type options are
"node"
and
"shard"
proc get_node_info_from_shard
{
id reference
{
type node
}}
{
set shards_response
[
R $reference CLUSTER SHARDS
]
foreach shard_response $shards_response
{
set nodes
[
dict get $shard_response nodes
]
foreach node $nodes
{
if
{[
dict get $node id
]
eq $id
}
{
if
{
$type
eq
"node"
}
{
return $node
}
elseif
{
$type
eq
"shard"
}
{
return $shard_response
}
else
{
return
{}
}
}
}
}
# No shard found, return nothing
return
{}
}
test
"Create a 8 nodes cluster with 4 shards"
{
cluster_create_with_split_slots 4 4
}
test
"Cluster should start ok"
{
assert_cluster_state ok
}
test
"Set cluster hostnames and verify they are propagated"
{
for
{
set j 0
}
{
$j
< $::cluster_master_nodes + $::cluster_replica_nodes
}
{
incr j
}
{
R $j config set cluster-announce-hostname
"host-
$j.com
"
}
# Wait for everyone to agree about the state
wait_for_cluster_propagation
}
test
"Verify information about the shards"
{
set ids
{}
for
{
set j 0
}
{
$j
< $::cluster_master_nodes + $::cluster_replica_nodes
}
{
incr j
}
{
lappend ids
[
R $j CLUSTER MYID
]
}
set slots
[
list $::slot0 $::slot1 $::slot2 $::slot3 $::slot0 $::slot1 $::slot2 $::slot3
]
# Verify on each node
(
primary/replica
)
, the response of the `CLUSTER SLOTS` command is consistent.
for
{
set ref 0
}
{
$ref
< $::cluster_master_nodes + $::cluster_replica_nodes
}
{
incr ref
}
{
for
{
set i 0
}
{
$i
< $::cluster_master_nodes + $::cluster_replica_nodes
}
{
incr i
}
{
assert_equal
[
lindex $slots $i
]
[
dict get
[
get_node_info_from_shard
[
lindex $ids $i
]
$ref
"shard"
]
slots
]
assert_equal
"host-
$i.com
"
[
dict get
[
get_node_info_from_shard
[
lindex $ids $i
]
$ref
"node"
]
hostname
]
assert_equal
"127.0.0.1"
[
dict get
[
get_node_info_from_shard
[
lindex $ids $i
]
$ref
"node"
]
ip
]
# Default value of 'cluster-preferred-endpoint-type' is ip.
assert_equal
"127.0.0.1"
[
dict get
[
get_node_info_from_shard
[
lindex $ids $i
]
$ref
"node"
]
endpoint
]
if
{
$::tls
}
{
assert_equal
[
get_instance_attrib redis $i plaintext-port
]
[
dict get
[
get_node_info_from_shard
[
lindex $ids $i
]
$ref
"node"
]
port
]
assert_equal
[
get_instance_attrib redis $i port
]
[
dict get
[
get_node_info_from_shard
[
lindex $ids $i
]
$ref
"node"
]
tls-port
]
}
else
{
assert_equal
[
get_instance_attrib redis $i port
]
[
dict get
[
get_node_info_from_shard
[
lindex $ids $i
]
$ref
"node"
]
port
]
}
if
{
$i
< 4
}
{
assert_equal
"master"
[
dict get
[
get_node_info_from_shard
[
lindex $ids $i
]
$ref
"node"
]
role
]
assert_equal
"online"
[
dict get
[
get_node_info_from_shard
[
lindex $ids $i
]
$ref
"node"
]
health
]
}
else
{
assert_equal
"replica"
[
dict get
[
get_node_info_from_shard
[
lindex $ids $i
]
$ref
"node"
]
role
]
# Replica could be in online or loading
}
}
}
}
test
"Verify no slot shard"
{
# Node 8 has no slots assigned
set node_8_id
[
R 8 CLUSTER MYID
]
assert_equal
{}
[
dict get
[
get_node_info_from_shard $node_8_id 8
"shard"
]
slots
]
assert_equal
{}
[
dict get
[
get_node_info_from_shard $node_8_id 0
"shard"
]
slots
]
}
set node_0_id
[
R 0 CLUSTER MYID
]
test
"Kill a node and tell the replica to immediately takeover"
{
kill_instance redis 0
R 4 cluster failover force
}
# Primary 0 node should report as fail, wait until the new primary acknowledges it.
test
"Verify health as fail for killed node"
{
wait_for_condition 50 100
{
"fail"
eq
[
dict get
[
get_node_info_from_shard $node_0_id 4
"node"
]
"health"
]
}
else
{
fail
"New primary never detected the node failed"
}
}
set primary_id 4
set replica_id 0
test
"Restarting primary node"
{
restart_instance redis $replica_id
}
test
"Instance #0 gets converted into a replica"
{
wait_for_condition 1000 50
{
[
RI $replica_id role
]
eq
{
slave
}
}
else
{
fail
"Old primary was not converted into replica"
}
}
test
"Test the replica reports a loading state while it's loading"
{
# Test the command is good for verifying everything moves to a happy state
set replica_cluster_id
[
R $replica_id CLUSTER MYID
]
wait_for_condition 50 1000
{
[
dict get
[
get_node_info_from_shard $replica_cluster_id $primary_id
"node"
]
health
]
eq
"online"
}
else
{
fail
"Replica never transitioned to online"
}
# Set 1 MB of data, so there is something to load on full sync
R $primary_id debug populate 1000 key 1000
# Kill replica client for primary and load new data to the primary
R $primary_id config set repl-backlog-size 100
# Set the key load delay so that it will take at least
# 2 seconds to fully load the data.
R $replica_id config set key-load-delay 4000
# Trigger event loop processing every 1024 bytes, this trigger
# allows us to send and receive cluster messages, so we are setting
# it low so that the cluster messages are sent more frequently.
R $replica_id config set loading-process-events-interval-bytes 1024
R $primary_id multi
R $primary_id client kill type replica
# populate the correct data
set num 100
set value
[
string repeat A 1024
]
for
{
set j 0
}
{
$j
< $num
}
{
incr j
}
{
# Use hashtag valid for shard #0
set key
"{ch3}
$j
"
R $primary_id set $key $value
}
R $primary_id exec
# The replica should reconnect and start a full sync, it will gossip about it's health to the primary.
wait_for_condition 50 1000
{
"loading"
eq
[
dict get
[
get_node_info_from_shard $replica_cluster_id $primary_id
"node"
]
health
]
}
else
{
fail
"Replica never transitioned to loading"
}
# Speed up the key loading and verify everything resumes
R $replica_id config set key-load-delay 0
wait_for_condition 50 1000
{
"online"
eq
[
dict get
[
get_node_info_from_shard $replica_cluster_id $primary_id
"node"
]
health
]
}
else
{
fail
"Replica never transitioned to online"
}
# Final sanity, the replica agrees it is online.
assert_equal
"online"
[
dict get
[
get_node_info_from_shard $replica_cluster_id $replica_id
"node"
]
health
]
}
\ No newline at end of file
tests/integration/redis-cli.tcl
View file @
d7971f96
...
...
@@ -228,6 +228,30 @@ start_server {tags {"cli"}} {
file delete $tmpfile
}
test_tty_cli
"Escape character in JSON mode"
{
# reverse solidus
r hset solidus
\/
\/
assert_equal
\/
\/
[
run_cli hgetall solidus
]
set escaped_reverse_solidus
\"\\
"
assert_equal
$escaped
_reverse_solidus
$escaped
_reverse_solidus
[
run_cli --json hgetall
\/
]
# non printable (0xF0 in ISO-8859-1, not UTF-8(0xC3 0xB0))
set eth "
\u00f0\u0065
"
r hset eth test
$eth
assert_equal
\"\\
xf0e
\"
[
run_cli hget eth test
]
assert_equal
\"\u00f0
e
\"
[
run_cli --json hget eth test
]
assert_equal
\"\\\\
xf0e
\"
[
run_cli --quoted-json hget eth test
]
# control characters
r hset control test "
Hello
\x00\x01\x02\x03
World
"
assert_equal
\"
Hello
\\
u0000
\\
u0001
\\
u0002
\\
u0003World"
[
run_cli --json hget control test
]
# non-string keys
r hset numkey 1 One
assert_equal
\{\"
1
\"
:
\"
One
\"\}
[
run_cli --json hgetall numkey
]
# non-string, non-printable keys
r hset npkey
"K
\u0000\u0001
ey"
"V
\u0000\u0001
alue"
assert_equal
\{\"
K
\\
u0000
\\
u0001ey
\"
:
\"
V
\\
u0000
\\
u0001alue
\"\}
[
run_cli --json hgetall npkey
]
assert_equal
\{\"
K
\\\\
x00
\\\\
x01ey
\"
:
\"
V
\\\\
x00
\\\\
x01alue
\"\}
[
run_cli --quoted-json hgetall npkey
]
}
test_nontty_cli
"Status reply"
{
assert_equal
"OK"
[
run_cli set key bar
]
assert_equal
"bar"
[
r get key
]
...
...
@@ -322,7 +346,7 @@ if {!$::tls} { ;# fake_redis_node doesn't support TLS
set dir
[
lindex
[
r config get dir
]
1
]
assert_equal
"OK"
[
r debug populate 100000 key 1000
]
assert_equal
"
OK
"
[
r function load lua lib1
"
redis.register_function('func1', function() return 123 end)"
]
assert_equal
"
lib1
"
[
r function load
"#!
lua
name=
lib1
\n
redis.register_function('func1', function() return 123 end)"
]
if
{
$functions
_only
}
{
set args
"--functions-rdb
$dir
/cli.rdb"
}
else
{
...
...
@@ -335,10 +359,10 @@ if {!$::tls} { ;# fake_redis_node doesn't support TLS
file rename
"
$dir
/cli.rdb"
"
$dir
/dump.rdb"
assert_equal
"OK"
[
r set should-not-exist 1
]
assert_equal
"
OK
"
[
r function load lua should_not_exist_func
"
redis.register_function('should_not_exist_func', function() return 456 end)"
]
assert_equal
"
should_not_exist_func
"
[
r function load
"#!
lua
name=
should_not_exist_func
\n
redis.register_function('should_not_exist_func', function() return 456 end)"
]
assert_equal
"OK"
[
r debug reload nosave
]
assert_equal
{}
[
r get should-not-exist
]
assert_equal
{{
library_name lib1 engine LUA
description
{}
functions
{{
name func1 description
{}
flags
{}}}}}
[
r function list
]
assert_equal
{{
library_name lib1 engine LUA functions
{{
name func1 description
{}
flags
{}}}}}
[
r function list
]
if
{
$functions
_only
}
{
assert_equal 0
[
r dbsize
]
}
else
{
...
...
tests/integration/replication-4.tcl
View file @
d7971f96
...
...
@@ -47,7 +47,7 @@ start_server {tags {"repl external:skip"}} {
set slave
[
srv 0 client
]
# Load some functions to be used later
$master FUNCTION load
lua test
replace
{
$master FUNCTION load replace
{
#!lua name=test
redis.register_function
{
function_name='f_default_flags', callback=function
(
keys, args
)
return redis.call
(
'get',keys
[
1
])
end, flags=
{}}
redis.register_function
{
function_name='f_no_writes', callback=function
(
keys, args
)
return redis.call
(
'get',keys
[
1
])
end, flags=
{
'no-writes'
}}
}
...
...
tests/integration/replication.tcl
View file @
d7971f96
...
...
@@ -523,10 +523,14 @@ foreach testType {Successful Aborted} {
$replica set mykey myvalue
# Set a function value on replica to check status during loading, on failure and after swapping db
$replica function load LUA test
{
redis.register_function
(
'test', function
()
return 'hello1' end
)}
$replica function load
{
#!lua name=test
redis.register_function
(
'test', function
()
return 'hello1' end
)
}
# Set a function value on master to check it reaches the replica when replication ends
$master function load LUA test
{
redis.register_function
(
'test', function
()
return 'hello2' end
)}
$master function load
{
#!lua name=test
redis.register_function
(
'test', function
()
return 'hello2' end
)
}
# Force the replica to try another full sync
(
this time it will have matching master replid
)
$master multi
...
...
@@ -659,7 +663,9 @@ test {diskless loading short read} {
set start
[
clock clicks -milliseconds
]
# Set a function value to check short read handling on functions
r function load LUA test
{
redis.register_function
(
'test', function
()
return 'hello1' end
)}
r function load
{
#!lua name=test
redis.register_function
(
'test', function
()
return 'hello1' end
)
}
for
{
set k 0
}
{
$k
< 3
}
{
incr k
}
{
for
{
set i 0
}
{
$i
< 10
}
{
incr i
}
{
...
...
tests/modules/Makefile
View file @
d7971f96
...
...
@@ -54,7 +54,9 @@ TEST_MODULES = \
subcommands.so
\
reply.so
\
cmdintrospection.so
\
eventloop.so
eventloop.so
\
moduleconfigs.so
\
moduleconfigstwo.so
.PHONY
:
all
...
...
tests/modules/aclcheck.c
View file @
d7971f96
...
...
@@ -136,6 +136,22 @@ int rm_call_aclcheck_cmd_module_user(RedisModuleCtx *ctx, RedisModuleString **ar
return
res
;
}
int
rm_call_aclcheck_with_errors
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
){
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
argc
<
2
){
return
RedisModule_WrongArity
(
ctx
);
}
const
char
*
cmd
=
RedisModule_StringPtrLen
(
argv
[
1
],
NULL
);
RedisModuleCallReply
*
rep
=
RedisModule_Call
(
ctx
,
cmd
,
"vEC"
,
argv
+
2
,
argc
-
2
);
RedisModule_ReplyWithCallReply
(
ctx
,
rep
);
RedisModule_FreeCallReply
(
rep
);
return
REDISMODULE_OK
;
}
/* A wrap for RM_Call that pass the 'C' flag to do ACL check on the command. */
int
rm_call_aclcheck
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
){
REDISMODULE_NOT_USED
(
argv
);
...
...
@@ -190,5 +206,9 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
"write"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"aclcheck.rm_call_with_errors"
,
rm_call_aclcheck_with_errors
,
"write"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
tests/modules/auth.c
View file @
d7971f96
...
...
@@ -54,6 +54,16 @@ int Auth_AuthRealUser(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
return
RedisModule_ReplyWithLongLong
(
ctx
,
(
uint64_t
)
client_id
);
}
/* This command redacts every other arguments and returns OK */
int
Auth_RedactedAPI
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
for
(
int
i
=
argc
-
1
;
i
>
0
;
i
-=
2
)
{
int
result
=
RedisModule_RedactClientCommandArgument
(
ctx
,
i
);
RedisModule_Assert
(
result
==
REDISMODULE_OK
);
}
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
}
int
Auth_ChangeCount
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
...
...
@@ -87,6 +97,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
Auth_ChangeCount
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"auth.redact"
,
Auth_RedactedAPI
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
...
...
tests/modules/basics.c
View file @
d7971f96
...
...
@@ -718,6 +718,25 @@ end:
/* Return 1 if the reply matches the specified string, otherwise log errors
* in the server log and return 0. */
int
TestAssertErrorReply
(
RedisModuleCtx
*
ctx
,
RedisModuleCallReply
*
reply
,
char
*
str
,
size_t
len
)
{
RedisModuleString
*
mystr
,
*
expected
;
if
(
RedisModule_CallReplyType
(
reply
)
!=
REDISMODULE_REPLY_ERROR
)
{
return
0
;
}
mystr
=
RedisModule_CreateStringFromCallReply
(
reply
);
expected
=
RedisModule_CreateString
(
ctx
,
str
,
len
);
if
(
RedisModule_StringCompare
(
mystr
,
expected
)
!=
0
)
{
const
char
*
mystr_ptr
=
RedisModule_StringPtrLen
(
mystr
,
NULL
);
const
char
*
expected_ptr
=
RedisModule_StringPtrLen
(
expected
,
NULL
);
RedisModule_Log
(
ctx
,
"warning"
,
"Unexpected Error reply reply '%s' (instead of '%s')"
,
mystr_ptr
,
expected_ptr
);
return
0
;
}
return
1
;
}
int
TestAssertStringReply
(
RedisModuleCtx
*
ctx
,
RedisModuleCallReply
*
reply
,
char
*
str
,
size_t
len
)
{
RedisModuleString
*
mystr
,
*
expected
;
...
...
@@ -846,6 +865,18 @@ int TestBasics(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if
(
!
TestAssertStringReply
(
ctx
,
RedisModule_CallReplyArrayElement
(
reply
,
0
),
"test"
,
4
))
goto
fail
;
if
(
!
TestAssertStringReply
(
ctx
,
RedisModule_CallReplyArrayElement
(
reply
,
1
),
"1234"
,
4
))
goto
fail
;
T
(
"foo"
,
"E"
);
if
(
!
TestAssertErrorReply
(
ctx
,
reply
,
"ERR Unknown Redis command 'foo'."
,
32
))
goto
fail
;
T
(
"set"
,
"Ec"
,
"x"
);
if
(
!
TestAssertErrorReply
(
ctx
,
reply
,
"ERR Wrong number of args calling Redis command 'set'."
,
53
))
goto
fail
;
T
(
"shutdown"
,
"SE"
);
if
(
!
TestAssertErrorReply
(
ctx
,
reply
,
"ERR command 'shutdown' is not allowed on script mode"
,
52
))
goto
fail
;
T
(
"set"
,
"WEcc"
,
"x"
,
"1"
);
if
(
!
TestAssertErrorReply
(
ctx
,
reply
,
"ERR Write command 'set' was called while write is not allowed."
,
62
))
goto
fail
;
RedisModule_ReplyWithSimpleString
(
ctx
,
"ALL TESTS PASSED"
);
return
REDISMODULE_OK
;
...
...
tests/modules/blockedclient.c
View file @
d7971f96
...
...
@@ -195,7 +195,7 @@ int do_rm_call(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
const
char
*
cmd
=
RedisModule_StringPtrLen
(
argv
[
1
],
NULL
);
RedisModuleCallReply
*
rep
=
RedisModule_Call
(
ctx
,
cmd
,
"v"
,
argv
+
2
,
argc
-
2
);
RedisModuleCallReply
*
rep
=
RedisModule_Call
(
ctx
,
cmd
,
"
E
v"
,
argv
+
2
,
argc
-
2
);
if
(
!
rep
){
RedisModule_ReplyWithError
(
ctx
,
"NULL reply returned"
);
}
else
{
...
...
tests/modules/blockonkeys.c
View file @
d7971f96
...
...
@@ -135,22 +135,29 @@ int bpop_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int arg
return
RedisModule_ReplyWithSimpleString
(
ctx
,
"Request timedout"
);
}
/* FSL.BPOP <key> <timeout> - Block clients until list has two or more elements.
/* FSL.BPOP <key> <timeout>
[NO_TO_CB]
- Block clients until list has two or more elements.
* When that happens, unblock client and pop the last two elements (from the right). */
int
fsl_bpop
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
3
)
if
(
argc
<
3
)
return
RedisModule_WrongArity
(
ctx
);
long
long
timeout
;
if
(
RedisModule_StringToLongLong
(
argv
[
2
],
&
timeout
)
!=
REDISMODULE_OK
||
timeout
<
0
)
return
RedisModule_ReplyWithError
(
ctx
,
"ERR invalid timeout"
);
int
to_cb
=
1
;
if
(
argc
==
4
)
{
if
(
strcasecmp
(
"NO_TO_CB"
,
RedisModule_StringPtrLen
(
argv
[
3
],
NULL
)))
return
RedisModule_ReplyWithError
(
ctx
,
"ERR invalid argument"
);
to_cb
=
0
;
}
fsl_t
*
fsl
;
if
(
!
get_fsl
(
ctx
,
argv
[
1
],
REDISMODULE_READ
,
0
,
&
fsl
,
1
))
return
REDISMODULE_OK
;
if
(
!
fsl
)
{
RedisModule_BlockClientOnKeys
(
ctx
,
bpop_reply_callback
,
bpop_timeout_callback
,
RedisModule_BlockClientOnKeys
(
ctx
,
bpop_reply_callback
,
to_cb
?
bpop_timeout_callback
:
NULL
,
NULL
,
timeout
,
&
argv
[
1
],
1
,
NULL
);
}
else
{
RedisModule_ReplyWithLongLong
(
ctx
,
fsl
->
list
[
--
fsl
->
length
]);
...
...
tests/modules/eventloop.c
View file @
d7971f96
...
...
@@ -11,7 +11,6 @@
* 4- test.oneshot : Test for oneshot API
*/
#define REDISMODULE_EXPERIMENTAL_API
#include "redismodule.h"
#include <stdlib.h>
#include <unistd.h>
...
...
tests/modules/hooks.c
View file @
d7971f96
...
...
@@ -267,6 +267,18 @@ void swapDbCallback(RedisModuleCtx *ctx, RedisModuleEvent e, uint64_t sub, void
LogNumericEvent
(
ctx
,
"swapdb-second"
,
ei
->
dbnum_second
);
}
void
configChangeCallback
(
RedisModuleCtx
*
ctx
,
RedisModuleEvent
e
,
uint64_t
sub
,
void
*
data
)
{
REDISMODULE_NOT_USED
(
e
);
if
(
sub
!=
REDISMODULE_SUBEVENT_CONFIG_CHANGE
)
{
return
;
}
RedisModuleConfigChangeV1
*
ei
=
data
;
LogNumericEvent
(
ctx
,
"config-change-count"
,
ei
->
num_changes
);
LogStringEvent
(
ctx
,
"config-change-first"
,
ei
->
config_names
[
0
]);
}
/* This function must be present on each Redis module. It is used in order to
* register the commands into the Redis server. */
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
...
...
@@ -317,6 +329,9 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
RedisModule_SubscribeToServerEvent
(
ctx
,
RedisModuleEvent_SwapDB
,
swapDbCallback
);
RedisModule_SubscribeToServerEvent
(
ctx
,
RedisModuleEvent_Config
,
configChangeCallback
);
event_log
=
RedisModule_CreateDict
(
ctx
);
if
(
RedisModule_CreateCommand
(
ctx
,
"hooks.event_count"
,
cmdEventCount
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
...
...
tests/modules/moduleconfigs.c
0 → 100644
View file @
d7971f96
#include "redismodule.h"
#include <strings.h>
int
mutable_bool_val
;
int
immutable_bool_val
;
long
long
longval
;
long
long
memval
;
RedisModuleString
*
strval
=
NULL
;
int
enumval
;
/* 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
* use names if they wanted, and store anything in privdata. */
int
getBoolConfigCommand
(
const
char
*
name
,
void
*
privdata
)
{
REDISMODULE_NOT_USED
(
name
);
return
(
*
(
int
*
)
privdata
);
}
int
setBoolConfigCommand
(
const
char
*
name
,
int
new
,
void
*
privdata
,
RedisModuleString
**
err
)
{
REDISMODULE_NOT_USED
(
name
);
REDISMODULE_NOT_USED
(
err
);
*
(
int
*
)
privdata
=
new
;
return
REDISMODULE_OK
;
}
long
long
getNumericConfigCommand
(
const
char
*
name
,
void
*
privdata
)
{
REDISMODULE_NOT_USED
(
name
);
return
(
*
(
long
long
*
)
privdata
);
}
int
setNumericConfigCommand
(
const
char
*
name
,
long
long
new
,
void
*
privdata
,
RedisModuleString
**
err
)
{
REDISMODULE_NOT_USED
(
name
);
REDISMODULE_NOT_USED
(
err
);
*
(
long
long
*
)
privdata
=
new
;
return
REDISMODULE_OK
;
}
RedisModuleString
*
getStringConfigCommand
(
const
char
*
name
,
void
*
privdata
)
{
REDISMODULE_NOT_USED
(
name
);
REDISMODULE_NOT_USED
(
privdata
);
return
strval
;
}
int
setStringConfigCommand
(
const
char
*
name
,
RedisModuleString
*
new
,
void
*
privdata
,
RedisModuleString
**
err
)
{
REDISMODULE_NOT_USED
(
name
);
REDISMODULE_NOT_USED
(
err
);
REDISMODULE_NOT_USED
(
privdata
);
size_t
len
;
if
(
!
strcasecmp
(
RedisModule_StringPtrLen
(
new
,
&
len
),
"rejectisfreed"
))
{
*
err
=
RedisModule_CreateString
(
NULL
,
"Cannot set string to 'rejectisfreed'"
,
36
);
return
REDISMODULE_ERR
;
}
if
(
strval
)
RedisModule_FreeString
(
NULL
,
strval
);
RedisModule_RetainString
(
NULL
,
new
);
strval
=
new
;
return
REDISMODULE_OK
;
}
int
getEnumConfigCommand
(
const
char
*
name
,
void
*
privdata
)
{
REDISMODULE_NOT_USED
(
name
);
REDISMODULE_NOT_USED
(
privdata
);
return
enumval
;
}
int
setEnumConfigCommand
(
const
char
*
name
,
int
val
,
void
*
privdata
,
RedisModuleString
**
err
)
{
REDISMODULE_NOT_USED
(
name
);
REDISMODULE_NOT_USED
(
err
);
REDISMODULE_NOT_USED
(
privdata
);
enumval
=
val
;
return
REDISMODULE_OK
;
}
int
boolApplyFunc
(
RedisModuleCtx
*
ctx
,
void
*
privdata
,
RedisModuleString
**
err
)
{
REDISMODULE_NOT_USED
(
ctx
);
REDISMODULE_NOT_USED
(
privdata
);
if
(
mutable_bool_val
&&
immutable_bool_val
)
{
*
err
=
RedisModule_CreateString
(
NULL
,
"Bool configs cannot both be yes."
,
32
);
return
REDISMODULE_ERR
;
}
return
REDISMODULE_OK
;
}
int
longlongApplyFunc
(
RedisModuleCtx
*
ctx
,
void
*
privdata
,
RedisModuleString
**
err
)
{
REDISMODULE_NOT_USED
(
ctx
);
REDISMODULE_NOT_USED
(
privdata
);
if
(
longval
==
memval
)
{
*
err
=
RedisModule_CreateString
(
NULL
,
"These configs cannot equal each other."
,
38
);
return
REDISMODULE_ERR
;
}
return
REDISMODULE_OK
;
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"moduleconfigs"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_RegisterBoolConfig
(
ctx
,
"mutable_bool"
,
1
,
REDISMODULE_CONFIG_DEFAULT
,
getBoolConfigCommand
,
setBoolConfigCommand
,
boolApplyFunc
,
&
mutable_bool_val
)
==
REDISMODULE_ERR
)
{
return
REDISMODULE_ERR
;
}
/* Immutable config here. */
if
(
RedisModule_RegisterBoolConfig
(
ctx
,
"immutable_bool"
,
0
,
REDISMODULE_CONFIG_IMMUTABLE
,
getBoolConfigCommand
,
setBoolConfigCommand
,
boolApplyFunc
,
&
immutable_bool_val
)
==
REDISMODULE_ERR
)
{
return
REDISMODULE_ERR
;
}
if
(
RedisModule_RegisterStringConfig
(
ctx
,
"string"
,
"secret password"
,
REDISMODULE_CONFIG_DEFAULT
,
getStringConfigCommand
,
setStringConfigCommand
,
NULL
,
NULL
)
==
REDISMODULE_ERR
)
{
return
REDISMODULE_ERR
;
}
/* 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
};
if
(
RedisModule_RegisterEnumConfig
(
ctx
,
"enum"
,
0
,
REDISMODULE_CONFIG_DEFAULT
,
enum_vals
,
int_vals
,
3
,
getEnumConfigCommand
,
setEnumConfigCommand
,
NULL
,
NULL
)
==
REDISMODULE_ERR
)
{
return
REDISMODULE_ERR
;
}
/* Memory config here. */
if
(
RedisModule_RegisterNumericConfig
(
ctx
,
"memory_numeric"
,
1024
,
REDISMODULE_CONFIG_DEFAULT
|
REDISMODULE_CONFIG_MEMORY
,
0
,
3000000
,
getNumericConfigCommand
,
setNumericConfigCommand
,
longlongApplyFunc
,
&
memval
)
==
REDISMODULE_ERR
)
{
return
REDISMODULE_ERR
;
}
if
(
RedisModule_RegisterNumericConfig
(
ctx
,
"numeric"
,
-
1
,
REDISMODULE_CONFIG_DEFAULT
,
-
5
,
2000
,
getNumericConfigCommand
,
setNumericConfigCommand
,
longlongApplyFunc
,
&
longval
)
==
REDISMODULE_ERR
)
{
return
REDISMODULE_ERR
;
}
size_t
len
;
if
(
argc
&&
!
strcasecmp
(
RedisModule_StringPtrLen
(
argv
[
0
],
&
len
),
"noload"
))
{
return
REDISMODULE_OK
;
}
else
if
(
RedisModule_LoadConfigs
(
ctx
)
==
REDISMODULE_ERR
)
{
if
(
strval
)
{
RedisModule_FreeString
(
ctx
,
strval
);
strval
=
NULL
;
}
return
REDISMODULE_ERR
;
}
return
REDISMODULE_OK
;
}
int
RedisModule_OnUnload
(
RedisModuleCtx
*
ctx
)
{
REDISMODULE_NOT_USED
(
ctx
);
if
(
strval
)
{
RedisModule_FreeString
(
ctx
,
strval
);
strval
=
NULL
;
}
return
REDISMODULE_OK
;
}
\ No newline at end of file
tests/modules/moduleconfigstwo.c
0 → 100644
View file @
d7971f96
#include "redismodule.h"
#include <strings.h>
/* Second module configs module, for testing.
* Need to make sure that multiple modules with configs don't interfere with each other */
int
bool_config
;
int
getBoolConfigCommand
(
const
char
*
name
,
void
*
privdata
)
{
REDISMODULE_NOT_USED
(
privdata
);
if
(
!
strcasecmp
(
name
,
"test"
))
{
return
bool_config
;
}
return
0
;
}
int
setBoolConfigCommand
(
const
char
*
name
,
int
new
,
void
*
privdata
,
RedisModuleString
**
err
)
{
REDISMODULE_NOT_USED
(
privdata
);
REDISMODULE_NOT_USED
(
err
);
if
(
!
strcasecmp
(
name
,
"test"
))
{
bool_config
=
new
;
return
REDISMODULE_OK
;
}
return
REDISMODULE_ERR
;
}
/* No arguments are expected */
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"configs"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_RegisterBoolConfig
(
ctx
,
"test"
,
1
,
REDISMODULE_CONFIG_DEFAULT
,
getBoolConfigCommand
,
setBoolConfigCommand
,
NULL
,
&
argc
)
==
REDISMODULE_ERR
)
{
return
REDISMODULE_ERR
;
}
if
(
RedisModule_LoadConfigs
(
ctx
)
==
REDISMODULE_ERR
)
{
return
REDISMODULE_ERR
;
}
return
REDISMODULE_OK
;
}
\ No newline at end of file
tests/sentinel/tests/03-runtime-reconf.tcl
View file @
d7971f96
...
...
@@ -2,6 +2,162 @@
source
"../tests/includes/init-tests.tcl"
set num_sentinels
[
llength $::sentinel_instances
]
set ::user
"testuser"
set ::password
"secret"
proc server_set_password
{}
{
foreach_redis_id id
{
assert_equal
{
OK
}
[
R $id CONFIG SET requirepass $::password
]
assert_equal
{
OK
}
[
R $id AUTH $::password
]
assert_equal
{
OK
}
[
R $id CONFIG SET masterauth $::password
]
}
}
proc server_reset_password
{}
{
foreach_redis_id id
{
assert_equal
{
OK
}
[
R $id CONFIG SET requirepass
""
]
assert_equal
{
OK
}
[
R $id CONFIG SET masterauth
""
]
}
}
proc server_set_acl
{
id
}
{
assert_equal
{
OK
}
[
R $id ACL SETUSER $::user on >$::password allchannels +@all
]
assert_equal
{
OK
}
[
R $id ACL SETUSER default off
]
R $id CLIENT KILL USER default SKIPME no
assert_equal
{
OK
}
[
R $id AUTH $::user $::password
]
assert_equal
{
OK
}
[
R $id CONFIG SET masteruser $::user
]
assert_equal
{
OK
}
[
R $id CONFIG SET masterauth $::password
]
}
proc server_reset_acl
{
id
}
{
assert_equal
{
OK
}
[
R $id ACL SETUSER default on
]
assert_equal
{
1
}
[
R $id ACL DELUSER $::user
]
assert_equal
{
OK
}
[
R $id CONFIG SET masteruser
""
]
assert_equal
{
OK
}
[
R $id CONFIG SET masterauth
""
]
}
proc verify_sentinel_connect_replicas
{
id
}
{
foreach replica
[
S $id SENTINEL REPLICAS mymaster
]
{
if
{[
string match
"*disconnected*"
[
dict get $replica flags
]]}
{
return 0
}
}
return 1
}
proc wait_for_sentinels_connect_servers
{
{
is_connect 1
}
}
{
foreach_sentinel_id id
{
wait_for_condition 1000 50
{
[
string match
"*disconnected*"
[
dict get
[
S $id SENTINEL MASTER mymaster
]
flags
]]
!= $is_connect
}
else
{
fail
"At least some sentinel can't connect to master"
}
wait_for_condition 1000 50
{
[
verify_sentinel_connect_replicas $id
]
== $is_connect
}
else
{
fail
"At least some sentinel can't connect to replica"
}
}
}
test
"Sentinels (re)connection following SENTINEL SET mymaster auth-pass"
{
# 3 types of sentinels to test:
#
(
re
)
started while master changed pwd. Manage to connect only after setting pwd
set sent2re 0
#
(
up
)
dated in advance with master new password
set sent2up 1
#
(
un
)
touched. Yet manage to maintain
(
old
)
connection
set sent2un 2
wait_for_sentinels_connect_servers
kill_instance sentinel $sent2re
server_set_password
assert_equal
{
OK
}
[
S $sent2up SENTINEL SET mymaster auth-pass $::password
]
restart_instance sentinel $sent2re
# Verify sentinel that restarted failed to connect master
if
{
!
[
string match
"*disconnected*"
[
dict get
[
S $sent2re SENTINEL MASTER mymaster
]
flags
]]}
{
fail
"Expected to be disconnected from master due to wrong password"
}
# Update restarted sentinel with master password
assert_equal
{
OK
}
[
S $sent2re SENTINEL SET mymaster auth-pass $::password
]
# All sentinels expected to connect successfully
wait_for_sentinels_connect_servers
# remove requirepass and verify sentinels manage to connect servers
server_reset_password
wait_for_sentinels_connect_servers
# Sanity check
verify_sentinel_auto_discovery
}
test
"Sentinels (re)connection following master ACL change"
{
# Three types of sentinels to test during ACL change:
# 1.
(
re
)
started Sentinel. Manage to connect only after setting new pwd
# 2.
(
up
)
dated Sentinel, get just before ACL change the new password
# 3.
(
un
)
touched Sentinel that kept old connection with master and didn't
# set new ACL password won't persist ACL pwd change
(
unlike legacy auth-pass
)
set sent2re 0
set sent2up 1
set sent2un 2
wait_for_sentinels_connect_servers
# kill sentinel 'sent2re' and restart it after ACL change
kill_instance sentinel $sent2re
# Update sentinel 'sent2up' with new user and pwd
assert_equal
{
OK
}
[
S $sent2up SENTINEL SET mymaster auth-user $::user
]
assert_equal
{
OK
}
[
S $sent2up SENTINEL SET mymaster auth-pass $::password
]
foreach_redis_id id
{
server_set_acl $id
}
restart_instance sentinel $sent2re
# Verify sentinel that restarted failed to reconnect master
wait_for_condition 100 50
{
[
string match
"*disconnected*"
[
dict get
[
S $sent2re SENTINEL MASTER mymaster
]
flags
]]
!= 0
}
else
{
fail
"Expected: Sentinel to be disconnected from master due to wrong password"
}
# Verify sentinel with updated password managed to connect
(
wait for sentinelTimer to reconnect
)
wait_for_condition 100 50
{
[
string match
"*disconnected*"
[
dict get
[
S $sent2up SENTINEL MASTER mymaster
]
flags
]]
== 0
}
else
{
fail
"Expected: Sentinel to be connected to master"
}
# Verify sentinel untouched gets failed to connect master
if
{
!
[
string match
"*disconnected*"
[
dict get
[
S $sent2un SENTINEL MASTER mymaster
]
flags
]]}
{
fail
"Expected: Sentinel to be disconnected from master due to wrong password"
}
# Now update all sentinels with new password
foreach_sentinel_id id
{
assert_equal
{
OK
}
[
S $id SENTINEL SET mymaster auth-user $::user
]
assert_equal
{
OK
}
[
S $id SENTINEL SET mymaster auth-pass $::password
]
}
# All sentinels expected to connect successfully
wait_for_sentinels_connect_servers
# remove requirepass and verify sentinels manage to connect servers
foreach_redis_id id
{
server_reset_acl $id
}
wait_for_sentinels_connect_servers
# Sanity check
verify_sentinel_auto_discovery
}
test
"Set parameters in normal case"
{
set info
[
S 0 SENTINEL master mymaster
]
...
...
tests/sentinel/tests/07-down-conditions.tcl
View file @
d7971f96
...
...
@@ -24,7 +24,6 @@ proc ensure_master_up {} {
}
}
proc ensure_master_down
{}
{
S $::alive_sentinel sentinel debug info-period 1000
S $::alive_sentinel sentinel debug ping-period 100
...
...
@@ -45,10 +44,14 @@ test "Crash the majority of Sentinels to prevent failovers for this unit" {
}
test
"SDOWN is triggered by non-responding but not crashed instance"
{
lassign
[
S $::alive_sentinel SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
host port
ensure_master_up
exec ../../../src/redis-cli -h $host -p $port
{*}
[
rediscli_tls_config
"../../../tests"
]
debug sleep 3 > /dev/null &
set master_addr
[
S $::alive_sentinel SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
set master_id
[
get_instance_id_by_port redis
[
lindex $master_addr 1
]]
set pid
[
get_instance_attrib redis $master_id pid
]
exec kill -SIGSTOP $pid
ensure_master_down
exec kill -SIGCONT $pid
ensure_master_up
}
...
...
tests/sentinel/tests/08-hostname-conf.tcl
View file @
d7971f96
source
"../tests/includes/utils.tcl"
proc set_redis_announce_ip
{
addr
}
{
foreach_redis_id id
{
R $id config set replica-announce-ip $addr
...
...
tests/sentinel/tests/09-acl-support.tcl
View file @
d7971f96
...
...
@@ -31,16 +31,23 @@ test "(post-init) Set up ACL configuration" {
test
"SENTINEL CONFIG SET handles on-the-fly credentials reconfiguration"
{
# Make sure we're starting with a broken state...
after 5000
catch
{
S 1 SENTINEL CKQUORUM mymaster
}
err
assert_match
{
*NOQUORUM*
}
$err
wait_for_condition 200 50
{
[
catch
{
S 1 SENTINEL CKQUORUM mymaster
}]
== 1
}
else
{
fail
"Expected: Sentinel to be disconnected from master due to wrong password"
}
assert_error
"*NOQUORUM*"
{
S 1 SENTINEL CKQUORUM mymaster
}
foreach_sentinel_id id
{
assert_equal
{
OK
}
[
S $id SENTINEL CONFIG SET sentinel-user $::user
]
assert_equal
{
OK
}
[
S $id SENTINEL CONFIG SET sentinel-pass $::password
]
}
after 5000
wait_for_condition 200 50
{
[
catch
{
S 1 SENTINEL CKQUORUM mymaster
}]
== 0
}
else
{
fail
"Expected: Sentinel to be connected to master after setting password"
}
assert_match
{
*OK*
}
[
S 1 SENTINEL CKQUORUM mymaster
]
}
...
...
tests/sentinel/tests/includes/init-tests.tcl
View file @
d7971f96
# Initialization tests -- most units will start including this.
proc restart_killed_instances
{}
{
foreach type
{
redis sentinel
}
{
foreach_$
{
type
}
_id id
{
if
{[
get_instance_attrib $type $id pid
]
== -1
}
{
puts -nonewline
"
$type
/
$id
"
flush stdout
restart_instance $type $id
}
}
}
}
source
"../tests/includes/utils.tcl"
test
"(init) Restart killed instances"
{
restart_killed_instances
...
...
@@ -59,14 +48,7 @@ test "(init) Sentinels can talk with the master" {
}
test
"(init) Sentinels are able to auto-discover other sentinels"
{
set sentinels
[
llength $::sentinel_instances
]
foreach_sentinel_id id
{
wait_for_condition 1000 50
{
[
dict get
[
S $id SENTINEL MASTER mymaster
]
num-other-sentinels
]
==
(
$sentinels-1
)
}
else
{
fail
"At least some sentinel can't detect some other sentinel"
}
}
verify_sentinel_auto_discovery
}
test
"(init) Sentinels are able to auto-discover slaves"
{
...
...
Prev
1
…
3
4
5
6
7
8
9
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