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
f5ca1f9e
Commit
f5ca1f9e
authored
Apr 19, 2021
by
Oran Agra
Browse files
Merge unstable into 6.2
parents
92bde124
61d3fdb4
Changes
117
Show whitespace changes
Inline
Side-by-side
tests/cluster/tests/includes/utils.tcl
0 → 100644
View file @
f5ca1f9e
source
"../../../tests/support/cli.tcl"
proc config_set_all_nodes
{
keyword value
}
{
foreach_redis_id id
{
R $id config set $keyword $value
}
}
proc fix_cluster
{
addr
}
{
set code
[
catch
{
exec ../../../src/redis-cli
{*}
[
rediscli_tls_config
"../../../tests"
]
--cluster fix $addr << yes
}
result
]
if
{
$code
!= 0
}
{
puts
"redis-cli --cluster fix returns non-zero exit code, output below:
\n
$result
"
}
# Note: redis-cli --cluster fix may return a non-zero exit code if nodes don't agree,
# but we can ignore that and rely on the check below.
assert_cluster_state ok
wait_for_condition 100 100
{
[
catch
{
exec ../../../src/redis-cli
{*}
[
rediscli_tls_config
"../../../tests"
]
--cluster check $addr
}
result
]
== 0
}
else
{
puts
"redis-cli --cluster check returns non-zero exit code, output below:
\n
$result
"
fail
"Cluster could not settle with configuration"
}
}
tests/instances.tcl
View file @
f5ca1f9e
...
@@ -64,6 +64,8 @@ proc exec_instance {type dirname cfgfile} {
...
@@ -64,6 +64,8 @@ proc exec_instance {type dirname cfgfile} {
proc spawn_instance
{
type base_port count
{
conf
{}}
{
base_conf_file
""
}}
{
proc spawn_instance
{
type base_port count
{
conf
{}}
{
base_conf_file
""
}}
{
for
{
set j 0
}
{
$j
< $count
}
{
incr j
}
{
for
{
set j 0
}
{
$j
< $count
}
{
incr j
}
{
set port
[
find_available_port $base_port $::redis_port_count
]
set port
[
find_available_port $base_port $::redis_port_count
]
# plaintext port
(
only used for TLS cluster
)
set pport 0
# Create a directory for this instance.
# Create a directory for this instance.
set dirname
"
${type}
_
${j}
"
set dirname
"
${type}
_
${j}
"
lappend ::dirs $dirname
lappend ::dirs $dirname
...
@@ -83,7 +85,9 @@ proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} {
...
@@ -83,7 +85,9 @@ proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} {
puts $cfg
"tls-port
$port
"
puts $cfg
"tls-port
$port
"
puts $cfg
"tls-replication yes"
puts $cfg
"tls-replication yes"
puts $cfg
"tls-cluster yes"
puts $cfg
"tls-cluster yes"
puts $cfg
"port 0"
# plaintext port, only used by plaintext clients in a TLS cluster
set pport
[
find_available_port $base_port $::redis_port_count
]
puts $cfg
"port
$pport
"
puts $cfg
[
format
"tls-cert-file %s/../../tls/server.crt"
[
pwd
]]
puts $cfg
[
format
"tls-cert-file %s/../../tls/server.crt"
[
pwd
]]
puts $cfg
[
format
"tls-key-file %s/../../tls/server.key"
[
pwd
]]
puts $cfg
[
format
"tls-key-file %s/../../tls/server.key"
[
pwd
]]
puts $cfg
[
format
"tls-client-cert-file %s/../../tls/client.crt"
[
pwd
]]
puts $cfg
[
format
"tls-client-cert-file %s/../../tls/client.crt"
[
pwd
]]
...
@@ -118,6 +122,8 @@ proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} {
...
@@ -118,6 +122,8 @@ proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} {
set cfg
[
open $cfgfile a+
]
set cfg
[
open $cfgfile a+
]
if
{
$::tls
}
{
if
{
$::tls
}
{
puts $cfg
"tls-port
$port
"
puts $cfg
"tls-port
$port
"
set pport
[
find_available_port $base_port $::redis_port_count
]
puts $cfg
"port
$pport
"
}
else
{
}
else
{
puts $cfg
"port
$port
"
puts $cfg
"port
$port
"
}
}
...
@@ -143,6 +149,7 @@ proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} {
...
@@ -143,6 +149,7 @@ proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} {
pid $pid
\
pid $pid
\
host $::host
\
host $::host
\
port $port
\
port $port
\
plaintext-port $pport
\
link $link
\
link $link
\
]
]
}
}
...
@@ -492,6 +499,14 @@ proc RI {n field} {
...
@@ -492,6 +499,14 @@ proc RI {n field} {
get_info_field
[
R $n info
]
$field
get_info_field
[
R $n info
]
$field
}
}
proc RPort
{
n
}
{
if
{
$::tls
}
{
return
[
lindex
[
R $n config get tls-port
]
1
]
}
else
{
return
[
lindex
[
R $n config get port
]
1
]
}
}
# Iterate over IDs of sentinel or redis instances.
# Iterate over IDs of sentinel or redis instances.
proc foreach_instance_id
{
instances idvar code
}
{
proc foreach_instance_id
{
instances idvar code
}
{
upvar 1 $idvar id
upvar 1 $idvar id
...
...
tests/integration/corrupt-dump.tcl
View file @
f5ca1f9e
...
@@ -518,5 +518,16 @@ test {corrupt payload: fuzzer findings - HRANDFIELD on bad ziplist} {
...
@@ -518,5 +518,16 @@ test {corrupt payload: fuzzer findings - HRANDFIELD on bad ziplist} {
}
}
}
}
test
{
corrupt payload: fuzzer findings - stream with no records
}
{
start_server
[
list overrides
[
list loglevel verbose use-exit-on-panic yes crash-memcheck-enabled no
]
]
{
r config set sanitize-dump-payload no
r debug set-skip-checksum-validation 1
r restore _stream 0
"
\x0F\x01\x10\x00\x00\x01\x78\x4D\x55\x68\x09\x00\x00\x00\x00\x00\x00\x00\x00\x40\x42\x42\x00\x00\x00\x18\x00\x02\x01\x01\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x00\x01\x02\x01\x00\x01\x00\x01\x01\x01\x00\x01\x05\x01\x03\x01\x3E\x01\x00\x01\x01\x01\x82\x5F\x31\x03\x05\x01\x02\x01\x50\x01\x00\x01\x01\x01\x02\x01\x05\x23\xFF\x02\x81\x00\x00\x01\x78\x4D\x55\x68\x59\x00\x01\x07\x6D\x79\x67\x72\x6F\x75\x70\x81\x00\x00\x01\x78\x4D\x55\x68\x47\x00\x01\x00\x00\x01\x78\x4D\x55\x68\x47\x00\x00\x00\x00\x00\x00\x00\x00\x9F\x68\x55\x4D\x78\x01\x00\x00\x01\x01\x05\x41\x6C\x69\x63\x65\x85\x68\x55\x4D\x78\x01\x00\x00\x01\x00\x00\x01\x78\x4D\x55\x68\x47\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\xF1\xC0\x72\x70\x39\x40\x1E\xA9
"
replace
catch
{
r XREAD STREAMS _stream $
}
assert_equal
[
count_log_message 0
"crashed by signal"
]
0
assert_equal
[
count_log_message 0
"Guru Meditation"
]
1
}
}
}
;
# tags
}
;
# tags
tests/integration/psync2-pingoff.tcl
View file @
f5ca1f9e
...
@@ -111,7 +111,13 @@ start_server {} {
...
@@ -111,7 +111,13 @@ start_server {} {
$replica1 replicaof no one
$replica1 replicaof no one
$replica2 replicaof 127.0.0.1 1
;
# we can't promote it to master since that will cycle the replication id
$replica2 replicaof 127.0.0.1 1
;
# we can't promote it to master since that will cycle the replication id
$master config set repl-ping-replica-period 1
$master config set repl-ping-replica-period 1
after 1500
set replofs
[
status $master master_repl_offset
]
wait_for_condition 50 100
{
[
status $replica3 master_repl_offset
]
> $replofs &&
[
status $replica4 master_repl_offset
]
> $replofs
}
else
{
fail
"replica didn't sync in time"
}
# make everyone sync from the replica1 that didn't get the last ping from the old master
# make everyone sync from the replica1 that didn't get the last ping from the old master
# replica4 will keep syncing from the old master which now syncs from replica1
# replica4 will keep syncing from the old master which now syncs from replica1
...
@@ -195,10 +201,16 @@ start_server {} {
...
@@ -195,10 +201,16 @@ start_server {} {
fail
"Chained replica not replicating from its master"
fail
"Chained replica not replicating from its master"
}
}
# Do a write on the master, and wait for
3 seconds for
the master to
# Do a write on the master, and wait for the master to
# send some PINGs to its replica
# send some PINGs to its replica
$R
(
0
)
INCR counter2
$R
(
0
)
INCR counter2
after 2000
set replofs
[
status $R
(
0
)
master_repl_offset
]
wait_for_condition 50 100
{
[
status $R
(
1
)
master_repl_offset
]
> $replofs &&
[
status $R
(
2
)
master_repl_offset
]
> $replofs
}
else
{
fail
"replica didn't sync in time"
}
set sync_partial_master
[
status $R
(
0
)
sync_partial_ok
]
set sync_partial_master
[
status $R
(
0
)
sync_partial_ok
]
set sync_partial_replica
[
status $R
(
1
)
sync_partial_ok
]
set sync_partial_replica
[
status $R
(
1
)
sync_partial_ok
]
$R
(
0
)
CONFIG SET repl-ping-replica-period 100
$R
(
0
)
CONFIG SET repl-ping-replica-period 100
...
...
tests/integration/redis-cli.tcl
View file @
f5ca1f9e
...
@@ -207,6 +207,28 @@ start_server {tags {"cli"}} {
...
@@ -207,6 +207,28 @@ start_server {tags {"cli"}} {
assert_equal
"foo
\n
bar"
[
run_cli lrange list 0 -1
]
assert_equal
"foo
\n
bar"
[
run_cli lrange list 0 -1
]
}
}
test_nontty_cli
"Quoted input arguments"
{
r set
"
\x00\x00
"
"value"
assert_equal
"value"
[
run_cli --quoted-input get
{
"
\x00\x00
"
}]
}
test_nontty_cli
"No accidental unquoting of input arguments"
{
run_cli --quoted-input set
{
"
\x41\x41
"
}
quoted-val
run_cli set
{
"
\x41\x41
"
}
unquoted-val
assert_equal
"quoted-val"
[
r get AA
]
assert_equal
"unquoted-val"
[
r get
{
"
\x41\x41
"
}]
}
test_nontty_cli
"Invalid quoted input arguments"
{
catch
{
run_cli --quoted-input set
{
"Unterminated
}}
err
assert_match
{
*exited abnormally*
}
$err
# A single arg that unquotes to two arguments is also not expected
catch
{
run_cli --quoted-input set
{
"arg1"
"arg2"
}}
err
assert_match
{
*exited abnormally*
}
$err
}
test_nontty_cli
"Read last argument from pipe"
{
test_nontty_cli
"Read last argument from pipe"
{
assert_equal
"OK"
[
run_cli_with_input_pipe
"echo foo"
set key
]
assert_equal
"OK"
[
run_cli_with_input_pipe
"echo foo"
set key
]
assert_equal
"foo
\n
"
[
r get key
]
assert_equal
"foo
\n
"
[
r get key
]
...
@@ -247,6 +269,20 @@ start_server {tags {"cli"}} {
...
@@ -247,6 +269,20 @@ start_server {tags {"cli"}} {
test_redis_cli_rdb_dump
test_redis_cli_rdb_dump
}
}
test
"Scan mode"
{
r flushdb
populate 1000 key: 1
# basic use
assert_equal 1000
[
llength
[
split
[
run_cli --scan
]]]
# pattern
assert_equal
{
key:2
}
[
run_cli --scan --pattern
"*:2"
]
# pattern matching with a quoted string
assert_equal
{
key:2
}
[
run_cli --scan --quoted-pattern
{
"*:
\x32
"
}]
}
test
"Connecting as a replica"
{
test
"Connecting as a replica"
{
set fd
[
open_cli
"--replica"
]
set fd
[
open_cli
"--replica"
]
wait_for_condition 500 500
{
wait_for_condition 500 500
{
...
...
tests/integration/replication-4.tcl
View file @
f5ca1f9e
...
@@ -79,12 +79,16 @@ start_server {tags {"repl"}} {
...
@@ -79,12 +79,16 @@ start_server {tags {"repl"}} {
$master config set min-slaves-max-lag 2
$master config set min-slaves-max-lag 2
$master config set min-slaves-to-write 1
$master config set min-slaves-to-write 1
assert
{[
$master
set foo bar
]
eq
{
OK
}}
assert
{[
$master
set foo bar
]
eq
{
OK
}}
$slave deferred 1
exec kill -SIGSTOP
[
srv 0 pid
]
$slave debug sleep 6
wait_for_condition 100 100
{
after 4000
[
catch
{
$master
set foo bar
}]
!= 0
catch
{
$master
set foo bar
}
e
}
else
{
set e
fail
"Master didn't become readonly"
}
{
NOREPLICAS*
}
}
catch
{
$master
set foo bar
}
err
assert_match
{
NOREPLICAS*
}
$err
exec kill -SIGCONT
[
srv 0 pid
]
}
}
}
}
}
...
...
tests/integration/replication.tcl
View file @
f5ca1f9e
...
@@ -196,9 +196,11 @@ start_server {tags {"repl"}} {
...
@@ -196,9 +196,11 @@ start_server {tags {"repl"}} {
}
{
master
}
}
{
master
}
test
{
SLAVEOF should start with link status
"down"
}
{
test
{
SLAVEOF should start with link status
"down"
}
{
r multi
r slaveof
[
srv -1 host
]
[
srv -1 port
]
r slaveof
[
srv -1 host
]
[
srv -1 port
]
s master_link_status
r info replication
}
{
down
}
r exec
}
{
*master_link_status:down*
}
test
{
The role should immediately be changed to
"replica"
}
{
test
{
The role should immediately be changed to
"replica"
}
{
s role
s role
...
@@ -595,9 +597,9 @@ start_server {tags {"repl"}} {
...
@@ -595,9 +597,9 @@ start_server {tags {"repl"}} {
$master debug populate 20000 test 10000
$master debug populate 20000 test 10000
$master config set rdbcompression no
$master config set rdbcompression no
# If running on Linux, we also measure utime/stime to detect possible I/O handling issues
# If running on Linux, we also measure utime/stime to detect possible I/O handling issues
set os
[
catch
{
exec uname
e
}]
set os
[
catch
{
exec uname
}]
set measure_time
[
expr
{
$os
==
"Linux"
}
? 1 : 0
]
set measure_time
[
expr
{
$os
==
"Linux"
}
? 1 : 0
]
foreach all_drop
{
no slow fast all
}
{
foreach all_drop
{
no slow fast all
timeout
}
{
test
"diskless
$all
_drop replicas drop during rdb pipe"
{
test
"diskless
$all
_drop replicas drop during rdb pipe"
{
set replicas
{}
set replicas
{}
set replicas_alive
{}
set replicas_alive
{}
...
@@ -614,7 +616,7 @@ start_server {tags {"repl"}} {
...
@@ -614,7 +616,7 @@ start_server {tags {"repl"}} {
# so that the whole rdb generation process is bound to that
# so that the whole rdb generation process is bound to that
set loglines
[
count_log_lines -1
]
set loglines
[
count_log_lines -1
]
[
lindex $replicas 0
]
config set repl-diskless-load swapdb
[
lindex $replicas 0
]
config set repl-diskless-load swapdb
[
lindex $replicas 0
]
config set key-load-delay 100
[
lindex $replicas 0
]
config set key-load-delay 100
;
# 20k keys and 100 microseconds sleep means at least 2 seconds
[
lindex $replicas 0
]
replicaof $master_host $master_port
[
lindex $replicas 0
]
replicaof $master_host $master_port
[
lindex $replicas 1
]
replicaof $master_host $master_port
[
lindex $replicas 1
]
replicaof $master_host $master_port
...
@@ -645,6 +647,12 @@ start_server {tags {"repl"}} {
...
@@ -645,6 +647,12 @@ start_server {tags {"repl"}} {
exec kill
[
srv -1 pid
]
exec kill
[
srv -1 pid
]
set replicas_alive
[
lreplace $replicas_alive 0 0
]
set replicas_alive
[
lreplace $replicas_alive 0 0
]
}
}
if
{
$all
_drop ==
"timeout"
}
{
$master config set repl-timeout 2
# we want the slow replica to hang on a key for very long so it'll reach repl-timeout
exec kill -SIGSTOP
[
srv -1 pid
]
after 2000
}
# wait for rdb child to exit
# wait for rdb child to exit
wait_for_condition 500 100
{
wait_for_condition 500 100
{
...
@@ -663,6 +671,14 @@ start_server {tags {"repl"}} {
...
@@ -663,6 +671,14 @@ start_server {tags {"repl"}} {
if
{
$all
_drop ==
"slow"
|| $all_drop ==
"fast"
}
{
if
{
$all
_drop ==
"slow"
|| $all_drop ==
"fast"
}
{
wait_for_log_messages -2
{
"*Diskless rdb transfer, done reading from pipe, 1 replicas still up*"
}
$loglines 1 1
wait_for_log_messages -2
{
"*Diskless rdb transfer, done reading from pipe, 1 replicas still up*"
}
$loglines 1 1
}
}
if
{
$all
_drop ==
"timeout"
}
{
wait_for_log_messages -2
{
"*Disconnecting timedout replica (full sync)*"
}
$loglines 1 1
wait_for_log_messages -2
{
"*Diskless rdb transfer, done reading from pipe, 1 replicas still up*"
}
$loglines 1 1
# master disconnected the slow replica, remove from array
set replicas_alive
[
lreplace $replicas_alive 0 0
]
# release it
exec kill -SIGCONT
[
srv -1 pid
]
}
# make sure we don't have a busy loop going thought epoll_wait
# make sure we don't have a busy loop going thought epoll_wait
if
{
$measure
_time
}
{
if
{
$measure
_time
}
{
...
@@ -676,7 +692,7 @@ start_server {tags {"repl"}} {
...
@@ -676,7 +692,7 @@ start_server {tags {"repl"}} {
puts
"master utime:
$master
_utime"
puts
"master utime:
$master
_utime"
puts
"master stime:
$master
_stime"
puts
"master stime:
$master
_stime"
}
}
if
{
!$::no_latency &&
(
$all
_drop ==
"all"
|| $all_drop ==
"slow"
)}
{
if
{
!$::no_latency &&
(
$all
_drop ==
"all"
|| $all_drop ==
"slow"
|| $all_drop ==
"timeout"
)}
{
assert
{
$master
_utime < 70
}
assert
{
$master
_utime < 70
}
assert
{
$master
_stime < 70
}
assert
{
$master
_stime < 70
}
}
}
...
@@ -720,7 +736,7 @@ start_server {tags {"repl"}} {
...
@@ -720,7 +736,7 @@ start_server {tags {"repl"}} {
test
"diskless replication child being killed is collected"
{
test
"diskless replication child being killed is collected"
{
# when diskless master is waiting for the replica to become writable
# when diskless master is waiting for the replica to become writable
# it removes the read event from the rdb pipe so if the child gets killed
# it removes the read event from the rdb pipe so if the child gets killed
# the replica will hung. and the master may not collect the pid with wait
3
# the replica will hung. and the master may not collect the pid with wait
pid
start_server
{
tags
{
"repl"
}}
{
start_server
{
tags
{
"repl"
}}
{
set master
[
srv 0 client
]
set master
[
srv 0 client
]
set master_host
[
srv 0 host
]
set master_host
[
srv 0 host
]
...
...
tests/modules/keyspace_events.c
View file @
f5ca1f9e
...
@@ -38,6 +38,8 @@
...
@@ -38,6 +38,8 @@
/** strores all the keys on which we got 'loaded' keyspace notification **/
/** strores all the keys on which we got 'loaded' keyspace notification **/
RedisModuleDict
*
loaded_event_log
=
NULL
;
RedisModuleDict
*
loaded_event_log
=
NULL
;
/** stores all the keys on which we got 'module' keyspace notification **/
RedisModuleDict
*
module_event_log
=
NULL
;
static
int
KeySpace_NotificationLoaded
(
RedisModuleCtx
*
ctx
,
int
type
,
const
char
*
event
,
RedisModuleString
*
key
){
static
int
KeySpace_NotificationLoaded
(
RedisModuleCtx
*
ctx
,
int
type
,
const
char
*
event
,
RedisModuleString
*
key
){
REDISMODULE_NOT_USED
(
ctx
);
REDISMODULE_NOT_USED
(
ctx
);
...
@@ -78,6 +80,50 @@ static int KeySpace_NotificationGeneric(RedisModuleCtx *ctx, int type, const cha
...
@@ -78,6 +80,50 @@ static int KeySpace_NotificationGeneric(RedisModuleCtx *ctx, int type, const cha
return
REDISMODULE_OK
;
return
REDISMODULE_OK
;
}
}
static
int
KeySpace_NotificationModule
(
RedisModuleCtx
*
ctx
,
int
type
,
const
char
*
event
,
RedisModuleString
*
key
)
{
REDISMODULE_NOT_USED
(
ctx
);
REDISMODULE_NOT_USED
(
type
);
REDISMODULE_NOT_USED
(
event
);
const
char
*
keyName
=
RedisModule_StringPtrLen
(
key
,
NULL
);
int
nokey
;
RedisModule_DictGetC
(
module_event_log
,
(
void
*
)
keyName
,
strlen
(
keyName
),
&
nokey
);
if
(
nokey
){
RedisModule_DictSetC
(
module_event_log
,
(
void
*
)
keyName
,
strlen
(
keyName
),
RedisModule_HoldString
(
ctx
,
key
));
}
return
REDISMODULE_OK
;
}
static
int
cmdNotify
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
){
if
(
argc
!=
2
){
return
RedisModule_WrongArity
(
ctx
);
}
RedisModule_NotifyKeyspaceEvent
(
ctx
,
REDISMODULE_NOTIFY_MODULE
,
"notify"
,
argv
[
1
]);
RedisModule_ReplyWithNull
(
ctx
);
return
REDISMODULE_OK
;
}
static
int
cmdIsModuleKeyNotified
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
){
if
(
argc
!=
2
){
return
RedisModule_WrongArity
(
ctx
);
}
const
char
*
key
=
RedisModule_StringPtrLen
(
argv
[
1
],
NULL
);
int
nokey
;
RedisModuleString
*
keyStr
=
RedisModule_DictGetC
(
module_event_log
,
(
void
*
)
key
,
strlen
(
key
),
&
nokey
);
RedisModule_ReplyWithArray
(
ctx
,
2
);
RedisModule_ReplyWithLongLong
(
ctx
,
!
nokey
);
if
(
nokey
){
RedisModule_ReplyWithNull
(
ctx
);
}
else
{
RedisModule_ReplyWithString
(
ctx
,
keyStr
);
}
return
REDISMODULE_OK
;
}
static
int
cmdIsKeyLoaded
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
){
static
int
cmdIsKeyLoaded
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
){
if
(
argc
!=
2
){
if
(
argc
!=
2
){
return
RedisModule_WrongArity
(
ctx
);
return
RedisModule_WrongArity
(
ctx
);
...
@@ -171,6 +217,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
...
@@ -171,6 +217,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
}
}
loaded_event_log
=
RedisModule_CreateDict
(
ctx
);
loaded_event_log
=
RedisModule_CreateDict
(
ctx
);
module_event_log
=
RedisModule_CreateDict
(
ctx
);
int
keySpaceAll
=
RedisModule_GetKeyspaceNotificationFlagsAll
();
int
keySpaceAll
=
RedisModule_GetKeyspaceNotificationFlagsAll
();
...
@@ -187,6 +234,18 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
...
@@ -187,6 +234,18 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return
REDISMODULE_ERR
;
return
REDISMODULE_ERR
;
}
}
if
(
RedisModule_SubscribeToKeyspaceEvents
(
ctx
,
REDISMODULE_NOTIFY_MODULE
,
KeySpace_NotificationModule
)
!=
REDISMODULE_OK
){
return
REDISMODULE_ERR
;
}
if
(
RedisModule_CreateCommand
(
ctx
,
"keyspace.notify"
,
cmdNotify
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
){
return
REDISMODULE_ERR
;
}
if
(
RedisModule_CreateCommand
(
ctx
,
"keyspace.is_module_key_notified"
,
cmdIsModuleKeyNotified
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
){
return
REDISMODULE_ERR
;
}
if
(
RedisModule_CreateCommand
(
ctx
,
"keyspace.is_key_loaded"
,
cmdIsKeyLoaded
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
){
if
(
RedisModule_CreateCommand
(
ctx
,
"keyspace.is_key_loaded"
,
cmdIsKeyLoaded
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
){
return
REDISMODULE_ERR
;
return
REDISMODULE_ERR
;
}
}
...
@@ -219,6 +278,16 @@ int RedisModule_OnUnload(RedisModuleCtx *ctx) {
...
@@ -219,6 +278,16 @@ int RedisModule_OnUnload(RedisModuleCtx *ctx) {
RedisModule_FreeString
(
ctx
,
val
);
RedisModule_FreeString
(
ctx
,
val
);
}
}
RedisModule_FreeDict
(
ctx
,
loaded_event_log
);
RedisModule_FreeDict
(
ctx
,
loaded_event_log
);
RedisModule_DictIteratorStop
(
iter
);
loaded_event_log
=
NULL
;
loaded_event_log
=
NULL
;
iter
=
RedisModule_DictIteratorStartC
(
module_event_log
,
"^"
,
NULL
,
0
);
while
((
key
=
RedisModule_DictNextC
(
iter
,
&
keyLen
,
(
void
**
)
&
val
))){
RedisModule_FreeString
(
ctx
,
val
);
}
RedisModule_FreeDict
(
ctx
,
module_event_log
);
RedisModule_DictIteratorStop
(
iter
);
module_event_log
=
NULL
;
return
REDISMODULE_OK
;
return
REDISMODULE_OK
;
}
}
tests/modules/propagate.c
View file @
f5ca1f9e
...
@@ -70,6 +70,44 @@ int propagateTestTimerCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int
...
@@ -70,6 +70,44 @@ int propagateTestTimerCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int
return
REDISMODULE_OK
;
return
REDISMODULE_OK
;
}
}
/* Timer callback. */
void
timerNestedHandler
(
RedisModuleCtx
*
ctx
,
void
*
data
)
{
int
repl
=
(
long
long
)
data
;
/* The goal is the trigger a module command that calls RM_Replicate
* in order to test MULTI/EXEC structre */
RedisModule_Replicate
(
ctx
,
"INCRBY"
,
"cc"
,
"timer-nested-start"
,
"1"
);
RedisModuleCallReply
*
reply
=
RedisModule_Call
(
ctx
,
"propagate-test.nested"
,
repl
?
"!"
:
""
);
RedisModule_FreeCallReply
(
reply
);
RedisModule_Replicate
(
ctx
,
"INCRBY"
,
"cc"
,
"timer-nested-end"
,
"1"
);
}
int
propagateTestTimerNestedCommand
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
RedisModuleTimerID
timer_id
=
RedisModule_CreateTimer
(
ctx
,
100
,
timerNestedHandler
,(
void
*
)
0
);
REDISMODULE_NOT_USED
(
timer_id
);
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
return
REDISMODULE_OK
;
}
int
propagateTestTimerNestedReplCommand
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
RedisModuleTimerID
timer_id
=
RedisModule_CreateTimer
(
ctx
,
100
,
timerNestedHandler
,(
void
*
)
1
);
REDISMODULE_NOT_USED
(
timer_id
);
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
return
REDISMODULE_OK
;
}
/* The thread entry point. */
/* The thread entry point. */
void
*
threadMain
(
void
*
arg
)
{
void
*
threadMain
(
void
*
arg
)
{
REDISMODULE_NOT_USED
(
arg
);
REDISMODULE_NOT_USED
(
arg
);
...
@@ -131,6 +169,42 @@ int propagateTestMixedCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int
...
@@ -131,6 +169,42 @@ int propagateTestMixedCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int
return
REDISMODULE_OK
;
return
REDISMODULE_OK
;
}
}
int
propagateTestNestedCommand
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
RedisModuleCallReply
*
reply
;
/* This test mixes multiple propagation systems. */
reply
=
RedisModule_Call
(
ctx
,
"INCR"
,
"c!"
,
"using-call"
);
RedisModule_FreeCallReply
(
reply
);
reply
=
RedisModule_Call
(
ctx
,
"propagate-test.simple"
,
"!"
);
RedisModule_FreeCallReply
(
reply
);
RedisModule_Replicate
(
ctx
,
"INCR"
,
"c"
,
"counter-3"
);
RedisModule_Replicate
(
ctx
,
"INCR"
,
"c"
,
"counter-4"
);
reply
=
RedisModule_Call
(
ctx
,
"INCR"
,
"c!"
,
"after-call"
);
RedisModule_FreeCallReply
(
reply
);
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
return
REDISMODULE_OK
;
}
int
propagateTestIncr
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argc
);
RedisModuleCallReply
*
reply
;
/* This test propagates the module command, not the INCR it executes. */
reply
=
RedisModule_Call
(
ctx
,
"INCR"
,
"s"
,
argv
[
1
]);
RedisModule_ReplyWithCallReply
(
ctx
,
reply
);
RedisModule_FreeCallReply
(
reply
);
RedisModule_ReplicateVerbatim
(
ctx
);
return
REDISMODULE_OK
;
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
REDISMODULE_NOT_USED
(
argc
);
...
@@ -143,6 +217,16 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
...
@@ -143,6 +217,16 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"propagate-test.timer-nested"
,
propagateTestTimerNestedCommand
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"propagate-test.timer-nested-repl"
,
propagateTestTimerNestedReplCommand
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"propagate-test.thread"
,
if
(
RedisModule_CreateCommand
(
ctx
,
"propagate-test.thread"
,
propagateTestThreadCommand
,
propagateTestThreadCommand
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
...
@@ -158,5 +242,15 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
...
@@ -158,5 +242,15 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"propagate-test.nested"
,
propagateTestNestedCommand
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"propagate-test.incr"
,
propagateTestIncr
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
return
REDISMODULE_OK
;
}
}
tests/sentinel/tests/00-base.tcl
View file @
f5ca1f9e
...
@@ -9,7 +9,7 @@ if {$::simulate_error} {
...
@@ -9,7 +9,7 @@ if {$::simulate_error} {
}
}
test
"Basic failover works if the master is down"
{
test
"Basic failover works if the master is down"
{
set old_port
[
R
I
$master_id
tcp_port
]
set old_port
[
R
Port
$master_id
]
set addr
[
S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
set addr
[
S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
assert
{[
lindex $addr 1
]
== $old_port
}
assert
{[
lindex $addr 1
]
== $old_port
}
kill_instance redis $master_id
kill_instance redis $master_id
...
@@ -53,7 +53,7 @@ test "ODOWN is not possible without N (quorum) Sentinels reports" {
...
@@ -53,7 +53,7 @@ test "ODOWN is not possible without N (quorum) Sentinels reports" {
foreach_sentinel_id id
{
foreach_sentinel_id id
{
S $id SENTINEL SET mymaster quorum
[
expr $sentinels+1
]
S $id SENTINEL SET mymaster quorum
[
expr $sentinels+1
]
}
}
set old_port
[
R
I
$master_id
tcp_port
]
set old_port
[
R
Port
$master_id
]
set addr
[
S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
set addr
[
S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
assert
{[
lindex $addr 1
]
== $old_port
}
assert
{[
lindex $addr 1
]
== $old_port
}
kill_instance redis $master_id
kill_instance redis $master_id
...
...
tests/sentinel/tests/01-conf-update.tcl
View file @
f5ca1f9e
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
source
"../tests/includes/init-tests.tcl"
source
"../tests/includes/init-tests.tcl"
test
"We can failover with Sentinel 1 crashed"
{
test
"We can failover with Sentinel 1 crashed"
{
set old_port
[
R
I
$master_id
tcp_port
]
set old_port
[
R
Port
$master_id
]
set addr
[
S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
set addr
[
S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
assert
{[
lindex $addr 1
]
== $old_port
}
assert
{[
lindex $addr 1
]
== $old_port
}
...
...
tests/sentinel/tests/02-slaves-reconf.tcl
View file @
f5ca1f9e
...
@@ -10,7 +10,7 @@ source "../tests/includes/init-tests.tcl"
...
@@ -10,7 +10,7 @@ source "../tests/includes/init-tests.tcl"
proc 02_test_slaves_replication
{}
{
proc 02_test_slaves_replication
{}
{
uplevel 1
{
uplevel 1
{
test
"Check that slaves replicate from current master"
{
test
"Check that slaves replicate from current master"
{
set master_port
[
R
I
$master_id
tcp_port
]
set master_port
[
R
Port
$master_id
]
foreach_redis_id id
{
foreach_redis_id id
{
if
{
$id
== $master_id
}
continue
if
{
$id
== $master_id
}
continue
if
{[
instance_is_killed redis $id
]}
continue
if
{[
instance_is_killed redis $id
]}
continue
...
@@ -28,7 +28,7 @@ proc 02_test_slaves_replication {} {
...
@@ -28,7 +28,7 @@ proc 02_test_slaves_replication {} {
proc 02_crash_and_failover
{}
{
proc 02_crash_and_failover
{}
{
uplevel 1
{
uplevel 1
{
test
"Crash the master and force a failover"
{
test
"Crash the master and force a failover"
{
set old_port
[
R
I
$master_id
tcp_port
]
set old_port
[
R
Port
$master_id
]
set addr
[
S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
set addr
[
S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
assert
{[
lindex $addr 1
]
== $old_port
}
assert
{[
lindex $addr 1
]
== $old_port
}
kill_instance redis $master_id
kill_instance redis $master_id
...
...
tests/sentinel/tests/05-manual.tcl
View file @
f5ca1f9e
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
source
"../tests/includes/init-tests.tcl"
source
"../tests/includes/init-tests.tcl"
test
"Manual failover works"
{
test
"Manual failover works"
{
set old_port
[
R
I
$master_id
tcp_port
]
set old_port
[
R
Port
$master_id
]
set addr
[
S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
set addr
[
S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster
]
assert
{[
lindex $addr 1
]
== $old_port
}
assert
{[
lindex $addr 1
]
== $old_port
}
catch
{
S 0 SENTINEL FAILOVER mymaster
}
reply
catch
{
S 0 SENTINEL FAILOVER mymaster
}
reply
...
...
tests/sentinel/tests/10-replica-priority.tcl
0 → 100644
View file @
f5ca1f9e
source
"../tests/includes/init-tests.tcl"
test
"Check acceptable replica-priority values"
{
foreach_redis_id id
{
if
{
$id
== $master_id
}
continue
# ensure replica-announced accepts yes and no
catch
{
R $id CONFIG SET replica-announced no
}
e
if
{
$e
ne
"OK"
}
{
fail
"Unable to set replica-announced to no"
}
catch
{
R $id CONFIG SET replica-announced yes
}
e
if
{
$e
ne
"OK"
}
{
fail
"Unable to set replica-announced to yes"
}
# ensure a random value throw error
catch
{
R $id CONFIG SET replica-announced 321
}
e
if
{
$e
eq
"OK"
}
{
fail
"Able to set replica-announced with something else than yes or no (321) whereas it should not be possible"
}
catch
{
R $id CONFIG SET replica-announced a3b2c1
}
e
if
{
$e
eq
"OK"
}
{
fail
"Able to set replica-announced with something else than yes or no (a3b2c1) whereas it should not be possible"
}
# test only the first redis replica, no need to double test
break
}
}
proc 10_test_number_of_replicas
{
n_replicas_expected
}
{
test
"Check sentinel replies with
$n
_replicas_expected replicas"
{
# ensure sentinels replies with the right number of replicas
foreach_sentinel_id id
{
# retries 40 x 500ms = 20s as SENTINEL_INFO_PERIOD = 10s
set len
[
llength
[
S $id SENTINEL REPLICAS mymaster
]]
wait_for_condition 40 500
{
[
llength
[
S $id SENTINEL REPLICAS mymaster
]]
== $n_replicas_expected
}
else
{
fail
"Sentinel replies with a wrong number of replicas with replica-announced=yes (expected
$n
_replicas_expected but got
$len
) on sentinel
$id
"
}
}
}
}
proc 10_set_replica_announced
{
master_id announced n_replicas
}
{
test
"Set replica-announced=
$announced
on
$n
_replicas replicas"
{
set i 0
foreach_redis_id id
{
if
{
$id
== $master_id
}
continue
#puts
"set replica-announce=
$announced
on redis #
$id
"
R $id CONFIG SET replica-announced
"
$announced
"
incr i
if
{
$n_replicas!=
"all"
&& $i >= $n_replicas
}
{
break
}
}
}
}
# ensure all replicas are announced
10_set_replica_announced $master_id
"yes"
"all"
# ensure all replicas are announced by sentinels
10_test_number_of_replicas 4
# ensure the first 2 replicas are not announced
10_set_replica_announced $master_id
"no"
2
# ensure sentinels are not announcing the first 2 replicas that have been set unannounced
10_test_number_of_replicas 2
# ensure all replicas are announced
10_set_replica_announced $master_id
"yes"
"all"
# ensure all replicas are not announced by sentinels
10_test_number_of_replicas 4
tests/support/cluster.tcl
View file @
f5ca1f9e
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
#
#
# Example usage:
# Example usage:
#
#
# set c
[
redis_cluster 127.0.0.1
6379 127.0.0.1
6380
]
# set c
[
redis_cluster
{
127.0.0.1
:
6379 127.0.0.1
:
6380
}
]
# $c set foo
# $c set foo
# $c get foo
# $c get foo
# $c close
# $c close
...
@@ -17,6 +17,7 @@ set ::redis_cluster::id 0
...
@@ -17,6 +17,7 @@ set ::redis_cluster::id 0
array set ::redis_cluster::startup_nodes
{}
array set ::redis_cluster::startup_nodes
{}
array set ::redis_cluster::nodes
{}
array set ::redis_cluster::nodes
{}
array set ::redis_cluster::slots
{}
array set ::redis_cluster::slots
{}
array set ::redis_cluster::tls
{}
# List of
"plain"
commands, which are commands where the sole key is always
# List of
"plain"
commands, which are commands where the sole key is always
# the first argument.
# the first argument.
...
@@ -34,11 +35,14 @@ set ::redis_cluster::plain_commands {
...
@@ -34,11 +35,14 @@ set ::redis_cluster::plain_commands {
dump bitcount bitpos pfadd pfcount
dump bitcount bitpos pfadd pfcount
}
}
proc redis_cluster
{
nodes
}
{
# Create a cluster client. The nodes are given as a list of host:port. The TLS
# parameter
(
1 or 0
)
is optional and defaults to the global $::tls.
proc redis_cluster
{
nodes
{
tls -1
}}
{
set id
[
incr ::redis_cluster::id
]
set id
[
incr ::redis_cluster::id
]
set ::redis_cluster::startup_nodes
(
$id
)
$nodes
set ::redis_cluster::startup_nodes
(
$id
)
$nodes
set ::redis_cluster::nodes
(
$id
)
{}
set ::redis_cluster::nodes
(
$id
)
{}
set ::redis_cluster::slots
(
$id
)
{}
set ::redis_cluster::slots
(
$id
)
{}
set ::redis_cluster::tls
(
$id
)
[
expr $tls == -1 ? $::tls : $tls
]
set handle
[
interp alias
{}
::redis_cluster::instance$id
{}
::redis_cluster::__dispatch__ $id
]
set handle
[
interp alias
{}
::redis_cluster::instance$id
{}
::redis_cluster::__dispatch__ $id
]
$handle refresh_nodes_map
$handle refresh_nodes_map
return $handle
return $handle
...
@@ -60,9 +64,10 @@ proc ::redis_cluster::__method__refresh_nodes_map {id} {
...
@@ -60,9 +64,10 @@ proc ::redis_cluster::__method__refresh_nodes_map {id} {
foreach start_node $::redis_cluster::startup_nodes
(
$id
)
{
foreach start_node $::redis_cluster::startup_nodes
(
$id
)
{
set ip_port
[
lindex
[
split $start_node @
]
0
]
set ip_port
[
lindex
[
split $start_node @
]
0
]
lassign
[
split $ip_port :
]
start_host start_port
lassign
[
split $ip_port :
]
start_host start_port
set tls $::redis_cluster::tls
(
$id
)
if
{[
catch
{
if
{[
catch
{
set r
{}
set r
{}
set r
[
redis $start_host $start_port 0 $
::
tls
]
set r
[
redis $start_host $start_port 0 $tls
]
set nodes_descr
[
$r
cluster nodes
]
set nodes_descr
[
$r
cluster nodes
]
$r close
$r close
}
e
]}
{
}
e
]}
{
...
@@ -107,7 +112,8 @@ proc ::redis_cluster::__method__refresh_nodes_map {id} {
...
@@ -107,7 +112,8 @@ proc ::redis_cluster::__method__refresh_nodes_map {id} {
# Connect to the node
# Connect to the node
set link
{}
set link
{}
catch
{
set link
[
redis $host $port 0 $::tls
]}
set tls $::redis_cluster::tls
(
$id
)
catch
{
set link
[
redis $host $port 0 $tls
]}
# Build this node description as an hash.
# Build this node description as an hash.
set node
[
dict create
\
set node
[
dict create
\
...
@@ -161,9 +167,32 @@ proc ::redis_cluster::__method__close {id} {
...
@@ -161,9 +167,32 @@ proc ::redis_cluster::__method__close {id} {
catch
{
unset ::redis_cluster::startup_nodes
(
$id
)}
catch
{
unset ::redis_cluster::startup_nodes
(
$id
)}
catch
{
unset ::redis_cluster::nodes
(
$id
)}
catch
{
unset ::redis_cluster::nodes
(
$id
)}
catch
{
unset ::redis_cluster::slots
(
$id
)}
catch
{
unset ::redis_cluster::slots
(
$id
)}
catch
{
unset ::redis_cluster::tls
(
$id
)}
catch
{
interp alias
{}
::redis_cluster::instance$id
{}}
catch
{
interp alias
{}
::redis_cluster::instance$id
{}}
}
}
proc ::redis_cluster::__method__masternode_for_slot
{
id slot
}
{
# Get the node mapped to this slot.
set node_addr
[
dict get $::redis_cluster::slots
(
$id
)
$slot
]
if
{
$node
_addr eq
{}}
{
error
"No mapped node for slot
$slot.
"
}
return
[
dict get $::redis_cluster::nodes
(
$id
)
$node_addr
]
}
proc ::redis_cluster::__method__masternode_notfor_slot
{
id slot
}
{
# Get a node that is not mapped to this slot.
set node_addr
[
dict get $::redis_cluster::slots
(
$id
)
$slot
]
set addrs
[
dict keys $::redis_cluster::nodes
(
$id
)]
foreach addr
[
lshuffle $addrs
]
{
set node
[
dict get $::redis_cluster::nodes
(
$id
)
$addr
]
if
{
$node
_addr ne $addr &&
[
dict get $node slaveof
]
eq
"-"
}
{
return $node
}
}
error
"Slot
$slot
is everywhere"
}
proc ::redis_cluster::__dispatch__
{
id method args
}
{
proc ::redis_cluster::__dispatch__
{
id method args
}
{
if
{[
info command ::redis_cluster::__method__$method
]
eq
{}}
{
if
{[
info command ::redis_cluster::__method__$method
]
eq
{}}
{
# Get the keys from the command.
# Get the keys from the command.
...
@@ -186,10 +215,15 @@ proc ::redis_cluster::__dispatch__ {id method args} {
...
@@ -186,10 +215,15 @@ proc ::redis_cluster::__dispatch__ {id method args} {
# Execute the command in the node we think is the slot owner.
# Execute the command in the node we think is the slot owner.
set retry 100
set retry 100
set asking 0
while
{[
incr retry -1
]}
{
while
{[
incr retry -1
]}
{
if
{
$retry
< 5
}
{
after 100
}
if
{
$retry
< 5
}
{
after 100
}
set node
[
dict get $::redis_cluster::nodes
(
$id
)
$node_addr
]
set node
[
dict get $::redis_cluster::nodes
(
$id
)
$node_addr
]
set link
[
dict get $node link
]
set link
[
dict get $node link
]
if
{
$asking
}
{
$link ASKING
set asking 0
}
if
{[
catch
{
$link
$method
{*}
$args
}
e
]}
{
if
{[
catch
{
$link
$method
{*}
$args
}
e
]}
{
if
{
$link
eq
{}
||
\
if
{
$link
eq
{}
||
\
[
string range $e 0 4
]
eq
{
MOVED
}
||
\
[
string range $e 0 4
]
eq
{
MOVED
}
||
\
...
@@ -202,6 +236,7 @@ proc ::redis_cluster::__dispatch__ {id method args} {
...
@@ -202,6 +236,7 @@ proc ::redis_cluster::__dispatch__ {id method args} {
}
elseif
{[
string range $e 0 2
]
eq
{
ASK
}}
{
}
elseif
{[
string range $e 0 2
]
eq
{
ASK
}}
{
# ASK redirection.
# ASK redirection.
set node_addr
[
lindex $e 2
]
set node_addr
[
lindex $e 2
]
set asking 1
continue
continue
}
else
{
}
else
{
# Non redirecting error.
# Non redirecting error.
...
...
tests/support/redis.tcl
View file @
f5ca1f9e
...
@@ -35,6 +35,7 @@ array set ::redis::addr {}
...
@@ -35,6 +35,7 @@ array set ::redis::addr {}
array set ::redis::blocking
{}
array set ::redis::blocking
{}
array set ::redis::deferred
{}
array set ::redis::deferred
{}
array set ::redis::reconnect
{}
array set ::redis::reconnect
{}
array set ::redis::tls
{}
array set ::redis::callback
{}
array set ::redis::callback
{}
array set ::redis::state
{}
;
# State in non-blocking reply reading
array set ::redis::state
{}
;
# State in non-blocking reply reading
array set ::redis::statestack
{}
;
# Stack of states, for nested mbulks
array set ::redis::statestack
{}
;
# Stack of states, for nested mbulks
...
@@ -58,7 +59,7 @@ proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}}} {
...
@@ -58,7 +59,7 @@ proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}}} {
set ::redis::blocking
(
$id
)
1
set ::redis::blocking
(
$id
)
1
set ::redis::deferred
(
$id
)
$defer
set ::redis::deferred
(
$id
)
$defer
set ::redis::reconnect
(
$id
)
0
set ::redis::reconnect
(
$id
)
0
set ::redis::tls $tls
set ::redis::tls
(
$id
)
$tls
::redis::redis_reset_state $id
::redis::redis_reset_state $id
interp alias
{}
::redis::redisHandle$id
{}
::redis::__dispatch__ $id
interp alias
{}
::redis::redisHandle$id
{}
::redis::__dispatch__ $id
}
}
...
@@ -83,7 +84,7 @@ proc ::redis::__dispatch__raw__ {id method argv} {
...
@@ -83,7 +84,7 @@ proc ::redis::__dispatch__raw__ {id method argv} {
# Reconnect the link if needed.
# Reconnect the link if needed.
if
{
$fd
eq
{}}
{
if
{
$fd
eq
{}}
{
lassign $::redis::addr
(
$id
)
host port
lassign $::redis::addr
(
$id
)
host port
if
{
$::redis::tls
}
{
if
{
$::redis::tls
(
$id
)
}
{
set ::redis::fd
(
$id
)
[
::tls::socket $host $port
]
set ::redis::fd
(
$id
)
[
::tls::socket $host $port
]
}
else
{
}
else
{
set ::redis::fd
(
$id
)
[
socket $host $port
]
set ::redis::fd
(
$id
)
[
socket $host $port
]
...
@@ -158,6 +159,7 @@ proc ::redis::__method__close {id fd} {
...
@@ -158,6 +159,7 @@ proc ::redis::__method__close {id fd} {
catch
{
unset ::redis::blocking
(
$id
)}
catch
{
unset ::redis::blocking
(
$id
)}
catch
{
unset ::redis::deferred
(
$id
)}
catch
{
unset ::redis::deferred
(
$id
)}
catch
{
unset ::redis::reconnect
(
$id
)}
catch
{
unset ::redis::reconnect
(
$id
)}
catch
{
unset ::redis::tls
(
$id
)}
catch
{
unset ::redis::state
(
$id
)}
catch
{
unset ::redis::state
(
$id
)}
catch
{
unset ::redis::statestack
(
$id
)}
catch
{
unset ::redis::statestack
(
$id
)}
catch
{
unset ::redis::callback
(
$id
)}
catch
{
unset ::redis::callback
(
$id
)}
...
...
tests/support/server.tcl
View file @
f5ca1f9e
...
@@ -253,7 +253,7 @@ proc wait_server_started {config_file stdout pid} {
...
@@ -253,7 +253,7 @@ proc wait_server_started {config_file stdout pid} {
# Check if the port is actually busy and the server failed
# Check if the port is actually busy and the server failed
# for this reason.
# for this reason.
if
{[
regexp
{
Could not create server TCP
}
[
exec cat $stdout
]]}
{
if
{[
regexp
{
Failed listening on port
}
[
exec cat $stdout
]]}
{
set port_busy 1
set port_busy 1
break
break
}
}
...
@@ -508,6 +508,7 @@ proc start_server {options {code undefined}} {
...
@@ -508,6 +508,7 @@ proc start_server {options {code undefined}} {
set num_tests $::num_tests
set num_tests $::num_tests
if
{[
catch
{
uplevel 1 $code
}
error
]}
{
if
{[
catch
{
uplevel 1 $code
}
error
]}
{
set backtrace $::errorInfo
set backtrace $::errorInfo
set assertion
[
string match
"assertion:*"
$error
]
# fetch srv back from the server list, in case it was restarted by restart_server
(
new PID
)
# fetch srv back from the server list, in case it was restarted by restart_server
(
new PID
)
set srv
[
lindex $::servers end
]
set srv
[
lindex $::servers end
]
...
@@ -519,17 +520,23 @@ proc start_server {options {code undefined}} {
...
@@ -519,17 +520,23 @@ proc start_server {options {code undefined}} {
dict set srv
"skipleaks"
1
dict set srv
"skipleaks"
1
kill_server $srv
kill_server $srv
# Print warnings from log
if
{
$::dump
_logs && $assertion
}
{
puts
[
format
"
\n
Logged warnings (pid %d):"
[
dict get $srv
"pid"
]]
# if we caught an assertion
(
$::num
_failed isn't incremented yet
)
set warnings
[
warnings_from_file
[
dict get $srv
"stdout"
]]
# this happens when the test spawns a server and not the other way around
if
{[
string length $warnings
]
> 0
}
{
dump_server_log $srv
puts
"
$warnings
"
}
else
{
}
else
{
puts
"(none)"
# Print crash report from log
}
set crashlog
[
crashlog_from_file
[
dict get $srv
"stdout"
]]
if
{[
string length $crashlog
]
> 0
}
{
puts
[
format
"
\n
Logged crash report (pid %d):"
[
dict get $srv
"pid"
]]
puts
"
$crashlog
"
puts
""
puts
""
}
}
if
{
$::durable
}
{
if
{
!$assertion && $::durable
}
{
# durable is meant to prevent the whole tcl test from exiting on
# an exception. an assertion will be caught by the test proc.
set msg
[
string range $error 10 end
]
set msg
[
string range $error 10 end
]
lappend details $msg
lappend details $msg
lappend details $backtrace
lappend details $backtrace
...
...
tests/support/test.tcl
View file @
f5ca1f9e
...
@@ -165,6 +165,8 @@ proc test {name code {okpattern undefined} {options undefined}} {
...
@@ -165,6 +165,8 @@ proc test {name code {okpattern undefined} {options undefined}} {
if
{[
catch
{
set retval
[
uplevel 1 $code
]}
error
]}
{
if
{[
catch
{
set retval
[
uplevel 1 $code
]}
error
]}
{
set assertion
[
string match
"assertion:*"
$error
]
set assertion
[
string match
"assertion:*"
$error
]
if
{
$assertion
|| $::durable
}
{
if
{
$assertion
|| $::durable
}
{
# durable prevents the whole tcl test from exiting on an exception.
# an assertion is handled gracefully anyway.
set msg
[
string range $error 10 end
]
set msg
[
string range $error 10 end
]
lappend details $msg
lappend details $msg
if
{
!$assertion
}
{
if
{
!$assertion
}
{
...
...
tests/support/util.tcl
View file @
f5ca1f9e
...
@@ -31,7 +31,7 @@ proc zlistAlikeSort {a b} {
...
@@ -31,7 +31,7 @@ proc zlistAlikeSort {a b} {
# Return all log lines starting with the first line that contains a warning.
# Return all log lines starting with the first line that contains a warning.
# Generally, this will be an assertion error with a stack trace.
# Generally, this will be an assertion error with a stack trace.
proc
warnings
_from_file
{
filename
}
{
proc
crashlog
_from_file
{
filename
}
{
set lines
[
split
[
exec cat $filename
]
"
\n
"
]
set lines
[
split
[
exec cat $filename
]
"
\n
"
]
set matched 0
set matched 0
set logall 0
set logall 0
...
@@ -506,18 +506,18 @@ proc stop_write_load {handle} {
...
@@ -506,18 +506,18 @@ proc stop_write_load {handle} {
proc K
{
x y
}
{
set x
}
proc K
{
x y
}
{
set x
}
# Shuffle a list. From Tcl wiki. Originally from Steve Cohen that improved
# Shuffle a list with Fisher-Yates algorithm.
# other versions. Code should be under public domain.
proc lshuffle
{
list
}
{
proc lshuffle
{
list
}
{
set n
[
llength $list
]
set n
[
llength $list
]
while
{
$n
>
0
}
{
while
{
$n
>
1
}
{
set j
[
expr
{
int
(
rand
()
*$n
)}]
set j
[
expr
{
int
(
rand
()
*$n
)}]
lappend slist
[
lindex $list $j
]
incr n -1
incr n -1
set temp
[
lindex $list $n
]
if
{
$n
==$j
}
continue
set list
[
lreplace
[
K $list
[
set list
{}]]
$j $j $temp
]
set v
[
lindex $list $j
]
lset list $j
[
lindex $list $n
]
lset list $n $v
}
}
return $
s
list
return $list
}
}
# Execute a background process writing complex data for the specified number
# Execute a background process writing complex data for the specified number
...
@@ -682,20 +682,83 @@ proc string2printable s {
...
@@ -682,20 +682,83 @@ proc string2printable s {
return $res
return $res
}
}
# Check that probability of each element are between
{
min_prop
}
and
{
max_prop
}
.
# Calculation value of Chi-Square Distribution. By this value
proc check_histogram_distribution
{
res min_prop max_prop
}
{
# we can verify the random distribution sample confidence.
# Based on the following wiki:
# https://en.wikipedia.org/wiki/Chi-square_distribution
#
# param res Random sample list
# return Value of Chi-Square Distribution
#
# x2_value: return of chi_square_value function
# df: Degrees of freedom, Number of independent values minus 1
#
# By using x2_value and df to back check the cardinality table,
# we can know the confidence of the random sample.
proc chi_square_value
{
res
}
{
unset -nocomplain mydict
unset -nocomplain mydict
foreach key $res
{
foreach key $res
{
dict incr mydict $key 1
dict incr mydict $key 1
}
}
set x2_value 0
set p
[
expr
[
llength $res
]
/
[
dict size $mydict
]]
foreach key
[
dict keys $mydict
]
{
foreach key
[
dict keys $mydict
]
{
set value
[
dict get $mydict $key
]
set value
[
dict get $mydict $key
]
set probability
[
expr
{
double
(
$value
)
/
[
llength $res
]}]
if
{
$probability
< $min_prop || $probability > $max_prop
}
{
# Aggregate the chi-square value of each element
return false
set v
[
expr
{
pow
(
$value
- $p, 2
)
/ $p
}]
set x2_value
[
expr
{
$x2
_value + $v
}]
}
}
return $x2_value
}
#subscribe to Pub/Sub channels
proc consume_subscribe_messages
{
client type channels
}
{
set numsub -1
set counts
{}
for
{
set i
[
llength $channels
]}
{
$i
> 0
}
{
incr i -1
}
{
set msg
[
$client
read
]
assert_equal $type
[
lindex $msg 0
]
# when receiving subscribe messages the channels names
# are ordered. when receiving unsubscribe messages
# they are unordered
set idx
[
lsearch -exact $channels
[
lindex $msg 1
]]
if
{[
string match
"*unsubscribe"
$type
]}
{
assert
{
$idx
>= 0
}
}
else
{
assert
{
$idx
== 0
}
}
set channels
[
lreplace $channels $idx $idx
]
# aggregate the subscription count to return to the caller
lappend counts
[
lindex $msg 2
]
}
}
return true
# we should have received messages for channels
assert
{[
llength $channels
]
== 0
}
return $counts
}
proc subscribe
{
client channels
}
{
$client subscribe
{*}
$channels
consume_subscribe_messages $client subscribe $channels
}
proc unsubscribe
{
client
{
channels
{}}}
{
$client unsubscribe
{*}
$channels
consume_subscribe_messages $client unsubscribe $channels
}
proc psubscribe
{
client channels
}
{
$client psubscribe
{*}
$channels
consume_subscribe_messages $client psubscribe $channels
}
proc punsubscribe
{
client
{
channels
{}}}
{
$client punsubscribe
{*}
$channels
consume_subscribe_messages $client punsubscribe $channels
}
}
\ No newline at end of file
tests/unit/acl.tcl
View file @
f5ca1f9e
...
@@ -113,6 +113,46 @@ start_server {tags {"acl"}} {
...
@@ -113,6 +113,46 @@ start_server {tags {"acl"}} {
set e
set e
}
{
*NOPERM*channel*
}
}
{
*NOPERM*channel*
}
test
{
Validate subset of channels is prefixed with resetchannels flag
}
{
r ACL setuser hpuser on nopass resetchannels &foo +@all
# Verify resetchannels flag is prefixed before the channel name
(
s
)
set users
[
r ACL LIST
]
set curruser
"hpuser"
foreach user
[
lshuffle $users
]
{
if
{[
string first $curruser $user
]
!= -1
}
{
assert_equal
{
user hpuser on nopass resetchannels &foo +@all
}
$user
}
}
# authenticate as hpuser
r AUTH hpuser pass
assert_equal
{
0
}
[
r PUBLISH foo bar
]
catch
{
r PUBLISH bar game
}
e
# Falling back to psuser for the below tests
r AUTH psuser pspass
r ACL deluser hpuser
set e
}
{
*NOPERM*channel*
}
test
{
In transaction queue publish/subscribe/psubscribe to unauthorized channel will fail
}
{
r ACL setuser psuser +multi +discard
r MULTI
catch
{
r PUBLISH notexits helloworld
}
e
r DISCARD
assert_match
{
*NOPERM*
}
$e
r MULTI
catch
{
r SUBSCRIBE notexits foo:1
}
e
r DISCARD
assert_match
{
*NOPERM*
}
$e
r MULTI
catch
{
r PSUBSCRIBE notexits:* bar:*
}
e
r DISCARD
assert_match
{
*NOPERM*
}
$e
}
test
{
It's possible to allow subscribing to a subset of channels
}
{
test
{
It's possible to allow subscribing to a subset of channels
}
{
set rd
[
redis_deferring_client
]
set rd
[
redis_deferring_client
]
$rd AUTH psuser pspass
$rd AUTH psuser pspass
...
@@ -409,6 +449,14 @@ start_server {tags {"acl"}} {
...
@@ -409,6 +449,14 @@ start_server {tags {"acl"}} {
set e
set e
}
{
*NOAUTH*
}
}
{
*NOAUTH*
}
test
{
When default user has no command permission, hello command still works for other users
}
{
r ACL setuser secure-user >supass on +@all
r ACL setuser default -@all
r HELLO 2 AUTH secure-user supass
r ACL setuser default nopass +@all
r AUTH default
""
}
test
{
ACL HELP should not have unexpected options
}
{
test
{
ACL HELP should not have unexpected options
}
{
catch
{
r ACL help xxx
}
e
catch
{
r ACL help xxx
}
e
assert_match
"*Unknown subcommand or wrong number of arguments*"
$e
assert_match
"*Unknown subcommand or wrong number of arguments*"
$e
...
@@ -437,14 +485,44 @@ exec cp -f tests/assets/user.acl $server_path
...
@@ -437,14 +485,44 @@ exec cp -f tests/assets/user.acl $server_path
start_server
[
list overrides
[
list
"dir"
$server_path
"aclfile"
"user.acl"
]]
{
start_server
[
list overrides
[
list
"dir"
$server_path
"aclfile"
"user.acl"
]]
{
# user alice on allcommands allkeys >alice
# user alice on allcommands allkeys >alice
# user bob on -@all +@set +acl ~set* >bob
# user bob on -@all +@set +acl ~set* >bob
# user default on nopass ~* +@all
test
{
default: load from include file, can access any channels
}
{
r SUBSCRIBE foo
r PSUBSCRIBE bar*
r UNSUBSCRIBE
r PUNSUBSCRIBE
r PUBLISH hello world
}
test
{
default: with config acl-pubsub-default allchannels after reset, can access any channels
}
{
r ACL setuser default reset on nopass ~* +@all
r SUBSCRIBE foo
r PSUBSCRIBE bar*
r UNSUBSCRIBE
r PUNSUBSCRIBE
r PUBLISH hello world
}
test
"Alice: can excute all command"
{
test
{
default: with config acl-pubsub-default resetchannels after reset, can not access any channels
}
{
r CONFIG SET acl-pubsub-default resetchannels
r ACL setuser default reset on nopass ~* +@all
catch
{
r SUBSCRIBE foo
}
e
assert_match
{
*NOPERM*
}
$e
catch
{
r PSUBSCRIBE bar*
}
e
assert_match
{
*NOPERM*
}
$e
catch
{
r PUBLISH hello world
}
e
assert_match
{
*NOPERM*
}
$e
r CONFIG SET acl-pubsub-default resetchannels
}
test
{
Alice: can execute all command
}
{
r AUTH alice alice
r AUTH alice alice
assert_equal
"alice"
[
r acl whoami
]
assert_equal
"alice"
[
r acl whoami
]
r SET key value
r SET key value
}
}
test
"
Bob: just excute @set and acl command
"
{
test
{
Bob: just ex
e
cute @set and acl command
}
{
r AUTH bob bob
r AUTH bob bob
assert_equal
"bob"
[
r acl whoami
]
assert_equal
"bob"
[
r acl whoami
]
assert_equal
"3"
[
r sadd set 1 2 3
]
assert_equal
"3"
[
r sadd set 1 2 3
]
...
@@ -452,7 +530,7 @@ start_server [list overrides [list "dir" $server_path "aclfile" "user.acl"]] {
...
@@ -452,7 +530,7 @@ start_server [list overrides [list "dir" $server_path "aclfile" "user.acl"]] {
set e
set e
}
{
*NOPERM*
}
}
{
*NOPERM*
}
test
"
ACL load and save
"
{
test
{
ACL load and save
}
{
r ACL setuser eve +get allkeys >eve on
r ACL setuser eve +get allkeys >eve on
r ACL save
r ACL save
...
@@ -469,4 +547,85 @@ start_server [list overrides [list "dir" $server_path "aclfile" "user.acl"]] {
...
@@ -469,4 +547,85 @@ start_server [list overrides [list "dir" $server_path "aclfile" "user.acl"]] {
catch
{
r SET key value
}
e
catch
{
r SET key value
}
e
set e
set e
}
{
*NOPERM*
}
}
{
*NOPERM*
}
test
{
ACL load and save with restricted channels
}
{
r AUTH alice alice
r ACL setuser harry on nopass resetchannels &test +@all ~*
r ACL save
# ACL load will free user and kill clients
r ACL load
catch
{
r ACL LIST
}
e
assert_match
{
*I/O error*
}
$e
reconnect
r AUTH harry anything
r publish test bar
catch
{
r publish test1 bar
}
e
r ACL deluser harry
set e
}
{
*NOPERM*
}
}
set server_path
[
tmpdir
"resetchannels.acl"
]
exec cp -f tests/assets/nodefaultuser.acl $server_path
exec cp -f tests/assets/default.conf $server_path
start_server
[
list overrides
[
list
"dir"
$server_path
"acl-pubsub-default"
"resetchannels"
"aclfile"
"nodefaultuser.acl"
]]
{
test
{
Default user has access to all channels irrespective of flag
}
{
set channelinfo
[
dict get
[
r ACL getuser default
]
channels
]
assert_equal
"*"
$channelinfo
set channelinfo
[
dict get
[
r ACL getuser alice
]
channels
]
assert_equal
""
$channelinfo
}
test
{
Update acl-pubsub-default, existing users shouldn't get affected
}
{
set channelinfo
[
dict get
[
r ACL getuser default
]
channels
]
assert_equal
"*"
$channelinfo
r CONFIG set acl-pubsub-default allchannels
r ACL setuser mydefault
set channelinfo
[
dict get
[
r ACL getuser mydefault
]
channels
]
assert_equal
"*"
$channelinfo
r CONFIG set acl-pubsub-default resetchannels
set channelinfo
[
dict get
[
r ACL getuser mydefault
]
channels
]
assert_equal
"*"
$channelinfo
}
test
{
Single channel is valid
}
{
r ACL setuser onechannel &test
set channelinfo
[
dict get
[
r ACL getuser onechannel
]
channels
]
assert_equal test $channelinfo
r ACL deluser onechannel
}
test
{
Single channel is not valid with allchannels
}
{
r CONFIG set acl-pubsub-default allchannels
catch
{
r ACL setuser onechannel &test
}
err
r CONFIG set acl-pubsub-default resetchannels
set err
}
{
*start with an empty list of channels*
}
}
set server_path
[
tmpdir
"resetchannels.acl"
]
exec cp -f tests/assets/nodefaultuser.acl $server_path
exec cp -f tests/assets/default.conf $server_path
start_server
[
list overrides
[
list
"dir"
$server_path
"acl-pubsub-default"
"resetchannels"
"aclfile"
"nodefaultuser.acl"
]]
{
test
{
Only default user has access to all channels irrespective of flag
}
{
set channelinfo
[
dict get
[
r ACL getuser default
]
channels
]
assert_equal
"*"
$channelinfo
set channelinfo
[
dict get
[
r ACL getuser alice
]
channels
]
assert_equal
""
$channelinfo
}
}
start_server
{
overrides
{
user
"default on nopass ~* +@all"
}}
{
test
{
default: load from config file, can access any channels
}
{
r SUBSCRIBE foo
r PSUBSCRIBE bar*
r UNSUBSCRIBE
r PUNSUBSCRIBE
r PUBLISH hello world
}
}
}
Prev
1
2
3
4
5
6
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