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
a83e3663
Commit
a83e3663
authored
Feb 28, 2022
by
Oran Agra
Browse files
Merge remote-tracking branch 'origin/unstable' into 7.0
parents
d5915a16
5860fa3d
Changes
142
Show whitespace changes
Inline
Side-by-side
tests/integration/replication-4.tcl
View file @
a83e3663
...
...
@@ -46,6 +46,12 @@ start_server {tags {"repl external:skip"}} {
set master_port
[
srv -1 port
]
set slave
[
srv 0 client
]
# Load some functions to be used later
$master FUNCTION load lua test replace
{
redis.register_function
{
function_name='f_default_flags', callback=function
(
keys, args
)
return redis.call
(
'get',keys
[
1
])
end, flags=
{}}
redis.register_function
{
function_name='f_no_writes', callback=function
(
keys, args
)
return redis.call
(
'get',keys
[
1
])
end, flags=
{
'no-writes'
}}
}
test
{
First server should have role slave after SLAVEOF
}
{
$slave slaveof $master_host $master_port
wait_replica_online $master
...
...
@@ -54,28 +60,46 @@ start_server {tags {"repl external:skip"}} {
test
{
With min-slaves-to-write
(
1,3
)
: master should be writable
}
{
$master config set min-slaves-max-lag 3
$master config set min-slaves-to-write 1
$master set foo bar
}
{
OK
}
assert_equal OK
[
$master
set foo 123
]
assert_equal OK
[
$master
eval
"return redis.call('set','foo',12345)"
0
]
}
test
{
With min-slaves-to-write
(
2,3
)
: master should not be writable
}
{
$master config set min-slaves-max-lag 3
$master config set min-slaves-to-write 2
catch
{
$master
set foo bar
}
e
set e
}
{
NOREPLICAS*
}
assert_error
"*NOREPLICAS*"
{
$master
set foo bar
}
assert_error
"*NOREPLICAS*"
{
$master
eval
"redis.call('set','foo','bar')"
0
}
}
test
{
With min-slaves-to-write function without no-write flag
}
{
assert_error
"*NOREPLICAS*"
{
$master
fcall f_default_flags 1 foo
}
assert_equal
"12345"
[
$master
fcall f_no_writes 1 foo
]
}
test
{
With not enough good slaves, read in Lua script is still accepted
}
{
$master config set min-slaves-max-lag 3
$master config set min-slaves-to-write 1
$master eval
"redis.call('set','foo','bar')"
0
$master config set min-slaves-to-write 2
$master eval
"return redis.call('get','foo')"
0
}
{
bar
}
test
{
With min-slaves-to-write: master not writable with lagged slave
}
{
$master config set min-slaves-max-lag 2
$master config set min-slaves-to-write 1
assert
{[
$master
set foo bar
]
eq
{
OK
}}
assert_equal OK
[
$master
set foo 123
]
assert_equal OK
[
$master
eval
"return redis.call('set','foo',12345)"
0
]
# Killing a slave to make it become a lagged slave.
exec kill -SIGSTOP
[
srv 0 pid
]
# Waiting for slave kill.
wait_for_condition 100 100
{
[
catch
{
$master
set foo
bar
}]
!= 0
[
catch
{
$master
set foo
123
}]
!= 0
}
else
{
fail
"Master didn't become readonly"
}
catch
{
$master
set foo
bar
}
err
assert_
match
{
NOREPLICAS*
}
$err
assert_error
"*NOREPLICAS*"
{
$master
set foo
123
}
assert_
error
"*
NOREPLICAS*
"
{
$master
eval
"return redis.call('set','foo',12345)"
0
}
exec kill -SIGCONT
[
srv 0 pid
]
}
}
...
...
tests/integration/replication.tcl
View file @
a83e3663
...
...
@@ -318,7 +318,7 @@ foreach mdl {no yes} {
stop_write_load $load_handle4
# Make sure no more commands processed
wait_load_handlers_disconnected
wait_load_handlers_disconnected
-3
wait_for_ofs_sync $master
[
lindex $slaves 0
]
wait_for_ofs_sync $master
[
lindex $slaves 1
]
...
...
@@ -1233,3 +1233,89 @@ test {replica can handle EINTR if use diskless load} {
}
}
}
{}
{
external:skip
}
start_server
{
tags
{
"repl"
"external:skip"
}}
{
test
"replica do not write the reply to the replication link - SYNC (_addReplyToBufferOrList)"
{
set rd
[
redis_deferring_client
]
set lines
[
count_log_lines 0
]
$rd sync
$rd ping
catch
{
$rd
read
}
e
if
{
$::verbose
}
{
puts
"SYNC _addReplyToBufferOrList:
$e
"
}
assert_equal
"PONG"
[
r ping
]
# Check we got the warning logs about the PING command.
verify_log_message 0
"*Replica generated a reply to command 'ping', disconnecting it: *"
$lines
$rd close
catch
{
exec kill -9
[
get_child_pid 0
]}
waitForBgsave r
}
test
"replica do not write the reply to the replication link - SYNC (addReplyDeferredLen)"
{
set rd
[
redis_deferring_client
]
set lines
[
count_log_lines 0
]
$rd sync
$rd xinfo help
catch
{
$rd
read
}
e
if
{
$::verbose
}
{
puts
"SYNC addReplyDeferredLen:
$e
"
}
assert_equal
"PONG"
[
r ping
]
# Check we got the warning logs about the XINFO HELP command.
verify_log_message 0
"*Replica generated a reply to command 'xinfo|help', disconnecting it: *"
$lines
$rd close
catch
{
exec kill -9
[
get_child_pid 0
]}
waitForBgsave r
}
test
"replica do not write the reply to the replication link - PSYNC (_addReplyToBufferOrList)"
{
set rd
[
redis_deferring_client
]
set lines
[
count_log_lines 0
]
$rd psync replicationid -1
assert_match
{
FULLRESYNC * 0
}
[
$rd
read
]
$rd get foo
catch
{
$rd
read
}
e
if
{
$::verbose
}
{
puts
"PSYNC _addReplyToBufferOrList:
$e
"
}
assert_equal
"PONG"
[
r ping
]
# Check we got the warning logs about the GET command.
verify_log_message 0
"*Replica generated a reply to command 'get', disconnecting it: *"
$lines
verify_log_message 0
"*== CRITICAL == This master is sending an error to its replica: *"
$lines
verify_log_message 0
"*Replica can't interact with the keyspace*"
$lines
$rd close
catch
{
exec kill -9
[
get_child_pid 0
]}
waitForBgsave r
}
test
"replica do not write the reply to the replication link - PSYNC (addReplyDeferredLen)"
{
set rd
[
redis_deferring_client
]
set lines
[
count_log_lines 0
]
$rd psync replicationid -1
assert_match
{
FULLRESYNC * 0
}
[
$rd
read
]
$rd slowlog get
catch
{
$rd
read
}
e
if
{
$::verbose
}
{
puts
"PSYNC addReplyDeferredLen:
$e
"
}
assert_equal
"PONG"
[
r ping
]
# Check we got the warning logs about the SLOWLOG GET command.
verify_log_message 0
"*Replica generated a reply to command 'slowlog|get', disconnecting it: *"
$lines
$rd close
catch
{
exec kill -9
[
get_child_pid 0
]}
waitForBgsave r
}
test
"PSYNC with wrong offset should throw error"
{
# It used to accept the FULL SYNC, but also replied with an error.
assert_error
{
ERR value is not an integer or out of range
}
{
r psync replicationid offset_str
}
set logs
[
exec tail -n 100 <
[
srv 0 stdout
]]
assert_match
{
*Replica * asks for synchronization but with a wrong offset
}
$logs
assert_equal
"PONG"
[
r ping
]
}
}
tests/modules/Makefile
View file @
a83e3663
...
...
@@ -2,11 +2,12 @@
# find the OS
uname_S
:=
$(
shell
sh
-c
'uname -s 2>/dev/null || echo not'
)
warning_cflags
=
-W
-Wall
-Wno-missing-field-initializers
ifeq
($(uname_S),Darwin)
SHOBJ_CFLAGS
?=
-W
-Wall
-dynamic
-fno-common
-g
-ggdb
-std
=
c99
-O2
SHOBJ_CFLAGS
?=
$(warning_cflags)
-dynamic
-fno-common
-g
-ggdb
-std
=
c99
-O2
SHOBJ_LDFLAGS
?=
-bundle
-undefined
dynamic_lookup
else
# Linux, others
SHOBJ_CFLAGS
?=
-W
-Wall
-fno-common
-g
-ggdb
-std
=
c99
-O2
SHOBJ_CFLAGS
?=
$(warning_cflags)
-fno-common
-g
-ggdb
-std
=
c99
-O2
SHOBJ_LDFLAGS
?=
-shared
endif
...
...
@@ -40,6 +41,7 @@ TEST_MODULES = \
keyspace_events.so
\
blockedclient.so
\
getkeys.so
\
getchannels.so
\
test_lazyfree.so
\
timer.so
\
defragtest.so
\
...
...
@@ -51,6 +53,7 @@ TEST_MODULES = \
list.so
\
subcommands.so
\
reply.so
\
cmdintrospection.so
\
eventloop.so
.PHONY
:
all
...
...
tests/modules/aclcheck.c
View file @
a83e3663
...
...
@@ -15,11 +15,13 @@ int set_aclcheck_key(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
const
char
*
flags
=
RedisModule_StringPtrLen
(
argv
[
1
],
NULL
);
if
(
!
strcasecmp
(
flags
,
"W"
))
{
permissions
=
REDISMODULE_
KEY_PERMISSION_WRI
TE
;
permissions
=
REDISMODULE_
CMD_KEY_UPDA
TE
;
}
else
if
(
!
strcasecmp
(
flags
,
"R"
))
{
permissions
=
REDISMODULE_
KEY_PERMISSION_READ
;
permissions
=
REDISMODULE_
CMD_KEY_ACCESS
;
}
else
if
(
!
strcasecmp
(
flags
,
"*"
))
{
permissions
=
REDISMODULE_KEY_PERMISSION_ALL
;
permissions
=
REDISMODULE_CMD_KEY_UPDATE
|
REDISMODULE_CMD_KEY_ACCESS
;
}
else
if
(
!
strcasecmp
(
flags
,
"~"
))
{
permissions
=
0
;
/* Requires either read or write */
}
else
{
RedisModule_ReplyWithError
(
ctx
,
"INVALID FLAGS"
);
return
REDISMODULE_OK
;
...
...
@@ -58,7 +60,7 @@ int publish_aclcheck_channel(RedisModuleCtx *ctx, RedisModuleString **argv, int
/* Check that the pubsub channel can be accessed */
RedisModuleString
*
user_name
=
RedisModule_GetCurrentUserName
(
ctx
);
RedisModuleUser
*
user
=
RedisModule_GetModuleUserFromUserName
(
user_name
);
int
ret
=
RedisModule_ACLCheckChannelPermissions
(
user
,
argv
[
1
],
1
);
int
ret
=
RedisModule_ACLCheckChannelPermissions
(
user
,
argv
[
1
],
REDISMODULE_CMD_CHANNEL_SUBSCRIBE
);
if
(
ret
!=
0
)
{
RedisModule_ReplyWithError
(
ctx
,
"DENIED CHANNEL"
);
RedisModule_FreeModuleUser
(
user
);
...
...
tests/modules/cmdintrospection.c
0 → 100644
View file @
a83e3663
#include "redismodule.h"
#define UNUSED(V) ((void) V)
int
cmd_xadd
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
UNUSED
(
argv
);
UNUSED
(
argc
);
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
return
REDISMODULE_OK
;
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"cmdintrospection"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"cmdintrospection.xadd"
,
cmd_xadd
,
"write deny-oom random fast"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
RedisModuleCommand
*
xadd
=
RedisModule_GetCommand
(
ctx
,
"cmdintrospection.xadd"
);
RedisModuleCommandInfo
info
=
{
.
version
=
REDISMODULE_COMMAND_INFO_VERSION
,
.
arity
=
-
5
,
.
summary
=
"Appends a new entry to a stream"
,
.
since
=
"5.0.0"
,
.
complexity
=
"O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted."
,
.
tips
=
"nondeterministic_output"
,
.
history
=
(
RedisModuleCommandHistoryEntry
[]){
/* NOTE: All versions specified should be the module's versions, not
* Redis'! We use Redis versions in this example for the purpose of
* testing (comparing the output with the output of the vanilla
* XADD). */
{
"6.2.0"
,
"Added the `NOMKSTREAM` option, `MINID` trimming strategy and the `LIMIT` option."
},
{
"7.0.0"
,
"Added support for the `<ms>-*` explicit ID form."
},
{
0
}
},
.
key_specs
=
(
RedisModuleCommandKeySpec
[]){
{
.
notes
=
"UPDATE instead of INSERT because of the optional trimming feature"
,
.
flags
=
REDISMODULE_CMD_KEY_RW
|
REDISMODULE_CMD_KEY_UPDATE
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_INDEX
,
.
bs
.
index
.
pos
=
1
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_RANGE
,
.
fk
.
range
=
{
0
,
1
,
0
}
},
{
0
}
},
.
args
=
(
RedisModuleCommandArg
[]){
{
.
name
=
"key"
,
.
type
=
REDISMODULE_ARG_TYPE_KEY
,
.
key_spec_index
=
0
},
{
.
name
=
"nomkstream"
,
.
type
=
REDISMODULE_ARG_TYPE_PURE_TOKEN
,
.
token
=
"NOMKSTREAM"
,
.
since
=
"6.2.0"
,
.
flags
=
REDISMODULE_CMD_ARG_OPTIONAL
},
{
.
name
=
"trim"
,
.
type
=
REDISMODULE_ARG_TYPE_BLOCK
,
.
flags
=
REDISMODULE_CMD_ARG_OPTIONAL
,
.
subargs
=
(
RedisModuleCommandArg
[]){
{
.
name
=
"strategy"
,
.
type
=
REDISMODULE_ARG_TYPE_ONEOF
,
.
subargs
=
(
RedisModuleCommandArg
[]){
{
.
name
=
"maxlen"
,
.
type
=
REDISMODULE_ARG_TYPE_PURE_TOKEN
,
.
token
=
"MAXLEN"
,
},
{
.
name
=
"minid"
,
.
type
=
REDISMODULE_ARG_TYPE_PURE_TOKEN
,
.
token
=
"MINID"
,
.
since
=
"6.2.0"
,
},
{
0
}
}
},
{
.
name
=
"operator"
,
.
type
=
REDISMODULE_ARG_TYPE_ONEOF
,
.
flags
=
REDISMODULE_CMD_ARG_OPTIONAL
,
.
subargs
=
(
RedisModuleCommandArg
[]){
{
.
name
=
"equal"
,
.
type
=
REDISMODULE_ARG_TYPE_PURE_TOKEN
,
.
token
=
"="
},
{
.
name
=
"approximately"
,
.
type
=
REDISMODULE_ARG_TYPE_PURE_TOKEN
,
.
token
=
"~"
},
{
0
}
}
},
{
.
name
=
"threshold"
,
.
type
=
REDISMODULE_ARG_TYPE_STRING
,
},
{
.
name
=
"count"
,
.
type
=
REDISMODULE_ARG_TYPE_INTEGER
,
.
token
=
"LIMIT"
,
.
since
=
"6.2.0"
,
.
flags
=
REDISMODULE_CMD_ARG_OPTIONAL
},
{
0
}
}
},
{
.
name
=
"id_or_auto"
,
.
type
=
REDISMODULE_ARG_TYPE_ONEOF
,
.
subargs
=
(
RedisModuleCommandArg
[]){
{
.
name
=
"auto_id"
,
.
type
=
REDISMODULE_ARG_TYPE_PURE_TOKEN
,
.
token
=
"*"
},
{
.
name
=
"id"
,
.
type
=
REDISMODULE_ARG_TYPE_STRING
,
},
{
0
}
}
},
{
.
name
=
"field_value"
,
.
type
=
REDISMODULE_ARG_TYPE_BLOCK
,
.
flags
=
REDISMODULE_CMD_ARG_MULTIPLE
,
.
subargs
=
(
RedisModuleCommandArg
[]){
{
.
name
=
"field"
,
.
type
=
REDISMODULE_ARG_TYPE_STRING
,
},
{
.
name
=
"value"
,
.
type
=
REDISMODULE_ARG_TYPE_STRING
,
},
{
0
}
}
},
{
0
}
}
};
if
(
RedisModule_SetCommandInfo
(
xadd
,
&
info
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
tests/modules/getchannels.c
0 → 100644
View file @
a83e3663
#include "redismodule.h"
#include <strings.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
/* A sample with declarable channels, that are used to validate against ACLs */
int
getChannels_subscribe
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
if
((
argc
-
1
)
%
3
!=
0
)
{
RedisModule_WrongArity
(
ctx
);
return
REDISMODULE_OK
;
}
char
*
err
=
NULL
;
/* getchannels.command [[subscribe|unsubscribe|publish] [pattern|literal] <channel> ...]
* This command marks the given channel is accessed based on the
* provided modifiers. */
for
(
int
i
=
1
;
i
<
argc
;
i
+=
3
)
{
const
char
*
operation
=
RedisModule_StringPtrLen
(
argv
[
i
],
NULL
);
const
char
*
type
=
RedisModule_StringPtrLen
(
argv
[
i
+
1
],
NULL
);
int
flags
=
0
;
if
(
!
strcasecmp
(
operation
,
"subscribe"
))
{
flags
|=
REDISMODULE_CMD_CHANNEL_SUBSCRIBE
;
}
else
if
(
!
strcasecmp
(
operation
,
"unsubscribe"
))
{
flags
|=
REDISMODULE_CMD_CHANNEL_UNSUBSCRIBE
;
}
else
if
(
!
strcasecmp
(
operation
,
"publish"
))
{
flags
|=
REDISMODULE_CMD_CHANNEL_PUBLISH
;
}
else
{
err
=
"Invalid channel operation"
;
break
;
}
if
(
!
strcasecmp
(
type
,
"literal"
))
{
/* No op */
}
else
if
(
!
strcasecmp
(
type
,
"pattern"
))
{
flags
|=
REDISMODULE_CMD_CHANNEL_PATTERN
;
}
else
{
err
=
"Invalid channel type"
;
break
;
}
if
(
RedisModule_IsChannelsPositionRequest
(
ctx
))
{
RedisModule_ChannelAtPosWithFlags
(
ctx
,
i
+
2
,
flags
);
}
}
if
(
!
RedisModule_IsChannelsPositionRequest
(
ctx
))
{
if
(
err
)
{
RedisModule_ReplyWithError
(
ctx
,
err
);
}
else
{
/* Normal implementation would go here, but for tests just return okay */
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
}
}
return
REDISMODULE_OK
;
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
RedisModule_Init
(
ctx
,
"getchannels"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"getchannels.command"
,
getChannels_subscribe
,
"getchannels-api"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
tests/modules/getkeys.c
View file @
a83e3663
...
...
@@ -44,6 +44,40 @@ int getkeys_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return
REDISMODULE_OK
;
}
int
getkeys_command_with_flags
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
int
i
;
int
count
=
0
;
/* Handle getkeys-api introspection */
if
(
RedisModule_IsKeysPositionRequest
(
ctx
))
{
for
(
i
=
0
;
i
<
argc
;
i
++
)
{
size_t
len
;
const
char
*
str
=
RedisModule_StringPtrLen
(
argv
[
i
],
&
len
);
if
(
len
==
3
&&
!
strncasecmp
(
str
,
"key"
,
3
)
&&
i
+
1
<
argc
)
RedisModule_KeyAtPosWithFlags
(
ctx
,
i
+
1
,
REDISMODULE_CMD_KEY_RO
|
REDISMODULE_CMD_KEY_ACCESS
);
}
return
REDISMODULE_OK
;
}
/* Handle real command invocation */
RedisModule_ReplyWithArray
(
ctx
,
REDISMODULE_POSTPONED_LEN
);
for
(
i
=
0
;
i
<
argc
;
i
++
)
{
size_t
len
;
const
char
*
str
=
RedisModule_StringPtrLen
(
argv
[
i
],
&
len
);
if
(
len
==
3
&&
!
strncasecmp
(
str
,
"key"
,
3
)
&&
i
+
1
<
argc
)
{
RedisModule_ReplyWithString
(
ctx
,
argv
[
i
+
1
]);
count
++
;
}
}
RedisModule_ReplySetArrayLength
(
ctx
,
count
);
return
REDISMODULE_OK
;
}
int
getkeys_fixed
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
int
i
;
...
...
@@ -57,19 +91,22 @@ int getkeys_fixed(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
/* Introspect a command using RM_GetCommandKeys() and returns the list
* of keys. Essentially this is COMMAND GETKEYS implemented in a module.
* INTROSPECT <with-flags> <cmd> <args>
*/
int
getkeys_introspect
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
UNUSED
(
argv
);
UNUSED
(
argc
);
long
long
with_flags
=
0
;
if
(
argc
<
3
)
{
if
(
argc
<
4
)
{
RedisModule_WrongArity
(
ctx
);
return
REDISMODULE_OK
;
}
int
num_keys
;
int
*
keyidx
=
RedisModule_GetCommandKeys
(
ctx
,
&
argv
[
1
],
argc
-
1
,
&
num_keys
);
if
(
RedisModule_StringToLongLong
(
argv
[
1
],
&
with_flags
)
!=
REDISMODULE_OK
)
return
RedisModule_ReplyWithError
(
ctx
,
"ERR invalid integer"
);
int
num_keys
,
*
keyflags
=
NULL
;
int
*
keyidx
=
RedisModule_GetCommandKeysWithFlags
(
ctx
,
&
argv
[
2
],
argc
-
2
,
&
num_keys
,
with_flags
?
&
keyflags
:
NULL
);
if
(
!
keyidx
)
{
if
(
!
errno
)
...
...
@@ -93,10 +130,27 @@ int getkeys_introspect(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
int
i
;
RedisModule_ReplyWithArray
(
ctx
,
num_keys
);
for
(
i
=
0
;
i
<
num_keys
;
i
++
)
RedisModule_ReplyWithString
(
ctx
,
argv
[
1
+
keyidx
[
i
]]);
for
(
i
=
0
;
i
<
num_keys
;
i
++
)
{
if
(
!
with_flags
)
{
RedisModule_ReplyWithString
(
ctx
,
argv
[
2
+
keyidx
[
i
]]);
continue
;
}
RedisModule_ReplyWithArray
(
ctx
,
2
);
RedisModule_ReplyWithString
(
ctx
,
argv
[
2
+
keyidx
[
i
]]);
char
*
sflags
=
""
;
if
(
keyflags
[
i
]
&
REDISMODULE_CMD_KEY_RO
)
sflags
=
"RO"
;
else
if
(
keyflags
[
i
]
&
REDISMODULE_CMD_KEY_RW
)
sflags
=
"RW"
;
else
if
(
keyflags
[
i
]
&
REDISMODULE_CMD_KEY_OW
)
sflags
=
"OW"
;
else
if
(
keyflags
[
i
]
&
REDISMODULE_CMD_KEY_RM
)
sflags
=
"RM"
;
RedisModule_ReplyWithCString
(
ctx
,
sflags
);
}
RedisModule_Free
(
keyidx
);
RedisModule_Free
(
keyflags
);
}
return
REDISMODULE_OK
;
...
...
@@ -111,6 +165,9 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
if
(
RedisModule_CreateCommand
(
ctx
,
"getkeys.command"
,
getkeys_command
,
"getkeys-api"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"getkeys.command_with_flags"
,
getkeys_command_with_flags
,
"getkeys-api"
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"getkeys.fixed"
,
getkeys_fixed
,
""
,
2
,
4
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
...
...
tests/modules/keyspecs.c
View file @
a83e3663
#include "redismodule.h"
#ifdef INCLUDE_UNRELEASED_KEYSPEC_API
#define UNUSED(V) ((void) V)
int
kspec_legacy
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
/* This function implements all commands in this module. All we care about is
* the COMMAND metadata anyway. */
int
kspec_impl
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
UNUSED
(
argv
);
UNUSED
(
argc
);
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
return
REDISMODULE_OK
;
}
int
kspec_complex1
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
UNUSED
(
argv
);
UNUSED
(
argc
);
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
return
REDISMODULE_OK
;
}
int
kspec_complex2
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
UNUSED
(
argv
);
UNUSED
(
argc
);
RedisModule_ReplyWithSimpleString
(
ctx
,
"OK"
);
int
createKspecNone
(
RedisModuleCtx
*
ctx
)
{
/* A command without keyspecs; only the legacy (first,last,step) triple (MSET like spec). */
if
(
RedisModule_CreateCommand
(
ctx
,
"kspec.none"
,
kspec_impl
,
""
,
1
,
-
1
,
2
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
int
spec_id
;
if
(
RedisModule_Init
(
ctx
,
"keyspecs"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
int
createKspecTwoRanges
(
RedisModuleCtx
*
ctx
)
{
/* Test that two position/range-based key specs are combined to produce the
* legacy (first,last,step) values representing both keys. */
if
(
RedisModule_CreateCommand
(
ctx
,
"kspec.tworanges"
,
kspec_impl
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
RedisModuleCommand
*
command
=
RedisModule_GetCommand
(
ctx
,
"kspec.tworanges"
);
RedisModuleCommandInfo
info
=
{
.
version
=
REDISMODULE_COMMAND_INFO_VERSION
,
.
arity
=
-
2
,
.
key_specs
=
(
RedisModuleCommandKeySpec
[]){
{
.
flags
=
REDISMODULE_CMD_KEY_RO
|
REDISMODULE_CMD_KEY_ACCESS
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_INDEX
,
.
bs
.
index
.
pos
=
1
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_RANGE
,
.
fk
.
range
=
{
0
,
1
,
0
}
},
{
.
flags
=
REDISMODULE_CMD_KEY_RW
|
REDISMODULE_CMD_KEY_UPDATE
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_INDEX
,
.
bs
.
index
.
pos
=
2
,
/* Omitted find_keys_type is shorthand for RANGE {0,1,0} */
},
{
0
}
}
};
if
(
RedisModule_SetCommandInfo
(
command
,
&
info
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
/* Test legacy range "gluing" */
if
(
RedisModule_CreateCommand
(
ctx
,
"kspec.legacy"
,
kspec_legacy
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
RedisModuleCommand
*
legacy
=
RedisModule_GetCommand
(
ctx
,
"kspec.legacy"
);
return
REDISMODULE_OK
;
}
if
(
RedisModule_AddCommandKeySpec
(
legacy
,
"RO ACCESS"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchIndex
(
legacy
,
spec_id
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysRange
(
legacy
,
spec_id
,
0
,
1
,
0
)
==
REDISMODULE_ERR
)
int
createKspecKeyword
(
RedisModuleCtx
*
ctx
)
{
/* Only keyword-based specs. The legacy triple is wiped and set to (0,0,0). */
if
(
RedisModule_CreateCommand
(
ctx
,
"kspec.keyword"
,
kspec_impl
,
""
,
3
,
-
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
RedisModuleCommand
*
command
=
RedisModule_GetCommand
(
ctx
,
"kspec.keyword"
);
RedisModuleCommandInfo
info
=
{
.
version
=
REDISMODULE_COMMAND_INFO_VERSION
,
.
key_specs
=
(
RedisModuleCommandKeySpec
[]){
{
.
flags
=
REDISMODULE_CMD_KEY_RO
|
REDISMODULE_CMD_KEY_ACCESS
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_KEYWORD
,
.
bs
.
keyword
.
keyword
=
"KEYS"
,
.
bs
.
keyword
.
startfrom
=
1
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_RANGE
,
.
fk
.
range
=
{
-
1
,
1
,
0
}
},
{
0
}
}
};
if
(
RedisModule_SetCommandInfo
(
command
,
&
info
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_AddCommandKeySpec
(
legacy
,
"RW UPDATE"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchIndex
(
legacy
,
spec_id
,
2
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysRange
(
legacy
,
spec_id
,
0
,
1
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
/* First is legacy, rest are new specs */
if
(
RedisModule_CreateCommand
(
ctx
,
"kspec.complex1"
,
kspec_complex1
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
int
createKspecComplex1
(
RedisModuleCtx
*
ctx
)
{
/* First is a range a single key. The rest are keyword-based specs. */
if
(
RedisModule_CreateCommand
(
ctx
,
"kspec.complex1"
,
kspec_impl
,
""
,
1
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
RedisModuleCommand
*
command
=
RedisModule_GetCommand
(
ctx
,
"kspec.complex1"
);
RedisModuleCommandInfo
info
=
{
.
version
=
REDISMODULE_COMMAND_INFO_VERSION
,
.
key_specs
=
(
RedisModuleCommandKeySpec
[]){
{
.
flags
=
REDISMODULE_CMD_KEY_RO
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_INDEX
,
.
bs
.
index
.
pos
=
1
,
},
{
.
flags
=
REDISMODULE_CMD_KEY_RW
|
REDISMODULE_CMD_KEY_UPDATE
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_KEYWORD
,
.
bs
.
keyword
.
keyword
=
"STORE"
,
.
bs
.
keyword
.
startfrom
=
2
,
},
{
.
flags
=
REDISMODULE_CMD_KEY_RO
|
REDISMODULE_CMD_KEY_ACCESS
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_KEYWORD
,
.
bs
.
keyword
.
keyword
=
"KEYS"
,
.
bs
.
keyword
.
startfrom
=
2
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_KEYNUM
,
.
fk
.
keynum
=
{
0
,
1
,
1
}
},
{
0
}
}
};
if
(
RedisModule_SetCommandInfo
(
command
,
&
info
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
RedisModuleCommand
*
complex1
=
RedisModule_GetCommand
(
ctx
,
"kspec.complex1"
);
if
(
RedisModule_AddCommandKeySpec
(
complex1
,
"RW UPDATE"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchKeyword
(
complex1
,
spec_id
,
"STORE"
,
2
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysRange
(
complex1
,
spec_id
,
0
,
1
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_AddCommandKeySpec
(
complex1
,
"RO ACCESS"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchKeyword
(
complex1
,
spec_id
,
"KEYS"
,
2
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysKeynum
(
complex1
,
spec_id
,
0
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
int
createKspecComplex2
(
RedisModuleCtx
*
ctx
)
{
/* First is not legacy, more than STATIC_KEYS_SPECS_NUM specs */
if
(
RedisModule_CreateCommand
(
ctx
,
"kspec.complex2"
,
kspec_complex2
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
RedisModuleCommand
*
complex2
=
RedisModule_GetCommand
(
ctx
,
"kspec.complex2"
);
if
(
RedisModule_AddCommandKeySpec
(
complex2
,
"RW UPDATE"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchKeyword
(
complex2
,
spec_id
,
"STORE"
,
5
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysRange
(
complex2
,
spec_id
,
0
,
1
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_AddCommandKeySpec
(
complex2
,
"RO ACCESS"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchIndex
(
complex2
,
spec_id
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysRange
(
complex2
,
spec_id
,
0
,
1
,
0
)
==
REDISMODULE_ERR
)
if
(
RedisModule_CreateCommand
(
ctx
,
"kspec.complex2"
,
kspec_impl
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
RedisModuleCommand
*
command
=
RedisModule_GetCommand
(
ctx
,
"kspec.complex2"
);
RedisModuleCommandInfo
info
=
{
.
version
=
REDISMODULE_COMMAND_INFO_VERSION
,
.
key_specs
=
(
RedisModuleCommandKeySpec
[]){
{
.
flags
=
REDISMODULE_CMD_KEY_RW
|
REDISMODULE_CMD_KEY_UPDATE
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_KEYWORD
,
.
bs
.
keyword
.
keyword
=
"STORE"
,
.
bs
.
keyword
.
startfrom
=
5
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_RANGE
,
.
fk
.
range
=
{
0
,
1
,
0
}
},
{
.
flags
=
REDISMODULE_CMD_KEY_RO
|
REDISMODULE_CMD_KEY_ACCESS
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_INDEX
,
.
bs
.
index
.
pos
=
1
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_RANGE
,
.
fk
.
range
=
{
0
,
1
,
0
}
},
{
.
flags
=
REDISMODULE_CMD_KEY_RO
|
REDISMODULE_CMD_KEY_ACCESS
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_INDEX
,
.
bs
.
index
.
pos
=
2
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_RANGE
,
.
fk
.
range
=
{
0
,
1
,
0
}
},
{
.
flags
=
REDISMODULE_CMD_KEY_RW
|
REDISMODULE_CMD_KEY_UPDATE
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_INDEX
,
.
bs
.
index
.
pos
=
3
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_KEYNUM
,
.
fk
.
keynum
=
{
0
,
1
,
1
}
},
{
.
flags
=
REDISMODULE_CMD_KEY_RW
|
REDISMODULE_CMD_KEY_UPDATE
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_KEYWORD
,
.
bs
.
keyword
.
keyword
=
"MOREKEYS"
,
.
bs
.
keyword
.
startfrom
=
5
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_RANGE
,
.
fk
.
range
=
{
-
1
,
1
,
0
}
},
{
0
}
}
};
if
(
RedisModule_SetCommandInfo
(
command
,
&
info
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_AddCommandKeySpec
(
complex2
,
"RO ACCESS"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchIndex
(
complex2
,
spec_id
,
2
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysRange
(
complex2
,
spec_id
,
0
,
1
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
if
(
RedisModule_AddCommandKeySpec
(
complex2
,
"RW UPDATE"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchIndex
(
complex2
,
spec_id
,
3
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysKeynum
(
complex2
,
spec_id
,
0
,
1
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
int
RedisModule_OnLoad
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
)
{
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
if
(
RedisModule_AddCommandKeySpec
(
complex2
,
"RW UPDATE"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchKeyword
(
complex2
,
spec_id
,
"MOREKEYS"
,
5
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysRange
(
complex2
,
spec_id
,
-
1
,
1
,
0
)
==
REDISMODULE_ERR
)
if
(
RedisModule_Init
(
ctx
,
"keyspecs"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
createKspecNone
(
ctx
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
createKspecTwoRanges
(
ctx
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
createKspecKeyword
(
ctx
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
createKspecComplex1
(
ctx
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
createKspecComplex2
(
ctx
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
return
REDISMODULE_OK
;
}
#endif
tests/modules/subcommands.c
View file @
a83e3663
...
...
@@ -29,11 +29,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
REDISMODULE_NOT_USED
(
argv
);
REDISMODULE_NOT_USED
(
argc
);
#ifdef INCLUDE_UNRELEASED_KEYSPEC_API
int
spec_id
;
#endif
if
(
RedisModule_Init
(
ctx
,
"subcommands"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
if
(
RedisModule_Init
(
ctx
,
"subcommands"
,
1
,
REDISMODULE_APIVER_1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_CreateCommand
(
ctx
,
"subcommands.bitarray"
,
NULL
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
...
...
@@ -43,28 +39,40 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
if
(
RedisModule_CreateSubcommand
(
parent
,
"set"
,
cmd_set
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
RedisModuleCommand
*
subcmd
=
RedisModule_GetCommand
(
ctx
,
"subcommands.bitarray|set"
);
#ifdef INCLUDE_UNRELEASED_KEYSPEC_API
if
(
RedisModule_AddCommandKeySpec
(
subcmd
,
"RW UPDATE"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchIndex
(
subcmd
,
spec_id
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysRange
(
subcmd
,
spec_id
,
0
,
1
,
0
)
==
REDISMODULE_ERR
)
RedisModuleCommandInfo
cmd_set_info
=
{
.
version
=
REDISMODULE_COMMAND_INFO_VERSION
,
.
key_specs
=
(
RedisModuleCommandKeySpec
[]){
{
.
flags
=
REDISMODULE_CMD_KEY_RW
|
REDISMODULE_CMD_KEY_UPDATE
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_INDEX
,
.
bs
.
index
.
pos
=
1
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_RANGE
,
.
fk
.
range
=
{
0
,
1
,
0
}
},
{
0
}
}
};
if
(
RedisModule_SetCommandInfo
(
subcmd
,
&
cmd_set_info
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
#endif
if
(
RedisModule_CreateSubcommand
(
parent
,
"get"
,
cmd_get
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
subcmd
=
RedisModule_GetCommand
(
ctx
,
"subcommands.bitarray|get"
);
#ifdef INCLUDE_UNRELEASED_KEYSPEC_API
if
(
RedisModule_AddCommandKeySpec
(
subcmd
,
"RO ACCESS"
,
&
spec_id
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecBeginSearchIndex
(
subcmd
,
spec_id
,
1
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
if
(
RedisModule_SetCommandKeySpecFindKeysRange
(
subcmd
,
spec_id
,
0
,
1
,
0
)
==
REDISMODULE_ERR
)
RedisModuleCommandInfo
cmd_get_info
=
{
.
version
=
REDISMODULE_COMMAND_INFO_VERSION
,
.
key_specs
=
(
RedisModuleCommandKeySpec
[]){
{
.
flags
=
REDISMODULE_CMD_KEY_RO
|
REDISMODULE_CMD_KEY_ACCESS
,
.
begin_search_type
=
REDISMODULE_KSPEC_BS_INDEX
,
.
bs
.
index
.
pos
=
1
,
.
find_keys_type
=
REDISMODULE_KSPEC_FK_RANGE
,
.
fk
.
range
=
{
0
,
1
,
0
}
},
{
0
}
}
};
if
(
RedisModule_SetCommandInfo
(
subcmd
,
&
cmd_get_info
)
==
REDISMODULE_ERR
)
return
REDISMODULE_ERR
;
#endif
/* Get the name of the command currently running. */
if
(
RedisModule_CreateCommand
(
ctx
,
"subcommands.parent_get_fullname"
,
cmd_get_fullname
,
""
,
0
,
0
,
0
)
==
REDISMODULE_ERR
)
...
...
tests/sentinel/tests/03-runtime-reconf.tcl
View file @
a83e3663
...
...
@@ -44,5 +44,22 @@ test "Sentinel Set with other error situations" {
# unknown parameter option
assert_error
"ERR Unknown option or number of arguments for SENTINEL SET 'fakeoption'"
{
S 0 SENTINEL SET mymaster fakeoption fakevalue
}
}
# save new config to disk failed
set info
[
S 0 SENTINEL master mymaster
]
set origin_quorum
[
dict get $info quorum
]
set update_quorum
[
expr $origin_quorum+1
]
set sentinel_id 0
set configfilename
[
file join
"sentinel_
$sentinel
_id"
"sentinel.conf"
]
set configfilename_bak
[
file join
"sentinel_
$sentinel
_id"
"sentinel.conf.bak"
]
file rename $configfilename $configfilename_bak
file mkdir $configfilename
catch
{[
S 0 SENTINEL SET mymaster quorum $update_quorum
]}
err
file delete $configfilename
file rename $configfilename_bak $configfilename
assert_match
"ERR Failed to save config file*"
$err
}
tests/sentinel/tests/13-info-command.tcl
0 → 100644
View file @
a83e3663
source
"../tests/includes/init-tests.tcl"
test
"info command with at most one argument"
{
set subCommandList
{}
foreach arg
{
""
"all"
"default"
"everything"
}
{
if
{
$arg
==
""
}
{
set info
[
S 0 info
]
}
else
{
set info
[
S 0 info $arg
]
}
assert
{
[
string match
"*redis_version*"
$info
]
}
assert
{
[
string match
"*maxclients*"
$info
]
}
assert
{
[
string match
"*used_cpu_user*"
$info
]
}
assert
{
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
!
[
string match
"*used_memory*"
$info
]
}
assert
{
!
[
string match
"*rdb_last_bgsave*"
$info
]
}
assert
{
!
[
string match
"*master_repl_offset*"
$info
]
}
assert
{
!
[
string match
"*cluster_enabled*"
$info
]
}
}
}
test
"info command with one sub-section"
{
set info
[
S 0 info cpu
]
assert
{
[
string match
"*used_cpu_user*"
$info
]
}
assert
{
!
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
!
[
string match
"*redis_version*"
$info
]
}
set info
[
S 0 info sentinel
]
assert
{
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
!
[
string match
"*used_cpu_user*"
$info
]
}
assert
{
!
[
string match
"*redis_version*"
$info
]
}
}
test
"info command with multiple sub-sections"
{
set info
[
S 0 info server sentinel replication
]
assert
{
[
string match
"*redis_version*"
$info
]
}
assert
{
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
!
[
string match
"*used_memory*"
$info
]
}
assert
{
!
[
string match
"*used_cpu_user*"
$info
]
}
set info
[
S 0 info cpu all
]
assert
{
[
string match
"*used_cpu_user*"
$info
]
}
assert
{
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
[
string match
"*redis_version*"
$info
]
}
assert
{
!
[
string match
"*used_memory*"
$info
]
}
assert
{
!
[
string match
"*master_repl_offset*"
$info
]
}
}
tests/support/redis.tcl
View file @
a83e3663
...
...
@@ -35,6 +35,7 @@ array set ::redis::addr {}
array set ::redis::blocking
{}
array set ::redis::deferred
{}
array set ::redis::readraw
{}
array set ::redis::attributes
{}
;
# Holds the RESP3 attributes from the last call
array set ::redis::reconnect
{}
array set ::redis::tls
{}
array set ::redis::callback
{}
...
...
@@ -105,6 +106,7 @@ proc ::redis::__dispatch__raw__ {id method argv} {
set argv
[
lrange $argv 0 end-1
]
}
if
{[
info command ::redis::__method__$method
]
eq
{}}
{
catch
{
unset ::redis::attributes
(
$id
)}
set cmd
"*
[
expr
{[
llength $argv
]
+1
}]
\r\n
"
append cmd
"
$
[
string length $method
]
\r\n
$method
\r\n
"
foreach a $argv
{
...
...
@@ -165,6 +167,7 @@ proc ::redis::__method__close {id fd} {
catch
{
unset ::redis::blocking
(
$id
)}
catch
{
unset ::redis::deferred
(
$id
)}
catch
{
unset ::redis::readraw
(
$id
)}
catch
{
unset ::redis::attributes
(
$id
)}
catch
{
unset ::redis::reconnect
(
$id
)}
catch
{
unset ::redis::tls
(
$id
)}
catch
{
unset ::redis::state
(
$id
)}
...
...
@@ -185,6 +188,10 @@ proc ::redis::__method__readraw {id fd val} {
set ::redis::readraw
(
$id
)
$val
}
proc ::redis::__method__attributes
{
id fd
}
{
set _ $::redis::attributes
(
$id
)
}
proc ::redis::redis_write
{
fd buf
}
{
puts -nonewline $fd $buf
}
...
...
@@ -286,8 +293,8 @@ proc ::redis::redis_read_reply {id fd} {
*
{
return
[
redis_multi_bulk_read $id $fd
]}
%
{
return
[
redis_read_map $id $fd
]}
|
{
# ignore attributes for now
(
nowhere to store them
)
redis_read_map
$id $
fd
set attrib
[
redis_read_map $id $fd
]
set ::redis::attributes
(
$id
)
$
attrib
continue
}
default
{
...
...
tests/support/test.tcl
View file @
a83e3663
...
...
@@ -24,10 +24,10 @@ proc assert_no_match {pattern value} {
}
}
proc assert_match
{
pattern value
}
{
proc assert_match
{
pattern value
{
detail
""
}
}
{
if
{
!
[
string match $pattern $value
]}
{
set context
"(context:
[
info frame -1
]
)"
error
"assertion:Expected '
$value
' to match '
$pattern
'
$context
"
error
"assertion:Expected '
$value
' to match '
$pattern
'
$context
$detail
"
}
}
...
...
@@ -84,9 +84,9 @@ proc assert_range {value min max {detail ""}} {
proc assert_error
{
pattern code
{
detail
""
}}
{
if
{[
catch
{
uplevel 1 $code
}
error
]}
{
assert_match $pattern $error
assert_match $pattern $error
$detail
}
else
{
assert_failed
"
assertion:
Expected an error
but nothing was caught
"
$detail
assert_failed
"Expected an error
matching '
$pattern
' but got '
$error
'
"
$detail
}
}
...
...
tests/support/util.tcl
View file @
a83e3663
...
...
@@ -89,7 +89,7 @@ proc waitForBgsave r {
puts -nonewline
"
\n
Waiting for background save to finish... "
flush stdout
}
after
100
0
after
5
0
}
else
{
break
}
...
...
@@ -103,7 +103,7 @@ proc waitForBgrewriteaof r {
puts -nonewline
"
\n
Waiting for background AOF rewrite to finish... "
flush stdout
}
after
100
0
after
5
0
}
else
{
break
}
...
...
@@ -647,7 +647,7 @@ proc latencyrstat_percentiles {cmd r} {
proc generate_fuzzy_traffic_on_key
{
key duration
}
{
# Commands per type, blocking commands removed
# TODO: extract these from
help.h or elsewhere
, and improve to include other types
# TODO: extract these from
COMMAND DOCS
, and improve to include other types
set string_commands
{
APPEND BITCOUNT BITFIELD BITOP BITPOS DECR DECRBY GET GETBIT GETRANGE GETSET INCR INCRBY INCRBYFLOAT MGET MSET MSETNX PSETEX SET SETBIT SETEX SETNX SETRANGE LCS STRLEN
}
set hash_commands
{
HDEL HEXISTS HGET HGETALL HINCRBY HINCRBYFLOAT HKEYS HLEN HMGET HMSET HSCAN HSET HSETNX HSTRLEN HVALS HRANDFIELD
}
set zset_commands
{
ZADD ZCARD ZCOUNT ZINCRBY ZINTERSTORE ZLEXCOUNT ZPOPMAX ZPOPMIN ZRANGE ZRANGEBYLEX ZRANGEBYSCORE ZRANK ZREM ZREMRANGEBYLEX ZREMRANGEBYRANK ZREMRANGEBYSCORE ZREVRANGE ZREVRANGEBYLEX ZREVRANGEBYSCORE ZREVRANK ZSCAN ZSCORE ZUNIONSTORE ZRANDMEMBER
}
...
...
tests/test_helper.tcl
View file @
a83e3663
...
...
@@ -20,6 +20,7 @@ set ::all_tests {
unit/keyspace
unit/scan
unit/info
unit/info-command
unit/type/string
unit/type/incr
unit/type/list
...
...
@@ -92,6 +93,7 @@ set ::all_tests {
unit/cluster
unit/client-eviction
unit/violations
unit/replybufsize
}
# Index to the next test to run in the ::all_tests list.
set ::next_test 0
...
...
tests/unit/acl.tcl
View file @
a83e3663
...
...
@@ -7,6 +7,11 @@ start_server {tags {"acl external:skip"}} {
r ACL setuser newuser
}
test
{
Usernames can not contain spaces or null characters
}
{
catch
{
r ACL setuser
"a a"
}
err
set err
}
{
*Usernames can't contain spaces or null characters*
}
test
{
New users start disabled
}
{
r ACL setuser newuser >passwd1
catch
{
r AUTH newuser passwd1
}
err
...
...
@@ -699,6 +704,23 @@ start_server {tags {"acl external:skip"}} {
catch
{[
r ping
]}
e
assert_match
"*I/O error*"
$e
}
test
{
ACL GENPASS command failed test
}
{
catch
{
r ACL genpass -236
}
err1
catch
{
r ACL genpass 5000
}
err2
assert_match
"*ACL GENPASS argument must be the number*"
$err1
assert_match
"*ACL GENPASS argument must be the number*"
$err2
}
test
{
Default user can not be removed
}
{
catch
{
r ACL deluser default
}
err
set err
}
{
ERR The 'default' user cannot be removed
}
test
{
ACL load non-existing configured ACL file
}
{
catch
{
r ACL load
}
err
set err
}
{
*Redis instance is not configured to use an ACL file*
}
}
set server_path
[
tmpdir
"server.acl"
]
...
...
tests/unit/auth.tcl
View file @
a83e3663
...
...
@@ -3,6 +3,11 @@ start_server {tags {"auth external:skip"}} {
catch
{
r auth foo
}
err
set _ $err
}
{
ERR*any password*
}
test
{
Arity check for auth command
}
{
catch
{
r auth a b c
}
err
set _ $err
}
{
*syntax error*
}
}
start_server
{
tags
{
"auth external:skip"
}
overrides
{
requirepass foobar
}}
{
...
...
tests/unit/client-eviction.tcl
View file @
a83e3663
...
...
@@ -45,6 +45,10 @@ proc mb {v} {
return
[
expr $v * 1024 * 1024
]
}
proc kb
{
v
}
{
return
[
expr $v * 1024
]
}
start_server
{}
{
set maxmemory_clients 3000000
r config set maxmemory-clients $maxmemory_clients
...
...
@@ -213,7 +217,7 @@ start_server {} {
r debug pause-cron 0
$rr close
$redirected_c close
}
}
{
0
}
{
needs:debug
}
test
"client evicted due to client tracking prefixes"
{
r flushdb
...
...
@@ -391,6 +395,7 @@ start_server {} {
test
"evict clients only until below limit"
{
set client_count 10
set client_mem
[
mb 1
]
r debug replybuffer-peak-reset-time never
r config set maxmemory-clients 0
r client setname control
r client no-evict on
...
...
@@ -433,19 +438,23 @@ start_server {} {
set connected_clients
[
llength
[
lsearch -all
[
split
[
string trim
[
r client list
]]
"
\r\n
"
]
*name=client*
]]
assert
{
$connected
_clients ==
[
expr $client_count / 2
]}
# Restore the peak reset time to default
r debug replybuffer-peak-reset-time reset
foreach rr $rrs
{
$rr
close
}
}
}
{}
{
needs:debug
}
}
start_server
{}
{
test
"evict clients in right order (large to small)"
{
# Note that each size step needs to be at least x2 larger than previous step
# because of how the client-eviction size bucktting works
set sizes
[
list
100000
[
mb 1
]
[
mb 3
]]
set sizes
[
list
[
kb 128
]
[
mb 1
]
[
mb 3
]]
set clients_per_size 3
r client setname control
r client no-evict on
r config set maxmemory-clients 0
r debug replybuffer-peak-reset-time never
# Run over all sizes and create some clients using up that size
set total_client_mem 0
...
...
@@ -470,7 +479,6 @@ start_server {} {
# Account total client memory usage
incr total_mem
[
expr $clients_per_size * $client_mem
]
}
incr total_mem
[
client_field control tot-mem
]
# Make sure all clients are connected
set clients
[
split
[
string trim
[
r client list
]]
"
\r\n
"
]
...
...
@@ -481,8 +489,9 @@ start_server {} {
# For each size reduce maxmemory-clients so relevant clients should be evicted
# do this from largest to smallest
foreach size
[
lreverse $sizes
]
{
set control_mem
[
client_field control tot-mem
]
set total_mem
[
expr $total_mem - $clients_per_size * $size
]
r config set maxmemory-clients $total_mem
r config set maxmemory-clients
[
expr
$total_mem
+ $control_mem
]
set clients
[
split
[
string trim
[
r client list
]]
"
\r\n
"
]
# Verify only relevant clients were evicted
for
{
set i 0
}
{
$i
<
[
llength $sizes
]}
{
incr i
}
{
...
...
@@ -495,8 +504,12 @@ start_server {} {
}
}
}
# Restore the peak reset time to default
r debug replybuffer-peak-reset-time reset
foreach rr $rrs
{
$rr
close
}
}
}
{}
{
needs:debug
}
}
}
...
...
tests/unit/geo.tcl
View file @
a83e3663
...
...
@@ -294,6 +294,31 @@ start_server {tags {"geo"}} {
r georadiusbymember nyc
"wtc one"
7 km
}
{{
wtc one
}
{
union square
}
{
central park n/q/r
}
4545
{
lic market
}}
test
{
GEORADIUSBYMEMBER search areas contain satisfied points in oblique direction
}
{
r del k1
r geoadd k1 -0.15307903289794921875 85 n1 0.3515625 85.00019260486917005437 n2
set ret1
[
r GEORADIUSBYMEMBER k1 n1 4891.94 m
]
assert_equal $ret1
{
n1 n2
}
r zrem k1 n1 n2
r geoadd k1 -4.95211958885192871094 85 n3 11.25 85.0511 n4
set ret2
[
r GEORADIUSBYMEMBER k1 n3 156544 m
]
assert_equal $ret2
{
n3 n4
}
r zrem k1 n3 n4
r geoadd k1 -45 65.50900022111811438208 n5 90 85.0511 n6
set ret3
[
r GEORADIUSBYMEMBER k1 n5 5009431 m
]
assert_equal $ret3
{
n5 n6
}
}
test
{
GEORADIUSBYMEMBER crossing pole search
}
{
r del k1
r geoadd k1 45 65 n1 -135 85.05 n2
set ret
[
r GEORADIUSBYMEMBER k1 n1 5009431 m
]
assert_equal $ret
{
n1 n2
}
}
test
{
GEOSEARCH FROMMEMBER simple
(
sorted
)}
{
r geosearch nyc frommember
"wtc one"
bybox 14 14 km
}
{{
wtc one
}
{
union square
}
{
central park n/q/r
}
4545
{
lic market
}
q4
}
...
...
tests/unit/info-command.tcl
0 → 100644
View file @
a83e3663
start_server
{
tags
{
"info and its relative command"
}}
{
test
"info command with at most one sub command"
{
foreach arg
{
""
"all"
"default"
"everything"
}
{
if
{
$arg
==
""
}
{
set info
[
r 0 info
]
}
else
{
set info
[
r 0 info $arg
]
}
assert
{
[
string match
"*redis_version*"
$info
]
}
assert
{
[
string match
"*used_cpu_user*"
$info
]
}
assert
{
!
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
[
string match
"*used_memory*"
$info
]
}
if
{
$arg
==
""
|| $arg ==
"default"
}
{
assert
{
!
[
string match
"*rejected_calls*"
$info
]
}
}
else
{
assert
{
[
string match
"*rejected_calls*"
$info
]
}
}
}
}
test
"info command with one sub-section"
{
set info
[
r info cpu
]
assert
{
[
string match
"*used_cpu_user*"
$info
]
}
assert
{
!
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
!
[
string match
"*used_memory*"
$info
]
}
set info
[
r info sentinel
]
assert
{
!
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
!
[
string match
"*used_memory*"
$info
]
}
set info
[
r info commandSTATS
]
;
# test case insensitive compare
assert
{
!
[
string match
"*used_memory*"
$info
]
}
assert
{
[
string match
"*rejected_calls*"
$info
]
}
}
test
"info command with multiple sub-sections"
{
set info
[
r info cpu sentinel
]
assert
{
[
string match
"*used_cpu_user*"
$info
]
}
assert
{
!
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
!
[
string match
"*master_repl_offset*"
$info
]
}
set info
[
r info cpu all
]
assert
{
[
string match
"*used_cpu_user*"
$info
]
}
assert
{
!
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
[
string match
"*used_memory*"
$info
]
}
assert
{
[
string match
"*master_repl_offset*"
$info
]
}
assert
{
[
string match
"*rejected_calls*"
$info
]
}
# check that we didn't get the same info twice
assert
{
!
[
string match
"*used_cpu_user_children*used_cpu_user_children*"
$info
]
}
set info
[
r info cpu default
]
assert
{
[
string match
"*used_cpu_user*"
$info
]
}
assert
{
!
[
string match
"*sentinel_tilt*"
$info
]
}
assert
{
[
string match
"*used_memory*"
$info
]
}
assert
{
[
string match
"*master_repl_offset*"
$info
]
}
assert
{
!
[
string match
"*rejected_calls*"
$info
]
}
# check that we didn't get the same info twice
assert
{
!
[
string match
"*used_cpu_user_children*used_cpu_user_children*"
$info
]
}
}
}
Prev
1
2
3
4
5
6
7
8
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