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
e5187ad2
Commit
e5187ad2
authored
Jul 30, 2019
by
Oran Agra
Browse files
Merge remote-tracking branch 'oss/unstable' into module_rdb_load_errors
parents
4339706e
f42846e8
Changes
28
Hide whitespace changes
Inline
Side-by-side
src/t_zset.c
View file @
e5187ad2
...
...
@@ -1357,9 +1357,8 @@ int zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore) {
/* Optimize: check if the element is too large or the list
* becomes too long *before* executing zzlInsert. */
zobj
->
ptr
=
zzlInsert
(
zobj
->
ptr
,
ele
,
score
);
if
(
zzlLength
(
zobj
->
ptr
)
>
server
.
zset_max_ziplist_entries
)
zsetConvert
(
zobj
,
OBJ_ENCODING_SKIPLIST
);
if
(
sdslen
(
ele
)
>
server
.
zset_max_ziplist_value
)
if
(
zzlLength
(
zobj
->
ptr
)
>
server
.
zset_max_ziplist_entries
||
sdslen
(
ele
)
>
server
.
zset_max_ziplist_value
)
zsetConvert
(
zobj
,
OBJ_ENCODING_SKIPLIST
);
if
(
newscore
)
*
newscore
=
score
;
*
flags
|=
ZADD_ADDED
;
...
...
src/tracking.c
View file @
e5187ad2
...
...
@@ -60,6 +60,7 @@
* use the most significant bits instead of the full 24 bits. */
#define TRACKING_TABLE_SIZE (1<<24)
rax
**
TrackingTable
=
NULL
;
unsigned
long
TrackingTableUsedSlots
=
0
;
robj
*
TrackingChannelName
;
/* Remove the tracking state from the client 'c'. Note that there is not much
...
...
@@ -109,67 +110,187 @@ void trackingRememberKeys(client *c) {
sds
sdskey
=
c
->
argv
[
idx
]
->
ptr
;
uint64_t
hash
=
crc64
(
0
,
(
unsigned
char
*
)
sdskey
,
sdslen
(
sdskey
))
&
(
TRACKING_TABLE_SIZE
-
1
);
if
(
TrackingTable
[
hash
]
==
NULL
)
if
(
TrackingTable
[
hash
]
==
NULL
)
{
TrackingTable
[
hash
]
=
raxNew
();
TrackingTableUsedSlots
++
;
}
raxTryInsert
(
TrackingTable
[
hash
],
(
unsigned
char
*
)
&
c
->
id
,
sizeof
(
c
->
id
),
NULL
,
NULL
);
}
getKeysFreeResult
(
keys
);
}
/* This function is called from signalModifiedKey() or other places in Redis
* when a key changes value. In the context of keys tracking, our task here is
* to send a notification to every client that may have keys about such . */
void
trackingInvalidateKey
(
robj
*
keyobj
)
{
sds
sdskey
=
keyobj
->
ptr
;
uint64_t
hash
=
crc64
(
0
,
(
unsigned
char
*
)
sdskey
,
sdslen
(
sdskey
))
&
(
TRACKING_TABLE_SIZE
-
1
);
if
(
TrackingTable
==
NULL
||
TrackingTable
[
hash
]
==
NULL
)
return
;
void
sendTrackingMessage
(
client
*
c
,
long
long
hash
)
{
int
using_redirection
=
0
;
if
(
c
->
client_tracking_redirection
)
{
client
*
redir
=
lookupClientByID
(
c
->
client_tracking_redirection
);
if
(
!
redir
)
{
/* We need to signal to the original connection that we
* are unable to send invalidation messages to the redirected
* connection, because the client no longer exist. */
if
(
c
->
resp
>
2
)
{
addReplyPushLen
(
c
,
3
);
addReplyBulkCBuffer
(
c
,
"tracking-redir-broken"
,
21
);
addReplyLongLong
(
c
,
c
->
client_tracking_redirection
);
}
return
;
}
c
=
redir
;
using_redirection
=
1
;
}
/* Only send such info for clients in RESP version 3 or more. However
* if redirection is active, and the connection we redirect to is
* in Pub/Sub mode, we can support the feature with RESP 2 as well,
* by sending Pub/Sub messages in the __redis__:invalidate channel. */
if
(
c
->
resp
>
2
)
{
addReplyPushLen
(
c
,
2
);
addReplyBulkCBuffer
(
c
,
"invalidate"
,
10
);
addReplyLongLong
(
c
,
hash
);
}
else
if
(
using_redirection
&&
c
->
flags
&
CLIENT_PUBSUB
)
{
robj
*
msg
=
createStringObjectFromLongLong
(
hash
);
addReplyPubsubMessage
(
c
,
TrackingChannelName
,
msg
);
decrRefCount
(
msg
);
}
}
/* Invalidates a caching slot: this is actually the low level implementation
* of the API that Redis calls externally, that is trackingInvalidateKey(). */
void
trackingInvalidateSlot
(
uint64_t
slot
)
{
if
(
TrackingTable
==
NULL
||
TrackingTable
[
slot
]
==
NULL
)
return
;
raxIterator
ri
;
raxStart
(
&
ri
,
TrackingTable
[
hash
]);
raxStart
(
&
ri
,
TrackingTable
[
slot
]);
raxSeek
(
&
ri
,
"^"
,
NULL
,
0
);
while
(
raxNext
(
&
ri
))
{
uint64_t
id
;
memcpy
(
&
id
,
ri
.
key
,
ri
.
key_len
);
client
*
c
=
lookupClientByID
(
id
);
if
(
c
==
NULL
)
continue
;
int
using_redirection
=
0
;
if
(
c
->
client_tracking_redirection
)
{
client
*
redir
=
lookupClientByID
(
c
->
client_tracking_redirection
);
if
(
!
redir
)
{
/* We need to signal to the original connection that we
* are unable to send invalidation messages to the redirected
* connection, because the client no longer exist. */
if
(
c
->
resp
>
2
)
{
addReplyPushLen
(
c
,
3
);
addReplyBulkCBuffer
(
c
,
"tracking-redir-broken"
,
21
);
addReplyLongLong
(
c
,
c
->
client_tracking_redirection
);
}
continue
;
if
(
c
==
NULL
||
!
(
c
->
flags
&
CLIENT_TRACKING
))
continue
;
sendTrackingMessage
(
c
,
slot
);
}
raxStop
(
&
ri
);
/* Free the tracking table: we'll create the radix tree and populate it
* again if more keys will be modified in this caching slot. */
raxFree
(
TrackingTable
[
slot
]);
TrackingTable
[
slot
]
=
NULL
;
TrackingTableUsedSlots
--
;
}
/* This function is called from signalModifiedKey() or other places in Redis
* when a key changes value. In the context of keys tracking, our task here is
* to send a notification to every client that may have keys about such caching
* slot. */
void
trackingInvalidateKey
(
robj
*
keyobj
)
{
if
(
TrackingTable
==
NULL
||
TrackingTableUsedSlots
==
0
)
return
;
sds
sdskey
=
keyobj
->
ptr
;
uint64_t
hash
=
crc64
(
0
,
(
unsigned
char
*
)
sdskey
,
sdslen
(
sdskey
))
&
(
TRACKING_TABLE_SIZE
-
1
);
trackingInvalidateSlot
(
hash
);
}
/* This function is called when one or all the Redis databases are flushed
* (dbid == -1 in case of FLUSHALL). Caching slots are not specific for
* each DB but are global: currently what we do is sending a special
* notification to clients with tracking enabled, invalidating the caching
* slot "-1", which means, "all the keys", in order to avoid flooding clients
* with many invalidation messages for all the keys they may hold.
*
* However trying to flush the tracking table here is very costly:
* we need scanning 16 million caching slots in the table to check
* if they are used, this introduces a big delay. So what we do is to really
* flush the table in the case of FLUSHALL. When a FLUSHDB is called instead
* we just send the invalidation message to all the clients, but don't
* flush the table: it will slowly get garbage collected as more keys
* are modified in the used caching slots. */
void
trackingInvalidateKeysOnFlush
(
int
dbid
)
{
if
(
server
.
tracking_clients
)
{
listNode
*
ln
;
listIter
li
;
listRewind
(
server
.
clients
,
&
li
);
while
((
ln
=
listNext
(
&
li
))
!=
NULL
)
{
client
*
c
=
listNodeValue
(
ln
);
if
(
c
->
flags
&
CLIENT_TRACKING
)
{
sendTrackingMessage
(
c
,
-
1
);
}
}
}
/* In case of FLUSHALL, reclaim all the memory used by tracking. */
if
(
dbid
==
-
1
&&
TrackingTable
)
{
for
(
int
j
=
0
;
j
<
TRACKING_TABLE_SIZE
&&
TrackingTableUsedSlots
>
0
;
j
++
)
{
if
(
TrackingTable
[
j
]
!=
NULL
)
{
raxFree
(
TrackingTable
[
j
]);
TrackingTable
[
j
]
=
NULL
;
TrackingTableUsedSlots
--
;
}
c
=
redir
;
using_redirection
=
1
;
}
/* Only send such info for clients in RESP version 3 or more. However
* if redirection is active, and the connection we redirect to is
* in Pub/Sub mode, we can support the feature with RESP 2 as well,
* by sending Pub/Sub messages in the __redis__:invalidate channel. */
if
(
c
->
resp
>
2
)
{
addReplyPushLen
(
c
,
2
);
addReplyBulkCBuffer
(
c
,
"invalidate"
,
10
);
addReplyLongLong
(
c
,
hash
);
}
else
if
(
using_redirection
&&
c
->
flags
&
CLIENT_PUBSUB
)
{
robj
*
msg
=
createStringObjectFromLongLong
(
hash
);
addReplyPubsubMessage
(
c
,
TrackingChannelName
,
msg
);
decrRefCount
(
msg
);
/* If there are no clients with tracking enabled, we can even
* reclaim the memory used by the table itself. The code assumes
* the table is allocated only if there is at least one client alive
* with tracking enabled. */
if
(
server
.
tracking_clients
==
0
)
{
zfree
(
TrackingTable
);
TrackingTable
=
NULL
;
}
}
raxStop
(
&
ri
);
}
/* Free the tracking table: we'll create the radix tree and populate it
* again if more keys will be modified in this hash slot. */
raxFree
(
TrackingTable
[
hash
]);
TrackingTable
[
hash
]
=
NULL
;
/* Tracking forces Redis to remember information about which client may have
* keys about certian caching slots. In workloads where there are a lot of
* reads, but keys are hardly modified, the amount of information we have
* to remember server side could be a lot: for each 16 millions of caching
* slots we may end with a radix tree containing many entries.
*
* So Redis allows the user to configure a maximum fill rate for the
* invalidation table. This function makes sure that we don't go over the
* specified fill rate: if we are over, we can just evict informations about
* random caching slots, and send invalidation messages to clients like if
* the key was modified. */
void
trackingLimitUsedSlots
(
void
)
{
static
unsigned
int
timeout_counter
=
0
;
if
(
server
.
tracking_table_max_fill
==
0
)
return
;
/* No limits set. */
unsigned
int
max_slots
=
(
TRACKING_TABLE_SIZE
/
100
)
*
server
.
tracking_table_max_fill
;
if
(
TrackingTableUsedSlots
<=
max_slots
)
{
timeout_counter
=
0
;
return
;
/* Limit not reached. */
}
/* We have to invalidate a few slots to reach the limit again. The effort
* we do here is proportional to the number of times we entered this
* function and found that we are still over the limit. */
int
effort
=
100
*
(
timeout_counter
+
1
);
/* Let's start at a random position, and perform linear probing, in order
* to improve cache locality. However once we are able to find an used
* slot, jump again randomly, in order to avoid creating big holes in the
* table (that will make this funciton use more resourced later). */
while
(
effort
>
0
)
{
unsigned
int
idx
=
rand
()
%
TRACKING_TABLE_SIZE
;
do
{
effort
--
;
idx
=
(
idx
+
1
)
%
TRACKING_TABLE_SIZE
;
if
(
TrackingTable
[
idx
]
!=
NULL
)
{
trackingInvalidateSlot
(
idx
);
if
(
TrackingTableUsedSlots
<=
max_slots
)
{
timeout_counter
=
0
;
return
;
/* Return ASAP: we are again under the limit. */
}
else
{
break
;
/* Jump to next random position. */
}
}
}
while
(
effort
>
0
);
}
timeout_counter
++
;
}
/* This is just used in order to access the amount of used slots in the
* tracking table. */
unsigned
long
long
trackingGetUsedSlots
(
void
)
{
return
TrackingTableUsedSlots
;
}
tests/integration/replication.tcl
View file @
e5187ad2
...
...
@@ -192,12 +192,6 @@ foreach mdl {no yes} {
set master_host
[
srv 0 host
]
set master_port
[
srv 0 port
]
set slaves
{}
set load_handle0
[
start_bg_complex_data $master_host $master_port 9 100000000
]
set load_handle1
[
start_bg_complex_data $master_host $master_port 11 100000000
]
set load_handle2
[
start_bg_complex_data $master_host $master_port 12 100000000
]
set load_handle3
[
start_write_load $master_host $master_port 8
]
set load_handle4
[
start_write_load $master_host $master_port 4
]
after 5000
;
# wait for some data to accumulate so that we have RDB part for the fork
start_server
{}
{
lappend slaves
[
srv 0 client
]
start_server
{}
{
...
...
@@ -205,6 +199,14 @@ foreach mdl {no yes} {
start_server
{}
{
lappend slaves
[
srv 0 client
]
test
"Connect multiple replicas at the same time (issue #141), master diskless=
$mdl
, replica diskless=
$sdl
"
{
# start load handles only inside the test, so that the test can be skipped
set load_handle0
[
start_bg_complex_data $master_host $master_port 9 100000000
]
set load_handle1
[
start_bg_complex_data $master_host $master_port 11 100000000
]
set load_handle2
[
start_bg_complex_data $master_host $master_port 12 100000000
]
set load_handle3
[
start_write_load $master_host $master_port 8
]
set load_handle4
[
start_write_load $master_host $master_port 4
]
after 5000
;
# wait for some data to accumulate so that we have RDB part for the fork
# Send SLAVEOF commands to slaves
[
lindex $slaves 0
]
config set repl-diskless-load $sdl
[
lindex $slaves 1
]
config set repl-diskless-load $sdl
...
...
@@ -278,9 +280,9 @@ start_server {tags {"repl"}} {
set master
[
srv 0 client
]
set master_host
[
srv 0 host
]
set master_port
[
srv 0 port
]
set load_handle0
[
start_write_load $master_host $master_port 3
]
start_server
{}
{
test
"Master stream is correctly processed while the replica has a script in -BUSY state"
{
set load_handle0
[
start_write_load $master_host $master_port 3
]
set slave
[
srv 0 client
]
$slave config set lua-time-limit 500
$slave slaveof $master_host $master_port
...
...
@@ -383,3 +385,84 @@ test {slave fails full sync and diskless load swapdb recoveres it} {
}
}
}
test
{
diskless loading short read
}
{
start_server
{
tags
{
"repl"
}}
{
set replica
[
srv 0 client
]
set replica_host
[
srv 0 host
]
set replica_port
[
srv 0 port
]
start_server
{}
{
set master
[
srv 0 client
]
set master_host
[
srv 0 host
]
set master_port
[
srv 0 port
]
# Set master and replica to use diskless replication
$master config set repl-diskless-sync yes
$master config set rdbcompression no
$replica config set repl-diskless-load swapdb
# Try to fill the master with all types of data types / encodings
for
{
set k 0
}
{
$k
< 3
}
{
incr k
}
{
for
{
set i 0
}
{
$i
< 10
}
{
incr i
}
{
r set
"
$k
int_
$i
"
[
expr
{
int
(
rand
()
*10000
)}]
r expire
"
$k
int_
$i
"
[
expr
{
int
(
rand
()
*10000
)}]
r set
"
$k
string_
$i
"
[
string repeat A
[
expr
{
int
(
rand
()
*1000000
)}]]
r hset
"
$k
hash_small"
[
string repeat A
[
expr
{
int
(
rand
()
*10
)}]]
0
[
string repeat A
[
expr
{
int
(
rand
()
*10
)}]]
r hset
"
$k
hash_large"
[
string repeat A
[
expr
{
int
(
rand
()
*10000
)}]]
[
string repeat A
[
expr
{
int
(
rand
()
*1000000
)}]]
r sadd
"
$k
set_small"
[
string repeat A
[
expr
{
int
(
rand
()
*10
)}]]
r sadd
"
$k
set_large"
[
string repeat A
[
expr
{
int
(
rand
()
*1000000
)}]]
r zadd
"
$k
zset_small"
[
expr
{
rand
()}]
[
string repeat A
[
expr
{
int
(
rand
()
*10
)}]]
r zadd
"
$k
zset_large"
[
expr
{
rand
()}]
[
string repeat A
[
expr
{
int
(
rand
()
*1000000
)}]]
r lpush
"
$k
list_small"
[
string repeat A
[
expr
{
int
(
rand
()
*10
)}]]
r lpush
"
$k
list_large"
[
string repeat A
[
expr
{
int
(
rand
()
*1000000
)}]]
for
{
set j 0
}
{
$j
< 10
}
{
incr j
}
{
r xadd
"
$k
stream"
* foo
"asdf"
bar
"1234"
}
r xgroup create
"
$k
stream"
"mygroup_
$i
"
0
r xreadgroup GROUP
"mygroup_
$i
"
Alice COUNT 1 STREAMS
"
$k
stream"
>
}
}
# Start the replication process...
$master config set repl-diskless-sync-delay 0
$replica replicaof $master_host $master_port
# kill the replication at various points
set attempts 3
if
{
$::accurate
}
{
set attempts 10
}
for
{
set i 0
}
{
$i
< $attempts
}
{
incr i
}
{
# wait for the replica to start reading the rdb
# using the log file since the replica only responds to INFO once in 2mb
wait_for_log_message -1
"*Loading DB in memory*"
5 2000 1
# add some additional random sleep so that we kill the master on a different place each time
after
[
expr
{
int
(
rand
()
*100
)}]
# kill the replica connection on the master
set killed
[
$master
client kill type replica
]
if
{[
catch
{
set res
[
wait_for_log_message -1
"*Internal error in RDB*"
5 100 10
]
if
{
$::verbose
}
{
puts $res
}
}]}
{
puts
"failed triggering short read"
# force the replica to try another full sync
$master client kill type replica
$master set asdf asdf
# the side effect of resizing the backlog is that it is flushed
(
16k is the min size
)
$master config set repl-backlog-size
[
expr
{
16384 + $i
}]
}
# wait for loading to stop
(
fail
)
wait_for_condition 100 10
{
[
s -1 loading
]
eq 0
}
else
{
fail
"Replica didn't disconnect"
}
}
# enable fast shutdown
$master config set rdb-key-save-delay 0
}
}
}
tests/modules/Makefile
View file @
e5187ad2
...
...
@@ -13,12 +13,16 @@ endif
.SUFFIXES
:
.c .so .xo .o
all
:
commandfilter.so
all
:
commandfilter.so
testrdb.so
.c.xo
:
$(CC)
-I
../../src
$(CFLAGS)
$(SHOBJ_CFLAGS)
-fPIC
-c
$<
-o
$@
commandfilter.xo
:
../../src/redismodule.h
testrdb.xo
:
../../src/redismodule.h
commandfilter.so
:
commandfilter.xo
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
testrdb.so
:
testrdb.xo
$(LD)
-o
$@
$<
$(SHOBJ_LDFLAGS)
$(LIBS)
-lc
tests/modules/testrdb.c
0 → 100644
View file @
e5187ad2
#include "redismodule.h"
#include <string.h>
#include <assert.h>
/* Module configuration, save aux or not? */
long
long
conf_aux_count
=
0
;
/* Registered type */
RedisModuleType
*
testrdb_type
=
NULL
;
/* Global values to store and persist to aux */
RedisModuleString
*
before_str
=
NULL
;
RedisModuleString
*
after_str
=
NULL
;
void
*
testrdb_type_load
(
RedisModuleIO
*
rdb
,
int
encver
)
{
int
count
=
RedisModule_LoadSigned
(
rdb
);
assert
(
count
==
1
);
assert
(
encver
==
1
);
RedisModuleString
*
str
=
RedisModule_LoadString
(
rdb
);
return
str
;
}
void
testrdb_type_save
(
RedisModuleIO
*
rdb
,
void
*
value
)
{
RedisModuleString
*
str
=
(
RedisModuleString
*
)
value
;
RedisModule_SaveSigned
(
rdb
,
1
);
RedisModule_SaveString
(
rdb
,
str
);
}
void
testrdb_aux_save
(
RedisModuleIO
*
rdb
,
int
when
)
{
if
(
conf_aux_count
==
1
)
assert
(
when
==
REDISMODULE_AUX_AFTER_RDB
);
if
(
conf_aux_count
==
0
)
assert
(
0
);
if
(
when
==
REDISMODULE_AUX_BEFORE_RDB
)
{
if
(
before_str
)
{
RedisModule_SaveSigned
(
rdb
,
1
);
RedisModule_SaveString
(
rdb
,
before_str
);
}
else
{
RedisModule_SaveSigned
(
rdb
,
0
);
}
}
else
{
if
(
after_str
)
{
RedisModule_SaveSigned
(
rdb
,
1
);
RedisModule_SaveString
(
rdb
,
after_str
);
}
else
{
RedisModule_SaveSigned
(
rdb
,
0
);
}
}
}
int
testrdb_aux_load
(
RedisModuleIO
*
rdb
,
int
encver
,
int
when
)
{
assert
(
encver
==
1
);
if
(
conf_aux_count
==
1
)
assert
(
when
==
REDISMODULE_AUX_AFTER_RDB
);
if
(
conf_aux_count
==
0
)
assert
(
0
);
RedisModuleCtx
*
ctx
=
RedisModule_GetContextFromIO
(
rdb
);
if
(
when
==
REDISMODULE_AUX_BEFORE_RDB
)
{
if
(
before_str
)
RedisModule_FreeString
(
ctx
,
before_str
);
before_str
=
NULL
;
int
count
=
RedisModule_LoadSigned
(
rdb
);
if
(
count
)
before_str
=
RedisModule_LoadString
(
rdb
);
}
else
{
if
(
after_str
)
RedisModule_FreeString
(
ctx
,
after_str
);
after_str
=
NULL
;
int
count
=
RedisModule_LoadSigned
(
rdb
);
if
(
count
)
after_str
=
RedisModule_LoadString
(
rdb
);
}
return
REDISMODULE_OK
;
}
void
testrdb_type_free
(
void
*
value
)
{
RedisModule_FreeString
(
NULL
,
(
RedisModuleString
*
)
value
);
}
int
testrdb_set_before
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
2
)
{
RedisModule_WrongArity
(
ctx
);
return
REDISMODULE_OK
;
}
if
(
before_str
)
RedisModule_FreeString
(
ctx
,
before_str
);
before_str
=
argv
[
1
];
RedisModule_RetainString
(
ctx
,
argv
[
1
]);
RedisModule_ReplyWithLongLong
(
ctx
,
1
);
return
REDISMODULE_OK
;
}
int
testrdb_get_before
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
if
(
argc
!=
1
){
RedisModule_WrongArity
(
ctx
);
return
REDISMODULE_OK
;
}
if
(
before_str
)
RedisModule_ReplyWithString
(
ctx
,
before_str
);
else
RedisModule_ReplyWithStringBuffer
(
ctx
,
""
,
0
);
return
REDISMODULE_OK
;
}
int
testrdb_set_after
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
2
){
RedisModule_WrongArity
(
ctx
);
return
REDISMODULE_OK
;
}
if
(
after_str
)
RedisModule_FreeString
(
ctx
,
after_str
);
after_str
=
argv
[
1
];
RedisModule_RetainString
(
ctx
,
argv
[
1
]);
RedisModule_ReplyWithLongLong
(
ctx
,
1
);
return
REDISMODULE_OK
;
}
int
testrdb_get_after
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
if
(
argc
!=
1
){
RedisModule_WrongArity
(
ctx
);
return
REDISMODULE_OK
;
}
if
(
after_str
)
RedisModule_ReplyWithString
(
ctx
,
after_str
);
else
RedisModule_ReplyWithStringBuffer
(
ctx
,
""
,
0
);
return
REDISMODULE_OK
;
}
int
testrdb_set_key
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
3
){
RedisModule_WrongArity
(
ctx
);
return
REDISMODULE_OK
;
}
RedisModuleKey
*
key
=
RedisModule_OpenKey
(
ctx
,
argv
[
1
],
REDISMODULE_WRITE
);
RedisModuleString
*
str
=
RedisModule_ModuleTypeGetValue
(
key
);
if
(
str
)
RedisModule_FreeString
(
ctx
,
str
);
RedisModule_ModuleTypeSetValue
(
key
,
testrdb_type
,
argv
[
2
]);
RedisModule_RetainString
(
ctx
,
argv
[
2
]);
RedisModule_CloseKey
(
key
);
RedisModule_ReplyWithLongLong
(
ctx
,
1
);
return
REDISMODULE_OK
;
}
int
testrdb_get_key
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
(
argc
!=
2
){
RedisModule_WrongArity
(
ctx
);
return
REDISMODULE_OK
;
}
RedisModuleKey
*
key
=
RedisModule_OpenKey
(
ctx
,
argv
[
1
],
REDISMODULE_WRITE
);
RedisModuleString
*
str
=
RedisModule_ModuleTypeGetValue
(
key
);
RedisModule_CloseKey
(
key
);
RedisModule_ReplyWithString
(
ctx
,
str
);
return
REDISMODULE_OK
;
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"testrdb"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
argc
>
0
)
RedisModule_StringToLongLong
(
argv
[
0
],
&
conf_aux_count
);
if
(
conf_aux_count
==
0
)
{
RedisModuleTypeMethods
datatype_methods
=
{
.
version
=
1
,
.
rdb_load
=
testrdb_type_load
,
.
rdb_save
=
testrdb_type_save
,
.
aof_rewrite
=
NULL
,
.
digest
=
NULL
,
.
free
=
testrdb_type_free
,
};
testrdb_type
=
RedisModule_CreateDataType
(
ctx
,
"test__rdb"
,
1
,
&
datatype_methods
);
if
(
testrdb_type
==
NULL
)
return
REDISMODULE_ERR
;
}
else
{
RedisModuleTypeMethods
datatype_methods
=
{
.
version
=
REDISMODULE_TYPE_METHOD_VERSION
,
.
rdb_load
=
testrdb_type_load
,
.
rdb_save
=
testrdb_type_save
,
.
aof_rewrite
=
NULL
,
.
digest
=
NULL
,
.
free
=
testrdb_type_free
,
.
aux_load
=
testrdb_aux_load
,
.
aux_save
=
testrdb_aux_save
,
.
aux_save_triggers
=
(
conf_aux_count
==
1
?
REDISMODULE_AUX_AFTER_RDB
:
REDISMODULE_AUX_BEFORE_RDB
|
REDISMODULE_AUX_AFTER_RDB
)
};
testrdb_type
=
RedisModule_CreateDataType
(
ctx
,
"test__rdb"
,
1
,
&
datatype_methods
);
if
(
testrdb_type
==
NULL
)
return
REDISMODULE_ERR
;
}
if
(
RedisModule_CreateCommand
(
ctx
,
"testrdb.set.before"
,
testrdb_set_before
,
"deny-oom"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"testrdb.get.before"
,
testrdb_get_before
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"testrdb.set.after"
,
testrdb_set_after
,
"deny-oom"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"testrdb.get.after"
,
testrdb_get_after
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"testrdb.set.key"
,
testrdb_set_key
,
"deny-oom"
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"testrdb.get.key"
,
testrdb_get_key
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
tests/support/util.tcl
View file @
e5187ad2
...
...
@@ -99,6 +99,25 @@ proc wait_for_ofs_sync {r1 r2} {
}
}
proc wait_for_log_message
{
srv_idx pattern last_lines maxtries delay
}
{
set retry $maxtries
set stdout
[
srv $srv_idx stdout
]
while
{
$retry
}
{
set result
[
exec tail -$last_lines < $stdout
]
set result
[
split $result
"
\n
"
]
foreach line $result
{
if
{[
string match $pattern $line
]}
{
return $line
}
}
incr retry -1
after $delay
}
if
{
$retry
== 0
}
{
fail
"log message of '
$pattern
' not found"
}
}
# Random integer between 0 and max
(
excluded
)
.
proc randomInt
{
max
}
{
expr
{
int
(
rand
()
*$max
)}
...
...
tests/unit/geo.tcl
View file @
e5187ad2
...
...
@@ -61,6 +61,7 @@ set regression_vectors {
{
939895 151 59.149620271823181 65.204186651485145
}
{
1412 156 149.29737817929004 15.95807862745508
}
{
564862 149 84.062063109158544 -65.685403922426232
}
{
1546032440391 16751 -1.8175081637769495 20.665668878082954
}
}
set rv_idx 0
...
...
@@ -274,8 +275,19 @@ start_server {tags {"geo"}} {
foreach place $diff
{
set mydist
[
geo_distance $lon $lat $search_lon $search_lat
]
set mydist
[
expr $mydist/1000
]
if
{(
$mydist
/ $radius_km
)
> 0.999
}
{
incr rounding_errors
}
if
{(
$mydist
/ $radius_km
)
> 0.999
}
{
incr rounding_errors
continue
}
if
{
$mydist
< $radius_m
}
{
# This is a false positive for redis since given the
# same points the higher precision calculation provided
# by TCL shows the point within range
incr rounding_errors
continue
}
}
# Make sure this is a real error and not a rounidng issue.
if
{[
llength $diff
]
== $rounding_errors
}
{
set res $res2
;
# Error silenced
...
...
tests/unit/moduleapi/testrdb.tcl
0 → 100644
View file @
e5187ad2
set testmodule
[
file normalize tests/modules/testrdb.so
]
proc restart_and_wait
{}
{
catch
{
r debug restart
}
# wait for the server to come back up
set retry 50
while
{
$retry
}
{
if
{[
catch
{
r ping
}]}
{
after 100
}
else
{
break
}
incr retry -1
}
}
tags
"modules"
{
start_server
[
list overrides
[
list loadmodule
"
$testmodule
"
]]
{
test
{
modules are able to persist types
}
{
r testrdb.set.key key1 value1
assert_equal
"value1"
[
r testrdb.get.key key1
]
r debug reload
assert_equal
"value1"
[
r testrdb.get.key key1
]
}
test
{
modules global are lost without aux
}
{
r testrdb.set.before global1
assert_equal
"global1"
[
r testrdb.get.before
]
restart_and_wait
assert_equal
""
[
r testrdb.get.before
]
}
}
start_server
[
list overrides
[
list loadmodule
"
$testmodule
2"
]]
{
test
{
modules are able to persist globals before and after
}
{
r testrdb.set.before global1
r testrdb.set.after global2
assert_equal
"global1"
[
r testrdb.get.before
]
assert_equal
"global2"
[
r testrdb.get.after
]
restart_and_wait
assert_equal
"global1"
[
r testrdb.get.before
]
assert_equal
"global2"
[
r testrdb.get.after
]
}
}
start_server
[
list overrides
[
list loadmodule
"
$testmodule
1"
]]
{
test
{
modules are able to persist globals just after
}
{
r testrdb.set.after global2
assert_equal
"global2"
[
r testrdb.get.after
]
restart_and_wait
assert_equal
"global2"
[
r testrdb.get.after
]
}
}
# TODO: test short read handling
}
Prev
1
2
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