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
959d6035
Unverified
Commit
959d6035
authored
Apr 20, 2021
by
Oran Agra
Committed by
GitHub
Apr 20, 2021
Browse files
Merge 6.2.2 release
Release 6.2.2
parents
92bde124
aa730ef1
Changes
119
Hide whitespace changes
Inline
Side-by-side
tests/cluster/tests/20-half-migrated-slot.tcl
0 → 100644
View file @
959d6035
# Tests for fixing migrating slot at all stages:
# 1. when migration is half inited on
"migrating"
node
# 2. when migration is half inited on
"importing"
node
# 3. migration inited, but not finished
# 4. migration is half finished on
"migrating"
node
# 5. migration is half finished on
"importing"
node
# TODO: Test is currently disabled until it is stabilized
(
fixing the test
# itself or real issues in Redis
)
.
if
{
false
}
{
source
"../tests/includes/init-tests.tcl"
source
"../tests/includes/utils.tcl"
test
"Create a 2 nodes cluster"
{
create_cluster 2 0
config_set_all_nodes cluster-allow-replica-migration no
}
test
"Cluster is up"
{
assert_cluster_state ok
}
set cluster
[
redis_cluster 127.0.0.1:
[
get_instance_attrib redis 0 port
]]
catch
{
unset nodefrom
}
catch
{
unset nodeto
}
proc reset_cluster
{}
{
uplevel 1
{
$cluster refresh_nodes_map
array set nodefrom
[
$cluster
masternode_for_slot 609
]
array set nodeto
[
$cluster
masternode_notfor_slot 609
]
}
}
reset_cluster
$cluster
set aga xyz
test
"Half init migration in 'migrating' is fixable"
{
assert_equal
{
OK
}
[
$nodefrom
(
link
)
cluster setslot 609 migrating $nodeto
(
id
)]
fix_cluster $nodefrom
(
addr
)
assert_equal
"xyz"
[
$cluster
get aga
]
}
test
"Half init migration in 'importing' is fixable"
{
assert_equal
{
OK
}
[
$nodeto
(
link
)
cluster setslot 609 importing $nodefrom
(
id
)]
fix_cluster $nodefrom
(
addr
)
assert_equal
"xyz"
[
$cluster
get aga
]
}
test
"Init migration and move key"
{
assert_equal
{
OK
}
[
$nodefrom
(
link
)
cluster setslot 609 migrating $nodeto
(
id
)]
assert_equal
{
OK
}
[
$nodeto
(
link
)
cluster setslot 609 importing $nodefrom
(
id
)]
assert_equal
{
OK
}
[
$nodefrom
(
link
)
migrate $nodeto
(
host
)
$nodeto
(
port
)
aga 0 10000
]
wait_for_cluster_propagation
assert_equal
"xyz"
[
$cluster
get aga
]
fix_cluster $nodefrom
(
addr
)
assert_equal
"xyz"
[
$cluster
get aga
]
}
reset_cluster
test
"Move key again"
{
wait_for_cluster_propagation
assert_equal
{
OK
}
[
$nodefrom
(
link
)
cluster setslot 609 migrating $nodeto
(
id
)]
assert_equal
{
OK
}
[
$nodeto
(
link
)
cluster setslot 609 importing $nodefrom
(
id
)]
assert_equal
{
OK
}
[
$nodefrom
(
link
)
migrate $nodeto
(
host
)
$nodeto
(
port
)
aga 0 10000
]
wait_for_cluster_propagation
assert_equal
"xyz"
[
$cluster
get aga
]
}
test
"Half-finish migration"
{
# half finish migration on 'migrating' node
assert_equal
{
OK
}
[
$nodefrom
(
link
)
cluster setslot 609 node $nodeto
(
id
)]
fix_cluster $nodefrom
(
addr
)
assert_equal
"xyz"
[
$cluster
get aga
]
}
reset_cluster
test
"Move key back"
{
# 'aga' key is in 609 slot
assert_equal
{
OK
}
[
$nodefrom
(
link
)
cluster setslot 609 migrating $nodeto
(
id
)]
assert_equal
{
OK
}
[
$nodeto
(
link
)
cluster setslot 609 importing $nodefrom
(
id
)]
assert_equal
{
OK
}
[
$nodefrom
(
link
)
migrate $nodeto
(
host
)
$nodeto
(
port
)
aga 0 10000
]
assert_equal
"xyz"
[
$cluster
get aga
]
}
test
"Half-finish importing"
{
# Now we half finish 'importing' node
assert_equal
{
OK
}
[
$nodeto
(
link
)
cluster setslot 609 node $nodeto
(
id
)]
fix_cluster $nodefrom
(
addr
)
assert_equal
"xyz"
[
$cluster
get aga
]
}
config_set_all_nodes cluster-allow-replica-migration yes
}
tests/cluster/tests/21-many-slot-migration.tcl
0 → 100644
View file @
959d6035
# Tests for many simlutaneous migrations.
# TODO: Test is currently disabled until it is stabilized
(
fixing the test
# itself or real issues in Redis
)
.
if
{
false
}
{
source
"../tests/includes/init-tests.tcl"
source
"../tests/includes/utils.tcl"
# TODO: This test currently runs without replicas, as failovers
(
which may
# happen on lower-end CI platforms
)
are still not handled properly by the
# cluster during slot migration
(
related to #6339
)
.
test
"Create a 10 nodes cluster"
{
create_cluster 10 0
config_set_all_nodes cluster-allow-replica-migration no
}
test
"Cluster is up"
{
assert_cluster_state ok
}
set cluster
[
redis_cluster 127.0.0.1:
[
get_instance_attrib redis 0 port
]]
catch
{
unset nodefrom
}
catch
{
unset nodeto
}
$cluster
refresh_nodes_map
test
"Set many keys"
{
for
{
set i 0
}
{
$i
< 40000
}
{
incr i
}
{
$cluster set key:$i val:$i
}
}
test
"Keys are accessible"
{
for
{
set i 0
}
{
$i
< 40000
}
{
incr i
}
{
assert
{
[
$cluster
get key:$i
]
eq
"val:
$i
"
}
}
}
test
"Init migration of many slots"
{
for
{
set slot 0
}
{
$slot
< 1000
}
{
incr slot
}
{
array set nodefrom
[
$cluster
masternode_for_slot $slot
]
array set nodeto
[
$cluster
masternode_notfor_slot $slot
]
$nodefrom
(
link
)
cluster setslot $slot migrating $nodeto
(
id
)
$nodeto
(
link
)
cluster setslot $slot importing $nodefrom
(
id
)
}
}
test
"Fix cluster"
{
wait_for_cluster_propagation
fix_cluster $nodefrom
(
addr
)
}
test
"Keys are accessible"
{
for
{
set i 0
}
{
$i
< 40000
}
{
incr i
}
{
assert
{
[
$cluster
get key:$i
]
eq
"val:
$i
"
}
}
}
config_set_all_nodes cluster-allow-replica-migration yes
}
tests/cluster/tests/includes/utils.tcl
0 → 100644
View file @
959d6035
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 @
959d6035
...
...
@@ -64,6 +64,8 @@ proc exec_instance {type dirname cfgfile} {
proc spawn_instance
{
type base_port count
{
conf
{}}
{
base_conf_file
""
}}
{
for
{
set j 0
}
{
$j
< $count
}
{
incr j
}
{
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.
set dirname
"
${type}
_
${j}
"
lappend ::dirs $dirname
...
...
@@ -83,7 +85,9 @@ proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} {
puts $cfg
"tls-port
$port
"
puts $cfg
"tls-replication 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-key-file %s/../../tls/server.key"
[
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 ""}} {
set cfg
[
open $cfgfile a+
]
if
{
$::tls
}
{
puts $cfg
"tls-port
$port
"
set pport
[
find_available_port $base_port $::redis_port_count
]
puts $cfg
"port
$pport
"
}
else
{
puts $cfg
"port
$port
"
}
...
...
@@ -143,6 +149,7 @@ proc spawn_instance {type base_port count {conf {}} {base_conf_file ""}} {
pid $pid
\
host $::host
\
port $port
\
plaintext-port $pport
\
link $link
\
]
}
...
...
@@ -492,6 +499,14 @@ proc RI {n 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.
proc foreach_instance_id
{
instances idvar code
}
{
upvar 1 $idvar id
...
...
tests/integration/corrupt-dump.tcl
View file @
959d6035
...
...
@@ -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
tests/integration/psync2-pingoff.tcl
View file @
959d6035
...
...
@@ -111,7 +111,13 @@ start_server {} {
$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
$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
# replica4 will keep syncing from the old master which now syncs from replica1
...
...
@@ -195,10 +201,16 @@ start_server {} {
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
$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_replica
[
status $R
(
1
)
sync_partial_ok
]
$R
(
0
)
CONFIG SET repl-ping-replica-period 100
...
...
tests/integration/redis-cli.tcl
View file @
959d6035
...
...
@@ -207,6 +207,28 @@ start_server {tags {"cli"}} {
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"
{
assert_equal
"OK"
[
run_cli_with_input_pipe
"echo foo"
set key
]
assert_equal
"foo
\n
"
[
r get key
]
...
...
@@ -247,6 +269,20 @@ start_server {tags {"cli"}} {
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"
{
set fd
[
open_cli
"--replica"
]
wait_for_condition 500 500
{
...
...
tests/integration/replication-4.tcl
View file @
959d6035
...
...
@@ -79,12 +79,16 @@ start_server {tags {"repl"}} {
$master config set min-slaves-max-lag 2
$master config set min-slaves-to-write 1
assert
{[
$master
set foo bar
]
eq
{
OK
}}
$slave deferred 1
$slave debug sleep 6
after 4000
catch
{
$master
set foo bar
}
e
set e
}
{
NOREPLICAS*
}
exec kill -SIGSTOP
[
srv 0 pid
]
wait_for_condition 100 100
{
[
catch
{
$master
set foo bar
}]
!= 0
}
else
{
fail
"Master didn't become readonly"
}
catch
{
$master
set foo bar
}
err
assert_match
{
NOREPLICAS*
}
$err
exec kill -SIGCONT
[
srv 0 pid
]
}
}
}
...
...
tests/integration/replication.tcl
View file @
959d6035
...
...
@@ -196,9 +196,11 @@ start_server {tags {"repl"}} {
}
{
master
}
test
{
SLAVEOF should start with link status
"down"
}
{
r multi
r slaveof
[
srv -1 host
]
[
srv -1 port
]
s master_link_status
}
{
down
}
r info replication
r exec
}
{
*master_link_status:down*
}
test
{
The role should immediately be changed to
"replica"
}
{
s role
...
...
@@ -595,9 +597,9 @@ start_server {tags {"repl"}} {
$master debug populate 20000 test 10000
$master config set rdbcompression no
# 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
]
foreach all_drop
{
no slow fast all
}
{
foreach all_drop
{
no slow fast all
timeout
}
{
test
"diskless
$all
_drop replicas drop during rdb pipe"
{
set replicas
{}
set replicas_alive
{}
...
...
@@ -614,7 +616,7 @@ start_server {tags {"repl"}} {
# so that the whole rdb generation process is bound to that
set loglines
[
count_log_lines -1
]
[
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 1
]
replicaof $master_host $master_port
...
...
@@ -645,6 +647,12 @@ start_server {tags {"repl"}} {
exec kill
[
srv -1 pid
]
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_condition 500 100
{
...
...
@@ -663,6 +671,14 @@ start_server {tags {"repl"}} {
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
}
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
if
{
$measure
_time
}
{
...
...
@@ -676,7 +692,7 @@ start_server {tags {"repl"}} {
puts
"master utime:
$master
_utime"
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
_stime < 70
}
}
...
...
@@ -720,7 +736,7 @@ start_server {tags {"repl"}} {
test
"diskless replication child being killed is collected"
{
# 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
# 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"
}}
{
set master
[
srv 0 client
]
set master_host
[
srv 0 host
]
...
...
tests/modules/keyspace_events.c
View file @
959d6035
...
...
@@ -38,6 +38,8 @@
/** strores all the keys on which we got 'loaded' keyspace notification **/
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
){
REDISMODULE_NOT_USED
(
ctx
);
...
...
@@ -78,6 +80,50 @@ static int KeySpace_NotificationGeneric(RedisModuleCtx *ctx, int type, const cha
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
){
if
(
argc
!=
2
){
return
RedisModule_WrongArity
(
ctx
);
...
...
@@ -171,6 +217,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
}
loaded_event_log
=
RedisModule_CreateDict
(
ctx
);
module_event_log
=
RedisModule_CreateDict
(
ctx
);
int
keySpaceAll
=
RedisModule_GetKeyspaceNotificationFlagsAll
();
...
...
@@ -187,6 +234,18 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
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
){
return
REDISMODULE_ERR
;
}
...
...
@@ -219,6 +278,16 @@ int RedisModule_OnUnload(RedisModuleCtx *ctx) {
RedisModule_FreeString
(
ctx
,
val
);
}
RedisModule_FreeDict
(
ctx
,
loaded_event_log
);
RedisModule_DictIteratorStop
(
iter
);
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
;
}
tests/modules/propagate.c
View file @
959d6035
...
...
@@ -70,6 +70,44 @@ int propagateTestTimerCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int
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. */
void
*
threadMain
(
void
*
arg
)
{
REDISMODULE_NOT_USED
(
arg
);
...
...
@@ -131,6 +169,42 @@ int propagateTestMixedCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int
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
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
...
...
@@ -143,6 +217,16 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
""
,
1
,
1
,
1
)
==
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"
,
propagateTestThreadCommand
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
...
...
@@ -158,5 +242,15 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
""
,
1
,
1
,
1
)
==
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
;
}
tests/sentinel/tests/00-base.tcl
View file @
959d6035
...
...
@@ -9,7 +9,7 @@ if {$::simulate_error} {
}
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
]
assert
{[
lindex $addr 1
]
== $old_port
}
kill_instance redis $master_id
...
...
@@ -53,7 +53,7 @@ test "ODOWN is not possible without N (quorum) Sentinels reports" {
foreach_sentinel_id id
{
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
]
assert
{[
lindex $addr 1
]
== $old_port
}
kill_instance redis $master_id
...
...
tests/sentinel/tests/01-conf-update.tcl
View file @
959d6035
...
...
@@ -3,7 +3,7 @@
source
"../tests/includes/init-tests.tcl"
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
]
assert
{[
lindex $addr 1
]
== $old_port
}
...
...
tests/sentinel/tests/02-slaves-reconf.tcl
View file @
959d6035
...
...
@@ -10,7 +10,7 @@ source "../tests/includes/init-tests.tcl"
proc 02_test_slaves_replication
{}
{
uplevel 1
{
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
{
if
{
$id
== $master_id
}
continue
if
{[
instance_is_killed redis $id
]}
continue
...
...
@@ -28,7 +28,7 @@ proc 02_test_slaves_replication {} {
proc 02_crash_and_failover
{}
{
uplevel 1
{
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
]
assert
{[
lindex $addr 1
]
== $old_port
}
kill_instance redis $master_id
...
...
tests/sentinel/tests/05-manual.tcl
View file @
959d6035
...
...
@@ -3,7 +3,7 @@
source
"../tests/includes/init-tests.tcl"
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
]
assert
{[
lindex $addr 1
]
== $old_port
}
catch
{
S 0 SENTINEL FAILOVER mymaster
}
reply
...
...
tests/sentinel/tests/10-replica-priority.tcl
0 → 100644
View file @
959d6035
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 @
959d6035
...
...
@@ -4,7 +4,7 @@
#
# 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 get foo
# $c close
...
...
@@ -17,6 +17,7 @@ set ::redis_cluster::id 0
array set ::redis_cluster::startup_nodes
{}
array set ::redis_cluster::nodes
{}
array set ::redis_cluster::slots
{}
array set ::redis_cluster::tls
{}
# List of
"plain"
commands, which are commands where the sole key is always
# the first argument.
...
...
@@ -34,11 +35,14 @@ set ::redis_cluster::plain_commands {
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 ::redis_cluster::startup_nodes
(
$id
)
$nodes
set ::redis_cluster::nodes
(
$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
]
$handle refresh_nodes_map
return $handle
...
...
@@ -60,9 +64,10 @@ proc ::redis_cluster::__method__refresh_nodes_map {id} {
foreach start_node $::redis_cluster::startup_nodes
(
$id
)
{
set ip_port
[
lindex
[
split $start_node @
]
0
]
lassign
[
split $ip_port :
]
start_host start_port
set tls $::redis_cluster::tls
(
$id
)
if
{[
catch
{
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
]
$r close
}
e
]}
{
...
...
@@ -107,7 +112,8 @@ proc ::redis_cluster::__method__refresh_nodes_map {id} {
# Connect to the node
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.
set node
[
dict create
\
...
...
@@ -161,9 +167,32 @@ proc ::redis_cluster::__method__close {id} {
catch
{
unset ::redis_cluster::startup_nodes
(
$id
)}
catch
{
unset ::redis_cluster::nodes
(
$id
)}
catch
{
unset ::redis_cluster::slots
(
$id
)}
catch
{
unset ::redis_cluster::tls
(
$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
}
{
if
{[
info command ::redis_cluster::__method__$method
]
eq
{}}
{
# Get the keys from the command.
...
...
@@ -186,10 +215,15 @@ proc ::redis_cluster::__dispatch__ {id method args} {
# Execute the command in the node we think is the slot owner.
set retry 100
set asking 0
while
{[
incr retry -1
]}
{
if
{
$retry
< 5
}
{
after 100
}
set node
[
dict get $::redis_cluster::nodes
(
$id
)
$node_addr
]
set link
[
dict get $node link
]
if
{
$asking
}
{
$link ASKING
set asking 0
}
if
{[
catch
{
$link
$method
{*}
$args
}
e
]}
{
if
{
$link
eq
{}
||
\
[
string range $e 0 4
]
eq
{
MOVED
}
||
\
...
...
@@ -202,6 +236,7 @@ proc ::redis_cluster::__dispatch__ {id method args} {
}
elseif
{[
string range $e 0 2
]
eq
{
ASK
}}
{
# ASK redirection.
set node_addr
[
lindex $e 2
]
set asking 1
continue
}
else
{
# Non redirecting error.
...
...
tests/support/redis.tcl
View file @
959d6035
...
...
@@ -35,6 +35,7 @@ array set ::redis::addr {}
array set ::redis::blocking
{}
array set ::redis::deferred
{}
array set ::redis::reconnect
{}
array set ::redis::tls
{}
array set ::redis::callback
{}
array set ::redis::state
{}
;
# State in non-blocking reply reading
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 {}}} {
set ::redis::blocking
(
$id
)
1
set ::redis::deferred
(
$id
)
$defer
set ::redis::reconnect
(
$id
)
0
set ::redis::tls $tls
set ::redis::tls
(
$id
)
$tls
::redis::redis_reset_state $id
interp alias
{}
::redis::redisHandle$id
{}
::redis::__dispatch__ $id
}
...
...
@@ -83,7 +84,7 @@ proc ::redis::__dispatch__raw__ {id method argv} {
# Reconnect the link if needed.
if
{
$fd
eq
{}}
{
lassign $::redis::addr
(
$id
)
host port
if
{
$::redis::tls
}
{
if
{
$::redis::tls
(
$id
)
}
{
set ::redis::fd
(
$id
)
[
::tls::socket $host $port
]
}
else
{
set ::redis::fd
(
$id
)
[
socket $host $port
]
...
...
@@ -158,6 +159,7 @@ proc ::redis::__method__close {id fd} {
catch
{
unset ::redis::blocking
(
$id
)}
catch
{
unset ::redis::deferred
(
$id
)}
catch
{
unset ::redis::reconnect
(
$id
)}
catch
{
unset ::redis::tls
(
$id
)}
catch
{
unset ::redis::state
(
$id
)}
catch
{
unset ::redis::statestack
(
$id
)}
catch
{
unset ::redis::callback
(
$id
)}
...
...
tests/support/server.tcl
View file @
959d6035
...
...
@@ -253,7 +253,7 @@ proc wait_server_started {config_file stdout pid} {
# Check if the port is actually busy and the server failed
# 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
break
}
...
...
@@ -508,6 +508,7 @@ proc start_server {options {code undefined}} {
set num_tests $::num_tests
if
{[
catch
{
uplevel 1 $code
}
error
]}
{
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
)
set srv
[
lindex $::servers end
]
...
...
@@ -519,17 +520,23 @@ proc start_server {options {code undefined}} {
dict set srv
"skipleaks"
1
kill_server $srv
# Print warnings from log
puts
[
format
"
\n
Logged warnings (pid %d):"
[
dict get $srv
"pid"
]]
set warnings
[
warnings_from_file
[
dict get $srv
"stdout"
]]
if
{[
string length $warnings
]
> 0
}
{
puts
"
$warnings
"
if
{
$::dump
_logs && $assertion
}
{
# if we caught an assertion
(
$::num
_failed isn't incremented yet
)
# this happens when the test spawns a server and not the other way around
dump_server_log $srv
}
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
]
lappend details $msg
lappend details $backtrace
...
...
tests/support/test.tcl
View file @
959d6035
...
...
@@ -165,6 +165,8 @@ proc test {name code {okpattern undefined} {options undefined}} {
if
{[
catch
{
set retval
[
uplevel 1 $code
]}
error
]}
{
set assertion
[
string match
"assertion:*"
$error
]
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
]
lappend details $msg
if
{
!$assertion
}
{
...
...
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