Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
a5d2df34
Commit
a5d2df34
authored
Jun 08, 2022
by
Oran Agra
Browse files
Merge remote-tracking branch 'origin/unstable' into 7.0
parents
d375595d
f22bfe86
Changes
108
Show whitespace changes
Inline
Side-by-side
src/help.h
View file @
a5d2df34
/* Automatically generated by ./generate-command-help.rb, do not edit. */
/* Automatically generated by ./
utils/
generate-command-help.rb, do not edit. */
#ifndef __REDIS_HELP_H
#define __REDIS_HELP_H
...
...
@@ -135,7 +135,7 @@ struct commandHelp {
15
,
"3.2.0"
},
{
"BITFIELD_RO"
,
"key GET encoding offset [encoding offset ...]"
,
"key GET encoding offset [
GET
encoding offset ...]"
,
"Perform arbitrary bitfield integer operations on strings. Read-only variant of BITFIELD"
,
15
,
"6.2.0"
},
...
...
@@ -255,7 +255,7 @@ struct commandHelp {
8
,
"2.6.9"
},
{
"CLIENT TRACKING"
,
"ON|OFF [REDIRECT client-id] [PREFIX prefix [prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]"
,
"ON|OFF [REDIRECT client-id] [PREFIX prefix [
PREFIX
prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]"
,
"Enable or disable server assisted client side caching support"
,
8
,
"6.0.0"
},
...
...
@@ -1025,7 +1025,7 @@ struct commandHelp {
9
,
"4.0.0"
},
{
"MODULE LOADEX"
,
"path [CONFIG name value [name value ...]] [ARGS arg [arg ...]]"
,
"path [CONFIG name value [
CONFIG
name value ...]] [ARGS arg [arg ...]]"
,
"Load a module with extended parameters"
,
9
,
"7.0.0"
},
...
...
@@ -1120,7 +1120,7 @@ struct commandHelp {
11
,
"2.8.9"
},
{
"PFDEBUG"
,
""
,
"
subcommand key
"
,
"Internal commands for debugging HyperLogLog values"
,
11
,
"2.8.9"
},
...
...
@@ -1195,7 +1195,7 @@ struct commandHelp {
6
,
"7.0.0"
},
{
"PUBSUB SHARDNUMSUB"
,
"[channel [channel ...]]"
,
"[
shard
channel [
shard
channel ...]]"
,
"Get the count of subscribers for shard channels"
,
6
,
"7.0.0"
},
...
...
@@ -1255,7 +1255,7 @@ struct commandHelp {
0
,
"2.6.0"
},
{
"RESTORE-ASKING"
,
""
,
"
key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]
"
,
"An internal command for migrating keys in a cluster"
,
9
,
"3.0.0"
},
...
...
@@ -1450,12 +1450,12 @@ struct commandHelp {
3
,
"1.0.0"
},
{
"SORT"
,
"key [BY pattern] [LIMIT offset count] [GET pattern [pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]"
,
"key [BY pattern] [LIMIT offset count] [GET pattern [
GET
pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]"
,
"Sort the elements in a list, set or sorted set"
,
0
,
"1.0.0"
},
{
"SORT_RO"
,
"key [BY pattern] [LIMIT offset count] [GET pattern [pattern ...]] [ASC|DESC] [ALPHA]"
,
"key [BY pattern] [LIMIT offset count] [GET pattern [
GET
pattern ...]] [ASC|DESC] [ALPHA]"
,
"Sort the elements in a list, set or sorted set. Read-only variant of SORT."
,
0
,
"7.0.0"
},
...
...
@@ -1465,7 +1465,7 @@ struct commandHelp {
3
,
"1.0.0"
},
{
"SPUBLISH"
,
"channel message"
,
"
shard
channel message"
,
"Post a message to a shard channel"
,
6
,
"7.0.0"
},
...
...
@@ -1485,7 +1485,7 @@ struct commandHelp {
3
,
"2.8.0"
},
{
"SSUBSCRIBE"
,
"channel [channel ...]"
,
"
shard
channel [
shard
channel ...]"
,
"Listen for messages published to the given shard channels"
,
6
,
"7.0.0"
},
...
...
@@ -1515,7 +1515,7 @@ struct commandHelp {
3
,
"1.0.0"
},
{
"SUNSUBSCRIBE"
,
"[channel [channel ...]]"
,
"[
shard
channel [
shard
channel ...]]"
,
"Stop listening for messages posted to the given shard channels"
,
6
,
"7.0.0"
},
...
...
src/hyperloglog.c
View file @
a5d2df34
...
...
@@ -1495,8 +1495,13 @@ cleanup:
if
(
o
)
decrRefCount
(
o
);
}
/* PFDEBUG <subcommand> <key> ... args ...
* Different debugging related operations about the HLL implementation. */
/* Different debugging related operations about the HLL implementation.
*
* PFDEBUG GETREG <key>
* PFDEBUG DECODE <key>
* PFDEBUG ENCODING <key>
* PFDEBUG TODENSE <key>
*/
void
pfdebugCommand
(
client
*
c
)
{
char
*
cmd
=
c
->
argv
[
1
]
->
ptr
;
struct
hllhdr
*
hdr
;
...
...
src/latency.c
View file @
a5d2df34
...
...
@@ -59,39 +59,6 @@ dictType latencyTimeSeriesDictType = {
/* ------------------------- Utility functions ------------------------------ */
#ifdef __linux__
#include <sys/prctl.h>
/* Returns 1 if Transparent Huge Pages support is enabled in the kernel.
* Otherwise (or if we are unable to check) 0 is returned. */
int
THPIsEnabled
(
void
)
{
char
buf
[
1024
];
FILE
*
fp
=
fopen
(
"/sys/kernel/mm/transparent_hugepage/enabled"
,
"r"
);
if
(
!
fp
)
return
0
;
if
(
fgets
(
buf
,
sizeof
(
buf
),
fp
)
==
NULL
)
{
fclose
(
fp
);
return
0
;
}
fclose
(
fp
);
return
(
strstr
(
buf
,
"[always]"
)
!=
NULL
)
?
1
:
0
;
}
/* since linux-3.5, kernel supports to set the state of the "THP disable" flag
* for the calling thread. PR_SET_THP_DISABLE is defined in linux/prctl.h */
int
THPDisable
(
void
)
{
int
ret
=
-
EINVAL
;
if
(
!
server
.
disable_thp
)
return
ret
;
#ifdef PR_SET_THP_DISABLE
ret
=
prctl
(
PR_SET_THP_DISABLE
,
1
,
0
,
0
,
0
);
#endif
return
ret
;
}
#endif
/* Report the amount of AnonHugePages in smap, in bytes. If the return
* value of the function is non-zero, the process is being targeted by
* THP support, and is likely to have memory usage / latency issues. */
...
...
src/latency.h
View file @
a5d2df34
...
...
@@ -63,8 +63,6 @@ struct latencyStats {
void
latencyMonitorInit
(
void
);
void
latencyAddSample
(
const
char
*
event
,
mstime_t
latency
);
int
THPIsEnabled
(
void
);
int
THPDisable
(
void
);
/* Latency monitoring macros. */
...
...
src/module.c
View file @
a5d2df34
...
...
@@ -56,6 +56,7 @@
#include "slowlog.h"
#include "rdb.h"
#include "monotonic.h"
#include "script.h"
#include "call_reply.h"
#include <dlfcn.h>
#include <sys/stat.h>
...
...
@@ -355,6 +356,7 @@ typedef struct RedisModuleServerInfoData {
#define REDISMODULE_ARGV_SCRIPT_MODE (1<<6)
#define REDISMODULE_ARGV_NO_WRITES (1<<7)
#define REDISMODULE_ARGV_CALL_REPLIES_AS_ERRORS (1<<8)
#define REDISMODULE_ARGV_RESPECT_DENY_OOM (1<<9)
/* Determine whether Redis should signalModifiedKey implicitly.
* In case 'ctx' has no 'module' member (and therefore no module->options),
...
...
@@ -781,7 +783,7 @@ void moduleCreateContext(RedisModuleCtx *out_ctx, RedisModule *module, int ctx_f
/* This Redis command binds the normal Redis command invocation with commands
* exported by modules. */
void
RedisModuleCommandDispatcher
(
client
*
c
)
{
RedisModuleCommand
*
cp
=
(
void
*
)(
unsigned
long
)
c
->
cmd
->
getkeys_proc
;
RedisModuleCommand
*
cp
=
c
->
cmd
->
module_cmd
;
RedisModuleCtx
ctx
;
moduleCreateContext
(
&
ctx
,
cp
->
module
,
REDISMODULE_CTX_NONE
);
...
...
@@ -816,7 +818,7 @@ void RedisModuleCommandDispatcher(client *c) {
* the context in a way that the command can recognize this is a special
* "get keys" call by calling RedisModule_IsKeysPositionRequest(ctx). */
int
moduleGetCommandKeysViaAPI
(
struct
redisCommand
*
cmd
,
robj
**
argv
,
int
argc
,
getKeysResult
*
result
)
{
RedisModuleCommand
*
cp
=
(
void
*
)(
unsigned
long
)
cmd
->
getkeys_proc
;
RedisModuleCommand
*
cp
=
cmd
->
module_cmd
;
RedisModuleCtx
ctx
;
moduleCreateContext
(
&
ctx
,
cp
->
module
,
REDISMODULE_CTX_KEYS_POS_REQUEST
);
...
...
@@ -836,7 +838,7 @@ int moduleGetCommandKeysViaAPI(struct redisCommand *cmd, robj **argv, int argc,
* moduleGetCommandKeysViaAPI, for modules that declare "getchannels-api"
* during registration. Unlike keys, this is the only way to declare channels. */
int
moduleGetCommandChannelsViaAPI
(
struct
redisCommand
*
cmd
,
robj
**
argv
,
int
argc
,
getKeysResult
*
result
)
{
RedisModuleCommand
*
cp
=
(
void
*
)(
unsigned
long
)
cmd
->
getkeys_proc
;
RedisModuleCommand
*
cp
=
cmd
->
module_cmd
;
RedisModuleCtx
ctx
;
moduleCreateContext
(
&
ctx
,
cp
->
module
,
REDISMODULE_CTX_CHANNELS_POS_REQUEST
);
...
...
@@ -1130,10 +1132,7 @@ RedisModuleCommand *moduleCreateCommandProxy(struct RedisModule *module, sds dec
/* Create a command "proxy", which is a structure that is referenced
* in the command table, so that the generic command that works as
* binding between modules and Redis, can know what function to call
* and what the module is.
*
* Note that we use the Redis command table 'getkeys_proc' in order to
* pass a reference to the command proxy structure. */
* and what the module is. */
cp
=
zcalloc
(
sizeof
(
*
cp
));
cp
->
module
=
module
;
cp
->
func
=
cmdfunc
;
...
...
@@ -1143,7 +1142,7 @@ RedisModuleCommand *moduleCreateCommandProxy(struct RedisModule *module, sds dec
cp
->
rediscmd
->
group
=
COMMAND_GROUP_MODULE
;
cp
->
rediscmd
->
proc
=
RedisModuleCommandDispatcher
;
cp
->
rediscmd
->
flags
=
flags
|
CMD_MODULE
;
cp
->
rediscmd
->
getkeys_proc
=
(
redisGetKeysProc
*
)(
unsigned
long
)
cp
;
cp
->
rediscmd
->
module_cmd
=
cp
;
cp
->
rediscmd
->
key_specs_max
=
STATIC_KEY_SPECS_NUM
;
cp
->
rediscmd
->
key_specs
=
cp
->
rediscmd
->
key_specs_static
;
if
(
firstkey
!=
0
)
{
...
...
@@ -1186,7 +1185,7 @@ RedisModuleCommand *RM_GetCommand(RedisModuleCtx *ctx, const char *name) {
if
(
!
cmd
||
!
(
cmd
->
flags
&
CMD_MODULE
))
return
NULL
;
RedisModuleCommand
*
cp
=
(
void
*
)(
unsigned
long
)
cmd
->
getkeys_proc
;
RedisModuleCommand
*
cp
=
cmd
->
module_cmd
;
if
(
cp
->
module
!=
ctx
->
module
)
return
NULL
;
...
...
@@ -1230,7 +1229,7 @@ int RM_CreateSubcommand(RedisModuleCommand *parent, const char *name, RedisModul
if
(
parent_cmd
->
parent
)
return
REDISMODULE_ERR
;
/* We don't allow more than one level of subcommands */
RedisModuleCommand
*
parent_cp
=
(
void
*
)(
unsigned
long
)
parent_cmd
->
getkeys_proc
;
RedisModuleCommand
*
parent_cp
=
parent_cmd
->
module_cmd
;
if
(
parent_cp
->
func
)
return
REDISMODULE_ERR
;
/* A parent command should be a pure container of subcommands */
...
...
@@ -1444,7 +1443,7 @@ moduleCmdArgAt(const RedisModuleCommandInfoVersion *version,
* begin search step. (Usually it's just after `keynumidx`, in
* which case it should be set to `keynumidx + 1`.)
*
* * `keystep`: How many arguments
s
should we skip after finding a
* * `keystep`: How many arguments should we skip after finding a
* key, in order to find the next one?
*
* Key-spec flags:
...
...
@@ -1769,7 +1768,7 @@ static int moduleValidateCommandInfo(const RedisModuleCommandInfo *info) {
if
(
!
isPowerOfTwo
(
spec
->
flags
&
key_flags
))
{
serverLog
(
LL_WARNING
,
"Invalid command info: key_specs[%zd].flags: "
"Exactly one of the flags RO, RW, OW, RM reqired"
,
j
);
"Exactly one of the flags RO, RW, OW, RM req
u
ired"
,
j
);
return
0
;
}
if
((
spec
->
flags
&
write_flags
)
!=
0
&&
...
...
@@ -1977,7 +1976,7 @@ int moduleIsModuleCommand(void *module_handle, struct redisCommand *cmd) {
return
0
;
if
(
module_handle
==
NULL
)
return
0
;
RedisModuleCommand
*
cp
=
(
void
*
)(
unsigned
long
)
cmd
->
getkeys_proc
;
RedisModuleCommand
*
cp
=
cmd
->
module_cmd
;
return
(
cp
->
module
==
module_handle
);
}
...
...
@@ -2304,7 +2303,7 @@ RedisModuleString *RM_CreateStringPrintf(RedisModuleCtx *ctx, const char *fmt, .
}
/* Like RedisModule_CreatString(), but creates a string starting from a long long
/* Like RedisModule_CreatString(), but creates a string starting from a
`
long long
`
* integer instead of taking a buffer and its length.
*
* The returned string must be released with RedisModule_FreeString() or by
...
...
@@ -2517,9 +2516,9 @@ const char *RM_StringPtrLen(const RedisModuleString *str, size_t *len) {
* Higher level string operations
* ------------------------------------------------------------------------- */
/* Convert the string into a long long integer, storing it at `*ll`.
/* Convert the string into a
`
long long
`
integer, storing it at `*ll`.
* Returns REDISMODULE_OK on success. If the string can't be parsed
* as a valid, strict long long (no spaces before/after), REDISMODULE_ERR
* as a valid, strict
`
long long
`
(no spaces before/after), REDISMODULE_ERR
* is returned. */
int
RM_StringToLongLong
(
const
RedisModuleString
*
str
,
long
long
*
ll
)
{
return
string2ll
(
str
->
ptr
,
sdslen
(
str
->
ptr
),
ll
)
?
REDISMODULE_OK
:
...
...
@@ -2692,7 +2691,7 @@ client *moduleGetReplyClient(RedisModuleCtx *ctx) {
}
}
/* Send an integer reply to the client, with the specified long long value.
/* Send an integer reply to the client, with the specified
`
long long
`
value.
* The function always returns REDISMODULE_OK. */
int
RM_ReplyWithLongLong
(
RedisModuleCtx
*
ctx
,
long
long
ll
)
{
client
*
c
=
moduleGetReplyClient
(
ctx
);
...
...
@@ -2838,8 +2837,8 @@ int RM_ReplyWithSet(RedisModuleCtx *ctx, long len) {
/* Add attributes (metadata) to the reply. Should be done before adding the
* actual reply. see https://github.com/antirez/RESP3/blob/master/spec.md#attribute-type
*
* After starting an attributes reply, the module must make `len*2` calls to other
* `ReplyWith*` style functions in order to emit the elements of the attrib
t
ute map.
* After starting an attribute
'
s reply, the module must make `len*2` calls to other
* `ReplyWith*` style functions in order to emit the elements of the attribute map.
* See Reply APIs section for more details.
*
* Use RM_ReplySetAttributeLength() to set deferred length.
...
...
@@ -3488,7 +3487,7 @@ int RM_GetContextFlags(RedisModuleCtx *ctx) {
}
}
if
(
s
erver
.
in_script
)
if
(
s
criptIsRunning
()
)
flags
|=
REDISMODULE_CTX_FLAGS_LUA
;
if
(
server
.
in_exec
)
...
...
@@ -3622,7 +3621,7 @@ static void moduleInitKeyTypeSpecific(RedisModuleKey *key) {
}
}
/* Return a
n
handle representing a Redis key, so that it is possible
/* Return a handle representing a Redis key, so that it is possible
* to call other APIs with the key handle as argument to perform
* operations on the key.
*
...
...
@@ -3986,7 +3985,7 @@ int RM_StringTruncate(RedisModuleKey *key, size_t newlen) {
* RM_ListInsert, the internal iterator is invalidated so the next operation
* will require a linear seek.
*
* Modifying a list in any another way, for examle using RM_Call(), while a key
* Modifying a list in any another way, for exam
p
le using RM_Call(), while a key
* is open will confuse the internal iterator and may cause trouble if the key
* is used after such modifications. The key must be reopened in this case.
*
...
...
@@ -4840,7 +4839,7 @@ int RM_HashSet(RedisModuleKey *key, int flags, ...) {
return
count
;
}
/* Get fields from a
n
hash value. This function is called using a variable
/* Get fields from a hash value. This function is called using a variable
* number of arguments, alternating a field name (as a RedisModuleString
* pointer) with a pointer to a RedisModuleString pointer, that is set to the
* value of the field if the field exists, or NULL if the field does not exist.
...
...
@@ -4874,7 +4873,7 @@ int RM_HashSet(RedisModuleKey *key, int flags, ...) {
* RedisModule_HashGet(mykey,REDISMODULE_HASH_EXISTS,argv[1],&exists,NULL);
*
* The function returns REDISMODULE_OK on success and REDISMODULE_ERR if
* the key is not a
n
hash value.
* the key is not a hash value.
*
* Memory management:
*
...
...
@@ -5451,7 +5450,7 @@ RedisModuleCallReply *RM_CallReplyArrayElement(RedisModuleCallReply *reply, size
return
callReplyGetArrayElement
(
reply
,
idx
);
}
/* Return the long long of an integer reply. */
/* Return the
`
long long
`
of an integer reply. */
long
long
RM_CallReplyInteger
(
RedisModuleCallReply
*
reply
)
{
return
callReplyGetLongLong
(
reply
);
}
...
...
@@ -5466,7 +5465,7 @@ const char *RM_CallReplyBigNumber(RedisModuleCallReply *reply, size_t *len) {
return
callReplyGetBigNumber
(
reply
,
len
);
}
/* Return the value of a
n
verbatim string reply,
/* Return the value of a verbatim string reply,
* An optional output argument can be given to get verbatim reply format. */
const
char
*
RM_CallReplyVerbatim
(
RedisModuleCallReply
*
reply
,
size_t
*
len
,
const
char
**
format
)
{
return
callReplyGetVerbatim
(
reply
,
len
,
format
);
...
...
@@ -5503,7 +5502,7 @@ RedisModuleCallReply *RM_CallReplyAttribute(RedisModuleCallReply *reply) {
return
callReplyGetAttribute
(
reply
);
}
/* Retrieve the 'idx'-th key and value of a attribute reply.
/* Retrieve the 'idx'-th key and value of a
n
attribute reply.
*
* Returns:
* - REDISMODULE_OK on success.
...
...
@@ -5551,7 +5550,7 @@ RedisModuleString *RM_CreateStringFromCallReply(RedisModuleCallReply *reply) {
* items and *argvlenp with the length of the allocated argv.
*
* The integer pointed by 'flags' is populated with flags according
* to special modifiers in "fmt".
For now only one exists:
* to special modifiers in "fmt".
*
* "!" -> REDISMODULE_ARGV_REPLICATE
* "A" -> REDISMODULE_ARGV_NO_AOF
...
...
@@ -5627,6 +5626,8 @@ robj **moduleCreateArgvFromUserFormat(const char *cmdname, const char *fmt, int
if
(
flags
)
(
*
flags
)
|=
REDISMODULE_ARGV_SCRIPT_MODE
;
}
else
if
(
*
p
==
'W'
)
{
if
(
flags
)
(
*
flags
)
|=
REDISMODULE_ARGV_NO_WRITES
;
}
else
if
(
*
p
==
'M'
)
{
if
(
flags
)
(
*
flags
)
|=
REDISMODULE_ARGV_RESPECT_DENY_OOM
;
}
else
if
(
*
p
==
'E'
)
{
if
(
flags
)
(
*
flags
)
|=
REDISMODULE_ARGV_CALL_REPLIES_AS_ERRORS
;
}
else
{
...
...
@@ -5656,7 +5657,7 @@ fmterr:
* * `b` -- The argument is a buffer and is immediately followed by another
* argument that is the buffer's length.
* * `c` -- The argument is a pointer to a plain C string (null-terminated).
* * `l` -- The argument is long long integer.
* * `l` -- The argument is
a `
long long
`
integer.
* * `s` -- The argument is a RedisModuleString.
* * `v` -- The argument(s) is a vector of RedisModuleString.
* * `!` -- Sends the Redis command and its arguments to replicas and AOF.
...
...
@@ -5668,14 +5669,15 @@ fmterr:
* same as the client attached to the given RedisModuleCtx. This will
* probably used when you want to pass the reply directly to the client.
* * `C` -- Check if command can be executed according to ACL rules.
* *
'S'
-- Run the command in a script mode, this means that it will raise
* *
`S`
-- Run the command in a script mode, this means that it will raise
* an error if a command which are not allowed inside a script
* (flagged with the `deny-script` flag) is invoked (like SHUTDOWN).
* In addition, on script mode, write commands are not allowed if there are
* not enough good replicas (as configured with `min-replicas-to-write`)
* or when the server is unable to persist to the disk.
* * 'W' -- Do not allow to run any write command (flagged with the `write` flag).
* * 'E' -- Return error as RedisModuleCallReply. If there is an error before
* * `W` -- Do not allow to run any write command (flagged with the `write` flag).
* * `M` -- Do not allow `deny-oom` flagged commands when over the memory limit.
* * `E` -- Return error as RedisModuleCallReply. If there is an error before
* invoking the command, the error is returned using errno mechanism.
* This flag allows to get the error also as an error CallReply with
* relevant error message.
...
...
@@ -5693,7 +5695,7 @@ fmterr:
* * ENETDOWN: operation in Cluster instance when cluster is down.
* * ENOTSUP: No ACL user for the specified module context
* * EACCES: Command cannot be executed, according to ACL rules
* * ENOSPC: Write command is not allowed
* * ENOSPC: Write
or deny-oom
command is not allowed
* * ESPIPE: Command not allowed on script mode
*
* Example code fragment:
...
...
@@ -5785,8 +5787,21 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
}
}
if
(
cmd
->
flags
&
CMD_WRITE
)
{
if
(
flags
&
REDISMODULE_ARGV_RESPECT_DENY_OOM
)
{
if
(
cmd
->
flags
&
CMD_DENYOOM
)
{
if
(
server
.
pre_command_oom_state
)
{
errno
=
ENOSPC
;
if
(
error_as_call_replies
)
{
sds
msg
=
sdsdup
(
shared
.
oomerr
->
ptr
);
reply
=
callReplyCreateError
(
msg
,
ctx
);
}
goto
cleanup
;
}
}
}
if
(
flags
&
REDISMODULE_ARGV_NO_WRITES
)
{
if
(
cmd
->
flags
&
CMD_WRITE
)
{
errno
=
ENOSPC
;
if
(
error_as_call_replies
)
{
sds
msg
=
sdscatfmt
(
sdsempty
(),
"Write command '%S' was "
...
...
@@ -5795,14 +5810,17 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
}
goto
cleanup
;
}
}
/* Script mode tests */
if
(
flags
&
REDISMODULE_ARGV_SCRIPT_MODE
)
{
if
(
cmd
->
flags
&
CMD_WRITE
)
{
/* on script mode, if a command is a write command,
* We will not run it if we encounter disk error
* or we do not have enough replicas */
if
(
!
checkGoodReplicasStatus
())
{
errno
=
E
NOSPC
;
errno
=
E
SPIPE
;
if
(
error_as_call_replies
)
{
sds
msg
=
sdsdup
(
shared
.
noreplicaserr
->
ptr
);
reply
=
callReplyCreateError
(
msg
,
ctx
);
...
...
@@ -5814,7 +5832,7 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
int
obey_client
=
mustObeyClient
(
server
.
current_client
);
if
(
deny_write_type
!=
DISK_ERROR_TYPE_NONE
&&
!
obey_client
)
{
errno
=
E
NOSPC
;
errno
=
E
SPIPE
;
if
(
error_as_call_replies
)
{
sds
msg
=
writeCommandsGetDiskErrorMessage
(
deny_write_type
);
reply
=
callReplyCreateError
(
msg
,
ctx
);
...
...
@@ -5822,6 +5840,24 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
goto
cleanup
;
}
if
(
server
.
masterhost
&&
server
.
repl_slave_ro
&&
!
obey_client
)
{
errno
=
ESPIPE
;
if
(
error_as_call_replies
)
{
sds
msg
=
sdsdup
(
shared
.
roslaveerr
->
ptr
);
reply
=
callReplyCreateError
(
msg
,
ctx
);
}
goto
cleanup
;
}
}
if
(
server
.
masterhost
&&
server
.
repl_state
!=
REPL_STATE_CONNECTED
&&
server
.
repl_serve_stale_data
==
0
&&
!
(
cmd
->
flags
&
CMD_STALE
))
{
errno
=
ESPIPE
;
if
(
error_as_call_replies
)
{
sds
msg
=
sdsdup
(
shared
.
masterdownerr
->
ptr
);
reply
=
callReplyCreateError
(
msg
,
ctx
);
}
goto
cleanup
;
}
}
...
...
@@ -6088,7 +6124,7 @@ const char *moduleTypeModuleName(moduleType *mt) {
const
char
*
moduleNameFromCommand
(
struct
redisCommand
*
cmd
)
{
serverAssert
(
cmd
->
proc
==
RedisModuleCommandDispatcher
);
RedisModuleCommand
*
cp
=
(
void
*
)(
unsigned
long
)
cmd
->
getkeys_proc
;
RedisModuleCommand
*
cp
=
cmd
->
module_cmd
;
return
cp
->
module
->
name
;
}
...
...
@@ -6531,7 +6567,7 @@ RedisModuleString *RM_LoadString(RedisModuleIO *io) {
return
moduleLoadString
(
io
,
0
,
NULL
);
}
/* Like RedisModule_LoadString() but returns a
n
heap allocated string that
/* Like RedisModule_LoadString() but returns a heap allocated string that
* was allocated with RedisModule_Alloc(), and can be resized or freed with
* RedisModule_Realloc() or RedisModule_Free().
*
...
...
@@ -6693,7 +6729,7 @@ ssize_t rdbSaveModulesAux(rio *rdb, int when) {
*
* Because Sets are not ordered, so every element added has a position that
* does not depend from the other. However if instead our elements are
* ordered in pairs, like field-value pairs of a
n
Hash, then one should
* ordered in pairs, like field-value pairs of a Hash, then one should
* use:
*
* foreach key,value {
...
...
@@ -6717,7 +6753,7 @@ void RM_DigestAddStringBuffer(RedisModuleDigest *md, const char *ele, size_t len
mixDigest
(
md
->
o
,
ele
,
len
);
}
/* Like `RedisModule_DigestAddStringBuffer()` but takes a long long as input
/* Like `RedisModule_DigestAddStringBuffer()` but takes a
`
long long
`
as input
* that gets converted into a string before adding it to the digest. */
void
RM_DigestAddLongLong
(
RedisModuleDigest
*
md
,
long
long
ll
)
{
char
buf
[
LONG_STR_SIZE
];
...
...
@@ -7061,7 +7097,7 @@ void unblockClientFromModule(client *c) {
*/
RedisModuleBlockedClient
*
moduleBlockClient
(
RedisModuleCtx
*
ctx
,
RedisModuleCmdFunc
reply_callback
,
RedisModuleCmdFunc
timeout_callback
,
void
(
*
free_privdata
)(
RedisModuleCtx
*
,
void
*
),
long
long
timeout_ms
,
RedisModuleString
**
keys
,
int
numkeys
,
void
*
privdata
)
{
client
*
c
=
ctx
->
client
;
int
islua
=
s
erver
.
in_script
;
int
islua
=
s
criptIsRunning
()
;
int
ismulti
=
server
.
in_exec
;
c
->
bpop
.
module_blocked_handle
=
zmalloc
(
sizeof
(
RedisModuleBlockedClient
));
...
...
@@ -7134,7 +7170,7 @@ int moduleTryServeClientBlockedOnKey(client *c, robj *key) {
return
served
;
}
/* Block a client in the context of a blocking command, returning a
n
handle
/* Block a client in the context of a blocking command, returning a handle
* which will be used, later, in order to unblock the client with a call to
* RedisModule_UnblockClient(). The arguments specify callback functions
* and a timeout after which the client is unblocked.
...
...
@@ -8056,7 +8092,7 @@ void RM_SetClusterFlags(RedisModuleCtx *ctx, uint64_t flags) {
/* --------------------------------------------------------------------------
* ## Modules Timers API
*
* Module timers are a
n
high precision "green timers" abstraction where
* Module timers are a high precision "green timers" abstraction where
* every module can register even millions of timers without problems, even if
* the actual event loop will just have a single timer that is used to awake the
* module timers subsystem in order to process the next event.
...
...
@@ -8663,7 +8699,7 @@ int RM_ACLCheckKeyPermissions(RedisModuleUser *user, RedisModuleString *key, int
* access flags. See RM_ChannelAtPosWithFlags for more information about the
* possible flags that can be passed in.
*
* If the user is able to ac
e
css the pubsub channel then REDISMODULE_OK is returned, otherwise
* If the user is able to acc
e
ss the pubsub channel then REDISMODULE_OK is returned, otherwise
* REDISMODULE_ERR is returned and errno is set to one of the following values:
*
* * EINVAL: The provided flags are invalid.
...
...
@@ -8783,7 +8819,7 @@ int RM_AuthenticateClientWithACLUser(RedisModuleCtx *ctx, const char *name, size
}
/* Deauthenticate and close the client. The client resources will not be
*
be
immediately freed, but will be cleaned up in a background job. This is
* immediately freed, but will be cleaned up in a background job. This is
* the recommended way to deauthenticate a client since most clients can't
* handle users becoming deauthenticated. Returns REDISMODULE_ERR when the
* client doesn't exist and REDISMODULE_OK when the operation was successful.
...
...
@@ -9000,7 +9036,7 @@ int RM_DictIteratorReseekC(RedisModuleDictIter *di, const char *op, void *key, s
return
raxSeek
(
&
di
->
ri
,
op
,
key
,
keylen
);
}
/* Like RedisModule_DictIteratorReseekC() but takes the key as a
s a
/* Like RedisModule_DictIteratorReseekC() but takes the key as a
* RedisModuleString. */
int
RM_DictIteratorReseek
(
RedisModuleDictIter
*
di
,
const
char
*
op
,
RedisModuleString
*
key
)
{
return
RM_DictIteratorReseekC
(
di
,
op
,
key
->
ptr
,
sdslen
(
key
->
ptr
));
...
...
@@ -9584,7 +9620,7 @@ int moduleUnregisterFilters(RedisModule *module) {
*
* 1. Invocation by a client.
* 2. Invocation through `RedisModule_Call()` by any module.
* 3. Invocation through Lua
'
redis.call()`.
* 3. Invocation through Lua
`
redis.call()`.
* 4. Replication of a command from a master.
*
* The filter executes in a special filter context, which is different and more
...
...
@@ -9764,6 +9800,12 @@ size_t RM_MallocSize(void* ptr) {
return
zmalloc_size
(
ptr
);
}
/* Similar to RM_MallocSize, the difference is that RM_MallocUsableSize
* returns the usable size of memory by the module. */
size_t
RM_MallocUsableSize
(
void
*
ptr
)
{
return
zmalloc_usable_size
(
ptr
);
}
/* Same as RM_MallocSize, except it works on RedisModuleString pointers.
*/
size_t
RM_MallocSizeString
(
RedisModuleString
*
str
)
{
...
...
@@ -9809,7 +9851,7 @@ typedef struct {
}
ScanCBData
;
typedef
struct
RedisModuleScanCursor
{
int
cursor
;
unsigned
long
cursor
;
int
done
;
}
RedisModuleScanCursor
;
...
...
@@ -9868,14 +9910,14 @@ void RM_ScanCursorDestroy(RedisModuleScanCursor *cursor) {
*
* The way it should be used:
*
* RedisModuleCursor *c = RedisModule_ScanCursorCreate();
* RedisModule
Scan
Cursor *c = RedisModule_ScanCursorCreate();
* while(RedisModule_Scan(ctx, c, callback, privateData));
* RedisModule_ScanCursorDestroy(c);
*
* It is also possible to use this API from another thread while the lock
* is acquired during the actual call to RM_Scan:
*
* RedisModuleCursor *c = RedisModule_ScanCursorCreate();
* RedisModule
Scan
Cursor *c = RedisModule_ScanCursorCreate();
* RedisModule_ThreadSafeContextLock(ctx);
* while(RedisModule_Scan(ctx, c, callback, privateData)){
* RedisModule_ThreadSafeContextUnlock(ctx);
...
...
@@ -9963,7 +10005,7 @@ static void moduleScanKeyCallback(void *privdata, const dictEntry *de) {
*
* The way it should be used:
*
* RedisModuleCursor *c = RedisModule_ScanCursorCreate();
* RedisModule
Scan
Cursor *c = RedisModule_ScanCursorCreate();
* RedisModuleKey *key = RedisModule_OpenKey(...)
* while(RedisModule_ScanKey(key, c, callback, privateData));
* RedisModule_CloseKey(key);
...
...
@@ -9972,7 +10014,7 @@ static void moduleScanKeyCallback(void *privdata, const dictEntry *de) {
* It is also possible to use this API from another thread while the lock is acquired during
* the actual call to RM_ScanKey, and re-opening the key each time:
*
* RedisModuleCursor *c = RedisModule_ScanCursorCreate();
* RedisModule
Scan
Cursor *c = RedisModule_ScanCursorCreate();
* RedisModule_ThreadSafeContextLock(ctx);
* RedisModuleKey *key = RedisModule_OpenKey(...)
* while(RedisModule_ScanKey(ctx, c, callback, privateData)){
...
...
@@ -10936,7 +10978,7 @@ int moduleFreeCommand(struct RedisModule *module, struct redisCommand *cmd) {
if
(
cmd
->
proc
!=
RedisModuleCommandDispatcher
)
return
C_ERR
;
RedisModuleCommand
*
cp
=
(
void
*
)(
unsigned
long
)
cmd
->
getkeys_proc
;
RedisModuleCommand
*
cp
=
cmd
->
module_cmd
;
if
(
cp
->
module
!=
module
)
return
C_ERR
;
...
...
@@ -11680,6 +11722,10 @@ int RM_RegisterBoolConfig(RedisModuleCtx *ctx, const char *name, int default_val
* ...
* RedisModule_RegisterEnumConfig(ctx, "enum", 0, REDISMODULE_CONFIG_DEFAULT, enum_vals, int_vals, 3, getEnumConfigCommand, setEnumConfigCommand, NULL, NULL);
*
* Note that you can use REDISMODULE_CONFIG_BITFLAGS so that multiple enum string
* can be combined into one integer as bit flags, in which case you may want to
* sort your enums so that the preferred combinations are present first.
*
* See RedisModule_RegisterStringConfig for detailed general information about configs. */
int
RM_RegisterEnumConfig
(
RedisModuleCtx
*
ctx
,
const
char
*
name
,
int
default_val
,
unsigned
int
flags
,
const
char
**
enum_values
,
const
int
*
int_values
,
int
num_enum_vals
,
RedisModuleConfigGetEnumFunc
getfn
,
RedisModuleConfigSetEnumFunc
setfn
,
RedisModuleConfigApplyFunc
applyfn
,
void
*
privdata
)
{
RedisModule
*
module
=
ctx
->
module
;
...
...
@@ -12016,7 +12062,7 @@ int *RM_GetCommandKeysWithFlags(RedisModuleCtx *ctx, RedisModuleString **argv, i
}
/* Bail out if command has no keys */
if
(
cmd
->
getkeys_proc
==
NULL
&&
cmd
->
key_specs_num
==
0
)
{
if
(
!
doesCommandHaveKeys
(
cmd
)
)
{
errno
=
0
;
return
NULL
;
}
...
...
@@ -12050,7 +12096,7 @@ int *RM_GetCommandKeysWithFlags(RedisModuleCtx *ctx, RedisModuleString **argv, i
return
res
;
}
/* Identi
n
al to RM_GetCommandKeysWithFlags when flags are not needed. */
/* Identi
c
al to RM_GetCommandKeysWithFlags when flags are not needed. */
int
*
RM_GetCommandKeys
(
RedisModuleCtx
*
ctx
,
RedisModuleString
**
argv
,
int
argc
,
int
*
num_keys
)
{
return
RM_GetCommandKeysWithFlags
(
ctx
,
argv
,
argc
,
num_keys
,
NULL
);
}
...
...
@@ -12564,6 +12610,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API
(
GetBlockedClientReadyKey
);
REGISTER_API
(
GetUsedMemoryRatio
);
REGISTER_API
(
MallocSize
);
REGISTER_API
(
MallocUsableSize
);
REGISTER_API
(
MallocSizeString
);
REGISTER_API
(
MallocSizeDict
);
REGISTER_API
(
ScanCursorCreate
);
...
...
src/multi.c
View file @
a5d2df34
...
...
@@ -56,7 +56,7 @@ void freeClientMultiState(client *c) {
}
/* Add a new command into the MULTI commands queue */
void
queueMultiCommand
(
client
*
c
)
{
void
queueMultiCommand
(
client
*
c
,
uint64_t
cmd_flags
)
{
multiCmd
*
mc
;
/* No sense to waste memory if the transaction is already aborted.
...
...
@@ -75,8 +75,8 @@ void queueMultiCommand(client *c) {
mc
->
argv_len
=
c
->
argv_len
;
c
->
mstate
.
count
++
;
c
->
mstate
.
cmd_flags
|=
c
->
cmd
->
flags
;
c
->
mstate
.
cmd_inv_flags
|=
~
c
->
cmd
->
flags
;
c
->
mstate
.
cmd_flags
|=
c
md_
flags
;
c
->
mstate
.
cmd_inv_flags
|=
~
c
md_
flags
;
c
->
mstate
.
argv_len_sums
+=
c
->
argv_len_sum
+
sizeof
(
robj
*
)
*
c
->
argc
;
/* Reset the client's args since we copied them into the mstate and shouldn't
...
...
src/networking.c
View file @
a5d2df34
...
...
@@ -1953,7 +1953,13 @@ int writeToClient(client *c, int handler_installed) {
zmalloc_used_memory
()
<
server
.
maxmemory
)
&&
!
(
c
->
flags
&
CLIENT_SLAVE
))
break
;
}
if
(
getClientType
(
c
)
==
CLIENT_TYPE_SLAVE
)
{
atomicIncr
(
server
.
stat_net_repl_output_bytes
,
totwritten
);
}
else
{
atomicIncr
(
server
.
stat_net_output_bytes
,
totwritten
);
}
if
(
nwritten
==
-
1
)
{
if
(
connGetState
(
c
->
conn
)
!=
CONN_STATE_CONNECTED
)
{
serverLog
(
LL_VERBOSE
,
...
...
@@ -2499,7 +2505,7 @@ int processInputBuffer(client *c) {
* condition on the slave. We want just to accumulate the replication
* stream (instead of replying -BUSY like we do with other clients) and
* later resume the processing. */
if
(
scriptIsTimedout
()
&&
c
->
flags
&
CLIENT_MASTER
)
break
;
if
(
isInsideYieldingLongCommand
()
&&
c
->
flags
&
CLIENT_MASTER
)
break
;
/* CLIENT_CLOSE_AFTER_REPLY closes the connection once the reply is
* written to the client. Make sure to not let the reply grow after
...
...
@@ -2655,8 +2661,13 @@ void readQueryFromClient(connection *conn) {
if
(
c
->
querybuf_peak
<
qblen
)
c
->
querybuf_peak
=
qblen
;
c
->
lastinteraction
=
server
.
unixtime
;
if
(
c
->
flags
&
CLIENT_MASTER
)
c
->
read_reploff
+=
nread
;
if
(
c
->
flags
&
CLIENT_MASTER
)
{
c
->
read_reploff
+=
nread
;
atomicIncr
(
server
.
stat_net_repl_input_bytes
,
nread
);
}
else
{
atomicIncr
(
server
.
stat_net_input_bytes
,
nread
);
}
if
(
!
(
c
->
flags
&
CLIENT_MASTER
)
&&
sdslen
(
c
->
querybuf
)
>
server
.
client_max_querybuf_len
)
{
sds
ci
=
catClientInfoString
(
sdsempty
(),
c
),
bytes
=
sdsempty
();
...
...
@@ -3557,7 +3568,6 @@ void replaceClientCommandVector(client *c, int argc, robj **argv) {
int
j
;
retainOriginalCommandVector
(
c
);
freeClientArgv
(
c
);
zfree
(
c
->
argv
);
c
->
argv
=
argv
;
c
->
argc
=
argc
;
c
->
argv_len_sum
=
0
;
...
...
src/object.c
View file @
a5d2df34
...
...
@@ -930,7 +930,6 @@ char *strEncoding(int encoding) {
case
OBJ_ENCODING_INT
:
return
"int"
;
case
OBJ_ENCODING_HT
:
return
"hashtable"
;
case
OBJ_ENCODING_QUICKLIST
:
return
"quicklist"
;
case
OBJ_ENCODING_ZIPLIST
:
return
"ziplist"
;
case
OBJ_ENCODING_LISTPACK
:
return
"listpack"
;
case
OBJ_ENCODING_INTSET
:
return
"intset"
;
case
OBJ_ENCODING_SKIPLIST
:
return
"skiplist"
;
...
...
@@ -998,8 +997,6 @@ size_t objectComputeSize(robj *key, robj *o, size_t sample_size, int dbid) {
samples
++
;
}
while
((
node
=
node
->
next
)
&&
samples
<
sample_size
);
asize
+=
(
double
)
elesize
/
samples
*
ql
->
len
;
}
else
if
(
o
->
encoding
==
OBJ_ENCODING_ZIPLIST
)
{
asize
=
sizeof
(
*
o
)
+
zmalloc_size
(
o
->
ptr
);
}
else
{
serverPanic
(
"Unknown list encoding"
);
}
...
...
src/pubsub.c
View file @
a5d2df34
...
...
@@ -39,6 +39,7 @@ typedef struct pubsubtype {
dict
**
serverPubSubChannels
;
robj
**
subscribeMsg
;
robj
**
unsubscribeMsg
;
robj
**
messageBulk
;
}
pubsubtype
;
/*
...
...
@@ -78,6 +79,7 @@ pubsubtype pubSubType = {
.
serverPubSubChannels
=
&
server
.
pubsub_channels
,
.
subscribeMsg
=
&
shared
.
subscribebulk
,
.
unsubscribeMsg
=
&
shared
.
unsubscribebulk
,
.
messageBulk
=
&
shared
.
messagebulk
,
};
/*
...
...
@@ -89,7 +91,8 @@ pubsubtype pubSubShardType = {
.
subscriptionCount
=
clientShardSubscriptionsCount
,
.
serverPubSubChannels
=
&
server
.
pubsubshard_channels
,
.
subscribeMsg
=
&
shared
.
ssubscribebulk
,
.
unsubscribeMsg
=
&
shared
.
sunsubscribebulk
.
unsubscribeMsg
=
&
shared
.
sunsubscribebulk
,
.
messageBulk
=
&
shared
.
smessagebulk
,
};
/*-----------------------------------------------------------------------------
...
...
@@ -101,12 +104,12 @@ pubsubtype pubSubShardType = {
* message. However if the caller sets 'msg' as NULL, it will be able
* to send a special message (for instance an Array type) by using the
* addReply*() API family. */
void
addReplyPubsubMessage
(
client
*
c
,
robj
*
channel
,
robj
*
msg
)
{
void
addReplyPubsubMessage
(
client
*
c
,
robj
*
channel
,
robj
*
msg
,
robj
*
message_bulk
)
{
if
(
c
->
resp
==
2
)
addReply
(
c
,
shared
.
mbulkhdr
[
3
]);
else
addReplyPushLen
(
c
,
3
);
addReply
(
c
,
shared
.
messagebulk
);
addReply
(
c
,
message
_
bulk
);
addReplyBulk
(
c
,
channel
);
if
(
msg
)
addReplyBulk
(
c
,
msg
);
}
...
...
@@ -461,7 +464,7 @@ int pubsubPublishMessageInternal(robj *channel, robj *message, pubsubtype type)
listRewind
(
list
,
&
li
);
while
((
ln
=
listNext
(
&
li
))
!=
NULL
)
{
client
*
c
=
ln
->
value
;
addReplyPubsubMessage
(
c
,
channel
,
message
);
addReplyPubsubMessage
(
c
,
channel
,
message
,
*
type
.
messageBulk
);
updateClientMemUsage
(
c
);
receivers
++
;
}
...
...
@@ -604,10 +607,10 @@ void pubsubCommand(client *c) {
" Return number of subscriptions to patterns."
,
"NUMSUB [<channel> ...]"
,
" Return the number of subscribers for the specified channels, excluding"
,
" pattern subscriptions(default: no channels)."
" pattern subscriptions(default: no channels)."
,
"SHARDCHANNELS [<pattern>]"
,
" Return the currently active shard level channels matching a <pattern> (default: '*')."
,
"SHARDNUMSUB [<channel> ...]"
,
"SHARDNUMSUB [<
shard
channel> ...]"
,
" Return the number of subscribers for the specified shard level channel(s)"
,
NULL
};
...
...
@@ -639,7 +642,7 @@ NULL
sds
pat
=
(
c
->
argc
==
2
)
?
NULL
:
c
->
argv
[
2
]
->
ptr
;
channelList
(
c
,
pat
,
server
.
pubsubshard_channels
);
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"shardnumsub"
)
&&
c
->
argc
>=
2
)
{
/* PUBSUB SHARDNUMSUB [Channel_1 ... Channel_N] */
/* PUBSUB SHARDNUMSUB [
Shard
Channel_1 ...
Shard
Channel_N] */
int
j
;
addReplyArrayLen
(
c
,
(
c
->
argc
-
2
)
*
2
);
...
...
@@ -676,7 +679,7 @@ void channelList(client *c, sds pat, dict *pubsub_channels) {
setDeferredArrayLen
(
c
,
replylen
,
mblen
);
}
/* SPUBLISH <channel> <message> */
/* SPUBLISH <
shard
channel> <message> */
void
spublishCommand
(
client
*
c
)
{
int
receivers
=
pubsubPublishMessageAndPropagateToCluster
(
c
->
argv
[
1
],
c
->
argv
[
2
],
1
);
if
(
!
server
.
cluster_enabled
)
...
...
@@ -684,7 +687,7 @@ void spublishCommand(client *c) {
addReplyLongLong
(
c
,
receivers
);
}
/* SSUBSCRIBE channel [channel ...] */
/* SSUBSCRIBE
shard
channel [
shard
channel ...] */
void
ssubscribeCommand
(
client
*
c
)
{
if
(
c
->
flags
&
CLIENT_DENY_BLOCKING
)
{
/* A client that has CLIENT_DENY_BLOCKING flag on
...
...
@@ -708,7 +711,7 @@ void ssubscribeCommand(client *c) {
}
/* SUNSUBSCRIBE [channel ...] */
/* SUNSUBSCRIBE [
shardchannel [shard
channel ...]
]
*/
void
sunsubscribeCommand
(
client
*
c
)
{
if
(
c
->
argc
==
1
)
{
pubsubUnsubscribeShardAllChannels
(
c
,
1
);
...
...
src/quicklist.c
View file @
a5d2df34
...
...
@@ -43,7 +43,7 @@
#endif
/* Optimization levels for size-based filling.
* Note that the largest possible limit is
1
6k, so even if each record takes
* Note that the largest possible limit is 6
4
k, so even if each record takes
* just one byte, it still won't overflow the 16 bit count field. */
static
const
size_t
optimization_level
[]
=
{
4096
,
8192
,
16384
,
32768
,
65536
};
...
...
src/quicklist.h
View file @
a5d2df34
...
...
@@ -39,7 +39,7 @@
* We use bit fields keep the quicklistNode at 32 bytes.
* count: 16 bits, max 65536 (max lp bytes is 65k, so max count actually < 32k).
* encoding: 2 bits, RAW=1, LZF=2.
* container: 2 bits, PLAIN=1
, PACKED=2
.
* container: 2 bits, PLAIN=1
(a single item as char array), PACKED=2 (listpack with multiple items)
.
* recompress: 1 bit, bool, true if node is temporary decompressed for usage.
* attempted_compress: 1 bit, boolean, used for verifying during testing.
* extra: 10 bits, free for future use; pads out the remainder of 32 bits */
...
...
src/rdb.c
View file @
a5d2df34
...
...
@@ -2746,7 +2746,7 @@ void stopLoading(int success) {
}
void
startSaving
(
int
rdbflags
)
{
/* Fire the persistence modules
end
event. */
/* Fire the persistence modules
start
event. */
int
subevent
;
if
(
rdbflags
&
RDBFLAGS_AOF_PREAMBLE
&&
getpid
()
!=
server
.
pid
)
subevent
=
REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START
;
...
...
@@ -2782,6 +2782,9 @@ void rdbLoadProgressCallback(rio *r, const void *buf, size_t len) {
processEventsWhileBlocked
();
processModuleLoadingProgressEvent
(
0
);
}
if
(
server
.
repl_state
==
REPL_STATE_TRANSFER
&&
rioCheckType
(
r
)
==
RIO_TYPE_CONN
)
{
atomicIncr
(
server
.
stat_net_repl_input_bytes
,
len
);
}
}
/* Save the given functions_ctx to the rdb.
...
...
@@ -3476,6 +3479,9 @@ void saveCommand(client *c) {
addReplyError
(
c
,
"Background save already in progress"
);
return
;
}
server
.
stat_rdb_saves
++
;
rdbSaveInfo
rsi
,
*
rsiptr
;
rsiptr
=
rdbPopulateSaveInfo
(
&
rsi
);
if
(
rdbSave
(
SLAVE_REQ_NONE
,
server
.
rdb_filename
,
rsiptr
)
==
C_OK
)
{
...
...
src/redis-cli.c
View file @
a5d2df34
...
...
@@ -8220,6 +8220,11 @@ static void getKeySizes(redisReply *keys, typeinfo **types,
}
}
static
void
longStatLoopModeStop
(
int
s
)
{
UNUSED
(
s
);
force_cancel_loop
=
1
;
}
static
void
findBigKeys
(
int
memkeys
,
unsigned
memkeys_samples
)
{
unsigned
long
long
sampled
=
0
,
total_keys
,
totlen
=
0
,
*
sizes
=
NULL
,
it
=
0
,
scan_loops
=
0
;
redisReply
*
reply
,
*
keys
;
...
...
@@ -8237,6 +8242,7 @@ static void findBigKeys(int memkeys, unsigned memkeys_samples) {
typeinfo_add
(
types_dict
,
"zset"
,
&
type_zset
);
typeinfo_add
(
types_dict
,
"stream"
,
&
type_stream
);
signal
(
SIGINT
,
longStatLoopModeStop
);
/* Total keys pre scanning */
total_keys
=
getDbSize
();
...
...
@@ -8315,14 +8321,14 @@ static void findBigKeys(int memkeys, unsigned memkeys_samples) {
}
freeReplyObject
(
reply
);
}
while
(
it
!=
0
);
}
while
(
force_cancel_loop
==
0
&&
it
!=
0
);
if
(
types
)
zfree
(
types
);
if
(
sizes
)
zfree
(
sizes
);
/* We're done */
printf
(
"
\n
-------- summary -------
\n\n
"
);
if
(
force_cancel_loop
)
printf
(
"[%05.2f%%] "
,
pct
);
printf
(
"Sampled %llu keys in the keyspace!
\n
"
,
sampled
);
printf
(
"Total key length in bytes is %llu (avg len %.2f)
\n\n
"
,
totlen
,
totlen
?
(
double
)
totlen
/
sampled
:
0
);
...
...
@@ -8401,6 +8407,7 @@ static void findHotKeys(void) {
unsigned
int
arrsize
=
0
,
i
,
k
;
double
pct
;
signal
(
SIGINT
,
longStatLoopModeStop
);
/* Total keys pre scanning */
total_keys
=
getDbSize
();
...
...
@@ -8466,13 +8473,13 @@ static void findHotKeys(void) {
}
freeReplyObject
(
reply
);
}
while
(
it
!=
0
);
}
while
(
force_cancel_loop
==
0
&&
it
!=
0
);
if
(
freqs
)
zfree
(
freqs
);
/* We're done */
printf
(
"
\n
-------- summary -------
\n\n
"
);
if
(
force_cancel_loop
)
printf
(
"[%05.2f%%] "
,
pct
);
printf
(
"Sampled %llu keys in the keyspace!
\n
"
,
sampled
);
for
(
i
=
1
;
i
<=
HOTKEYS_SAMPLE
;
i
++
)
{
...
...
@@ -8643,7 +8650,7 @@ static void statMode(void) {
static
void
scanMode
(
void
)
{
redisReply
*
reply
;
unsigned
long
long
cur
=
0
;
signal
(
SIGINT
,
longStatLoopModeStop
);
do
{
reply
=
sendScan
(
&
cur
);
for
(
unsigned
int
j
=
0
;
j
<
reply
->
element
[
1
]
->
elements
;
j
++
)
{
...
...
@@ -8658,7 +8665,7 @@ static void scanMode(void) {
}
freeReplyObject
(
reply
);
if
(
config
.
interval
)
usleep
(
config
.
interval
);
}
while
(
cur
!=
0
);
}
while
(
force_cancel_loop
==
0
&&
cur
!=
0
);
exit
(
0
);
}
...
...
@@ -8785,11 +8792,6 @@ unsigned long compute_something_fast(void) {
return
output
;
}
static
void
intrinsicLatencyModeStop
(
int
s
)
{
UNUSED
(
s
);
force_cancel_loop
=
1
;
}
static
void
sigIntHandler
(
int
s
)
{
UNUSED
(
s
);
...
...
@@ -8807,7 +8809,7 @@ static void intrinsicLatencyMode(void) {
run_time
=
(
long
long
)
config
.
intrinsic_latency_duration
*
1000000
;
test_end
=
ustime
()
+
run_time
;
signal
(
SIGINT
,
intrinsicLatency
ModeStop
);
signal
(
SIGINT
,
longStatLoop
ModeStop
);
while
(
1
)
{
long
long
start
,
end
,
latency
;
...
...
src/redismodule.h
View file @
a5d2df34
...
...
@@ -185,11 +185,12 @@ This flag should not be used directly by the module.
#define REDISMODULE_NOTIFY_KEY_MISS (1<<11)
/* m (Note: This one is excluded from REDISMODULE_NOTIFY_ALL on purpose) */
#define REDISMODULE_NOTIFY_LOADED (1<<12)
/* module only key space notification, indicate a key loaded from rdb */
#define REDISMODULE_NOTIFY_MODULE (1<<13)
/* d, module key space notification */
#define REDISMODULE_NOTIFY_NEW (1<<14)
/* n, new key notification */
/* Next notification flag, must be updated when adding new flags above!
This flag should not be used directly by the module.
* Use RedisModule_GetKeyspaceNotificationFlagsAll instead. */
#define _REDISMODULE_NOTIFY_NEXT (1<<1
4
)
#define _REDISMODULE_NOTIFY_NEXT (1<<1
5
)
#define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM | REDISMODULE_NOTIFY_MODULE)
/* A */
...
...
@@ -248,7 +249,7 @@ typedef uint64_t RedisModuleTimerID;
/* When set, Redis will not call RedisModule_SignalModifiedKey(), implicitly in
* RedisModule_CloseKey, and the module needs to do that when manually when keys
* are modified from the user's
s
perspective, to invalidate WATCH. */
* are modified from the user's perspective, to invalidate WATCH. */
#define REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED (1<<1)
/* Declare that the module can handle diskless async replication with RedisModule_SetModuleOptions. */
...
...
@@ -346,7 +347,7 @@ typedef struct {
const
char
*
keyword
;
/* An index in argv from which to start searching.
* Can be negative, which means start search from the end, in reverse
* (Example: -2 means to start in reverse from the p
a
nultimate arg) */
* (Example: -2 means to start in reverse from the p
e
nultimate arg) */
int
startfrom
;
}
keyword
;
}
bs
;
...
...
@@ -1160,6 +1161,7 @@ REDISMODULE_API int (*RedisModule_ExitFromChild)(int retcode) REDISMODULE_ATTR;
REDISMODULE_API
int
(
*
RedisModule_KillForkChild
)(
int
child_pid
)
REDISMODULE_ATTR
;
REDISMODULE_API
float
(
*
RedisModule_GetUsedMemoryRatio
)()
REDISMODULE_ATTR
;
REDISMODULE_API
size_t
(
*
RedisModule_MallocSize
)(
void
*
ptr
)
REDISMODULE_ATTR
;
REDISMODULE_API
size_t
(
*
RedisModule_MallocUsableSize
)(
void
*
ptr
)
REDISMODULE_ATTR
;
REDISMODULE_API
size_t
(
*
RedisModule_MallocSizeString
)(
RedisModuleString
*
str
)
REDISMODULE_ATTR
;
REDISMODULE_API
size_t
(
*
RedisModule_MallocSizeDict
)(
RedisModuleDict
*
dict
)
REDISMODULE_ATTR
;
REDISMODULE_API
RedisModuleUser
*
(
*
RedisModule_CreateModuleUser
)(
const
char
*
name
)
REDISMODULE_ATTR
;
...
...
@@ -1190,7 +1192,7 @@ REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromDefragCtx)
REDISMODULE_API
int
(
*
RedisModule_EventLoopAdd
)(
int
fd
,
int
mask
,
RedisModuleEventLoopFunc
func
,
void
*
user_data
)
REDISMODULE_ATTR
;
REDISMODULE_API
int
(
*
RedisModule_EventLoopDel
)(
int
fd
,
int
mask
)
REDISMODULE_ATTR
;
REDISMODULE_API
int
(
*
RedisModule_EventLoopAddOneShot
)(
RedisModuleEventLoopOneShotFunc
func
,
void
*
user_data
)
REDISMODULE_ATTR
;
REDISMODULE_API
int
(
*
RedisModule_RegisterBoolConfig
)(
RedisModuleCtx
*
ctx
,
char
*
name
,
int
default_val
,
unsigned
int
flags
,
RedisModuleConfigGetBoolFunc
getfn
,
RedisModuleConfigSetBoolFunc
setfn
,
RedisModuleConfigApplyFunc
applyfn
,
void
*
privdata
)
REDISMODULE_ATTR
;
REDISMODULE_API
int
(
*
RedisModule_RegisterBoolConfig
)(
RedisModuleCtx
*
ctx
,
const
char
*
name
,
int
default_val
,
unsigned
int
flags
,
RedisModuleConfigGetBoolFunc
getfn
,
RedisModuleConfigSetBoolFunc
setfn
,
RedisModuleConfigApplyFunc
applyfn
,
void
*
privdata
)
REDISMODULE_ATTR
;
REDISMODULE_API
int
(
*
RedisModule_RegisterNumericConfig
)(
RedisModuleCtx
*
ctx
,
const
char
*
name
,
long
long
default_val
,
unsigned
int
flags
,
long
long
min
,
long
long
max
,
RedisModuleConfigGetNumericFunc
getfn
,
RedisModuleConfigSetNumericFunc
setfn
,
RedisModuleConfigApplyFunc
applyfn
,
void
*
privdata
)
REDISMODULE_ATTR
;
REDISMODULE_API
int
(
*
RedisModule_RegisterStringConfig
)(
RedisModuleCtx
*
ctx
,
const
char
*
name
,
const
char
*
default_val
,
unsigned
int
flags
,
RedisModuleConfigGetStringFunc
getfn
,
RedisModuleConfigSetStringFunc
setfn
,
RedisModuleConfigApplyFunc
applyfn
,
void
*
privdata
)
REDISMODULE_ATTR
;
REDISMODULE_API
int
(
*
RedisModule_RegisterEnumConfig
)(
RedisModuleCtx
*
ctx
,
const
char
*
name
,
int
default_val
,
unsigned
int
flags
,
const
char
**
enum_values
,
const
int
*
int_values
,
int
num_enum_vals
,
RedisModuleConfigGetEnumFunc
getfn
,
RedisModuleConfigSetEnumFunc
setfn
,
RedisModuleConfigApplyFunc
applyfn
,
void
*
privdata
)
REDISMODULE_ATTR
;
...
...
@@ -1493,6 +1495,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
KillForkChild
);
REDISMODULE_GET_API
(
GetUsedMemoryRatio
);
REDISMODULE_GET_API
(
MallocSize
);
REDISMODULE_GET_API
(
MallocUsableSize
);
REDISMODULE_GET_API
(
MallocSizeString
);
REDISMODULE_GET_API
(
MallocSizeDict
);
REDISMODULE_GET_API
(
CreateModuleUser
);
...
...
src/replication.c
View file @
a5d2df34
...
...
@@ -1358,7 +1358,7 @@ void sendBulkToSlave(connection *conn) {
freeClient
(
slave
);
return
;
}
atomicIncr
(
server
.
stat_net_output_bytes
,
nwritten
);
atomicIncr
(
server
.
stat_net_
repl_
output_bytes
,
nwritten
);
sdsrange
(
slave
->
replpreamble
,
nwritten
,
-
1
);
if
(
sdslen
(
slave
->
replpreamble
)
==
0
)
{
sdsfree
(
slave
->
replpreamble
);
...
...
@@ -1387,7 +1387,7 @@ void sendBulkToSlave(connection *conn) {
return
;
}
slave
->
repldboff
+=
nwritten
;
atomicIncr
(
server
.
stat_net_output_bytes
,
nwritten
);
atomicIncr
(
server
.
stat_net_
repl_
output_bytes
,
nwritten
);
if
(
slave
->
repldboff
==
slave
->
repldbsize
)
{
close
(
slave
->
repldbfd
);
slave
->
repldbfd
=
-
1
;
...
...
@@ -1419,7 +1419,7 @@ void rdbPipeWriteHandlerConnRemoved(struct connection *conn) {
void
rdbPipeWriteHandler
(
struct
connection
*
conn
)
{
serverAssert
(
server
.
rdb_pipe_bufflen
>
0
);
client
*
slave
=
connGetPrivateData
(
conn
);
in
t
nwritten
;
ssize_
t
nwritten
;
if
((
nwritten
=
connWrite
(
conn
,
server
.
rdb_pipe_buff
+
slave
->
repldboff
,
server
.
rdb_pipe_bufflen
-
slave
->
repldboff
))
==
-
1
)
{
...
...
@@ -1431,7 +1431,7 @@ void rdbPipeWriteHandler(struct connection *conn) {
return
;
}
else
{
slave
->
repldboff
+=
nwritten
;
atomicIncr
(
server
.
stat_net_output_bytes
,
nwritten
);
atomicIncr
(
server
.
stat_net_
repl_
output_bytes
,
nwritten
);
if
(
slave
->
repldboff
<
server
.
rdb_pipe_bufflen
)
{
slave
->
repl_last_partial_write
=
server
.
unixtime
;
return
;
/* more data to write.. */
...
...
@@ -1491,7 +1491,7 @@ void rdbPipeReadHandler(struct aeEventLoop *eventLoop, int fd, void *clientData,
int
stillAlive
=
0
;
for
(
i
=
0
;
i
<
server
.
rdb_pipe_numconns
;
i
++
)
{
in
t
nwritten
;
ssize_
t
nwritten
;
connection
*
conn
=
server
.
rdb_pipe_conns
[
i
];
if
(
!
conn
)
continue
;
...
...
@@ -1511,7 +1511,7 @@ void rdbPipeReadHandler(struct aeEventLoop *eventLoop, int fd, void *clientData,
/* Note: when use diskless replication, 'repldboff' is the offset
* of 'rdb_pipe_buff' sent rather than the offset of entire RDB. */
slave
->
repldboff
=
nwritten
;
atomicIncr
(
server
.
stat_net_output_bytes
,
nwritten
);
atomicIncr
(
server
.
stat_net_
repl_
output_bytes
,
nwritten
);
}
/* If we were unable to write all the data to one of the replicas,
* setup write handler (and disable pipe read handler, below) */
...
...
@@ -1817,11 +1817,16 @@ void readSyncBulkPayload(connection *conn) {
/* If repl_transfer_size == -1 we still have to read the bulk length
* from the master reply. */
if
(
server
.
repl_transfer_size
==
-
1
)
{
if
(
connSyncReadLine
(
conn
,
buf
,
1024
,
server
.
repl_syncio_timeout
*
1000
)
==
-
1
)
{
nread
=
connSyncReadLine
(
conn
,
buf
,
1024
,
server
.
repl_syncio_timeout
*
1000
);
if
(
nread
==
-
1
)
{
serverLog
(
LL_WARNING
,
"I/O error reading bulk count from MASTER: %s"
,
strerror
(
errno
));
goto
error
;
}
else
{
/* nread here is returned by connSyncReadLine(), which calls syncReadLine() and
* convert "\r\n" to '\0' so 1 byte is lost. */
atomicIncr
(
server
.
stat_net_repl_input_bytes
,
nread
+
1
);
}
if
(
buf
[
0
]
==
'-'
)
{
...
...
@@ -1892,7 +1897,7 @@ void readSyncBulkPayload(connection *conn) {
cancelReplicationHandshake
(
1
);
return
;
}
atomicIncr
(
server
.
stat_net_input_bytes
,
nread
);
atomicIncr
(
server
.
stat_net_
repl_
input_bytes
,
nread
);
/* When a mark is used, we want to detect EOF asap in order to avoid
* writing the EOF mark into the file... */
...
...
@@ -2223,8 +2228,8 @@ char *receiveSynchronousResponse(connection *conn) {
/* Read the reply from the server. */
if
(
connSyncReadLine
(
conn
,
buf
,
sizeof
(
buf
),
server
.
repl_syncio_timeout
*
1000
)
==
-
1
)
{
return
sdscatprintf
(
sdsempty
(),
"-Reading from master: %s"
,
strerror
(
errno
))
;
serverLog
(
LL_WARNING
,
"Failed to read response from the server: %s"
,
strerror
(
errno
));
return
NULL
;
}
server
.
repl_transfer_lastio
=
server
.
unixtime
;
return
sdsnew
(
buf
);
...
...
@@ -2407,6 +2412,13 @@ int slaveTryPartialResynchronization(connection *conn, int read_reply) {
/* Reading half */
reply
=
receiveSynchronousResponse
(
conn
);
/* Master did not reply to PSYNC */
if
(
reply
==
NULL
)
{
connSetReadHandler
(
conn
,
NULL
);
serverLog
(
LL_WARNING
,
"Master did not reply to PSYNC, will try later"
);
return
PSYNC_TRY_LATER
;
}
if
(
sdslen
(
reply
)
==
0
)
{
/* The master may send empty newlines after it receives PSYNC
* and before to reply, just to keep the connection alive. */
...
...
@@ -2566,6 +2578,9 @@ void syncWithMaster(connection *conn) {
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_PING_REPLY
)
{
err
=
receiveSynchronousResponse
(
conn
);
/* The master did not reply */
if
(
err
==
NULL
)
goto
no_response_error
;
/* We accept only two replies as valid, a positive +PONG reply
* (we just check for "+") or an authentication error.
* Note that older versions of Redis replied with "operation not
...
...
@@ -2652,6 +2667,7 @@ void syncWithMaster(connection *conn) {
/* Receive AUTH reply. */
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_AUTH_REPLY
)
{
err
=
receiveSynchronousResponse
(
conn
);
if
(
err
==
NULL
)
goto
no_response_error
;
if
(
err
[
0
]
==
'-'
)
{
serverLog
(
LL_WARNING
,
"Unable to AUTH to MASTER: %s"
,
err
);
sdsfree
(
err
);
...
...
@@ -2666,6 +2682,7 @@ void syncWithMaster(connection *conn) {
/* Receive REPLCONF listening-port reply. */
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_PORT_REPLY
)
{
err
=
receiveSynchronousResponse
(
conn
);
if
(
err
==
NULL
)
goto
no_response_error
;
/* Ignore the error if any, not all the Redis versions support
* REPLCONF listening-port. */
if
(
err
[
0
]
==
'-'
)
{
...
...
@@ -2683,6 +2700,7 @@ void syncWithMaster(connection *conn) {
/* Receive REPLCONF ip-address reply. */
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_IP_REPLY
)
{
err
=
receiveSynchronousResponse
(
conn
);
if
(
err
==
NULL
)
goto
no_response_error
;
/* Ignore the error if any, not all the Redis versions support
* REPLCONF ip-address. */
if
(
err
[
0
]
==
'-'
)
{
...
...
@@ -2697,6 +2715,7 @@ void syncWithMaster(connection *conn) {
/* Receive CAPA reply. */
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_CAPA_REPLY
)
{
err
=
receiveSynchronousResponse
(
conn
);
if
(
err
==
NULL
)
goto
no_response_error
;
/* Ignore the error if any, not all the Redis versions support
* REPLCONF capa. */
if
(
err
[
0
]
==
'-'
)
{
...
...
@@ -2810,6 +2829,10 @@ void syncWithMaster(connection *conn) {
server
.
repl_transfer_lastio
=
server
.
unixtime
;
return
;
no_response_error:
/* Handle receiveSynchronousResponse() error when master has no reply */
serverLog
(
LL_WARNING
,
"Master did not respond to command during SYNC handshake"
);
/* Fall through to regular error handling */
error:
if
(
dfd
!=
-
1
)
close
(
dfd
);
connClose
(
conn
);
...
...
src/rio.c
View file @
a5d2df34
...
...
@@ -438,6 +438,20 @@ void rioSetAutoSync(rio *r, off_t bytes) {
r
->
io
.
file
.
autosync
=
bytes
;
}
/* Check the type of rio. */
uint8_t
rioCheckType
(
rio
*
r
)
{
if
(
r
->
read
==
rioFileRead
)
{
return
RIO_TYPE_FILE
;
}
else
if
(
r
->
read
==
rioBufferRead
)
{
return
RIO_TYPE_BUFFER
;
}
else
if
(
r
->
read
==
rioConnRead
)
{
return
RIO_TYPE_CONN
;
}
else
{
/* r->read == rioFdRead */
return
RIO_TYPE_FD
;
}
}
/* --------------------------- Higher level interface --------------------------
*
* The following higher level functions use lower level rio.c functions to help
...
...
src/rio.h
View file @
a5d2df34
...
...
@@ -40,6 +40,11 @@
#define RIO_FLAG_READ_ERROR (1<<0)
#define RIO_FLAG_WRITE_ERROR (1<<1)
#define RIO_TYPE_FILE (1<<0)
#define RIO_TYPE_BUFFER (1<<1)
#define RIO_TYPE_CONN (1<<2)
#define RIO_TYPE_FD (1<<3)
struct
_rio
{
/* Backend functions.
* Since this functions do not tolerate short writes or reads the return
...
...
@@ -174,5 +179,5 @@ int rioWriteBulkObject(rio *r, struct redisObject *obj);
void
rioGenericUpdateChecksum
(
rio
*
r
,
const
void
*
buf
,
size_t
len
);
void
rioSetAutoSync
(
rio
*
r
,
off_t
bytes
);
uint8_t
rioCheckType
(
rio
*
r
);
#endif
src/script.c
View file @
a5d2df34
...
...
@@ -92,8 +92,8 @@ int scriptInterrupt(scriptRunCtx *run_ctx) {
serverLog
(
LL_WARNING
,
"Slow script detected: still in execution after %lld milliseconds. "
"You can try killing the script using the %s command."
,
elapsed
,
(
run_ctx
->
flags
&
SCRIPT_EVAL_MODE
)
?
"SCRIPT KILL"
:
"FUNCTION KILL"
);
"You can try killing the script using the %s command.
Script name is: %s.
"
,
elapsed
,
(
run_ctx
->
flags
&
SCRIPT_EVAL_MODE
)
?
"SCRIPT KILL"
:
"FUNCTION KILL"
,
run_ctx
->
funcname
);
enterScriptTimedoutMode
(
run_ctx
);
/* Once the script timeouts we reenter the event loop to permit others
...
...
@@ -108,6 +108,25 @@ int scriptInterrupt(scriptRunCtx *run_ctx) {
return
(
run_ctx
->
flags
&
SCRIPT_KILLED
)
?
SCRIPT_KILL
:
SCRIPT_CONTINUE
;
}
uint64_t
scriptFlagsToCmdFlags
(
uint64_t
cmd_flags
,
uint64_t
script_flags
)
{
/* If the script declared flags, clear the ones from the command and use the ones it declared.*/
cmd_flags
&=
~
(
CMD_STALE
|
CMD_DENYOOM
|
CMD_WRITE
);
/* NO_WRITES implies ALLOW_OOM */
if
(
!
(
script_flags
&
(
SCRIPT_FLAG_ALLOW_OOM
|
SCRIPT_FLAG_NO_WRITES
)))
cmd_flags
|=
CMD_DENYOOM
;
if
(
!
(
script_flags
&
SCRIPT_FLAG_NO_WRITES
))
cmd_flags
|=
CMD_WRITE
;
if
(
script_flags
&
SCRIPT_FLAG_ALLOW_STALE
)
cmd_flags
|=
CMD_STALE
;
/* In addition the MAY_REPLICATE flag is set for these commands, but
* if we have flags we know if it's gonna do any writes or not. */
cmd_flags
&=
~
CMD_MAY_REPLICATE
;
return
cmd_flags
;
}
/* Prepare the given run ctx for execution */
int
scriptPrepareForRun
(
scriptRunCtx
*
run_ctx
,
client
*
engine_client
,
client
*
caller
,
const
char
*
funcname
,
uint64_t
script_flags
,
int
ro
)
{
serverAssert
(
!
curr_run_ctx
);
...
...
@@ -123,12 +142,6 @@ int scriptPrepareForRun(scriptRunCtx *run_ctx, client *engine_client, client *ca
return
C_ERR
;
}
if
(
!
(
script_flags
&
SCRIPT_FLAG_ALLOW_OOM
)
&&
server
.
script_oom
&&
server
.
maxmemory
)
{
addReplyError
(
caller
,
"-OOM allow-oom flag is not set on the script, "
"can not run it when used memory > 'maxmemory'"
);
return
C_ERR
;
}
if
(
running_stale
&&
!
(
script_flags
&
SCRIPT_FLAG_ALLOW_STALE
))
{
addReplyError
(
caller
,
"-MASTERDOWN Link with MASTER is down, "
"replica-serve-stale-data is set to 'no' "
...
...
@@ -142,7 +155,7 @@ int scriptPrepareForRun(scriptRunCtx *run_ctx, client *engine_client, client *ca
* 2. no disk error detected
* 3. command is not `fcall_ro`/`eval[sha]_ro` */
if
(
server
.
masterhost
&&
server
.
repl_slave_ro
&&
!
obey_client
)
{
addReplyError
(
caller
,
"Can not run script with write flag on readonly replica"
);
addReplyError
(
caller
,
"
-READONLY
Can not run script with write flag on readonly replica"
);
return
C_ERR
;
}
...
...
@@ -177,6 +190,17 @@ int scriptPrepareForRun(scriptRunCtx *run_ctx, client *engine_client, client *ca
return
C_ERR
;
}
}
/* Check OOM state. the no-writes flag imply allow-oom. we tested it
* after the no-write error, so no need to mention it in the error reply. */
if
(
server
.
pre_command_oom_state
&&
server
.
maxmemory
&&
!
(
script_flags
&
(
SCRIPT_FLAG_ALLOW_OOM
|
SCRIPT_FLAG_NO_WRITES
)))
{
addReplyError
(
caller
,
"-OOM allow-oom flag is not set on the script, "
"can not run it when used memory > 'maxmemory'"
);
return
C_ERR
;
}
}
else
{
/* Special handling for backwards compatibility (no shebang eval[sha]) mode */
if
(
running_stale
)
{
...
...
@@ -202,8 +226,6 @@ int scriptPrepareForRun(scriptRunCtx *run_ctx, client *engine_client, client *ca
script_client
->
flags
|=
CLIENT_MULTI
;
}
server
.
in_script
=
1
;
run_ctx
->
start_time
=
getMonotonicUs
();
run_ctx
->
snapshot_time
=
mstime
();
...
...
@@ -216,6 +238,8 @@ int scriptPrepareForRun(scriptRunCtx *run_ctx, client *engine_client, client *ca
run_ctx
->
flags
|=
SCRIPT_READ_ONLY
;
}
if
(
!
(
script_flags
&
SCRIPT_FLAG_EVAL_COMPAT_MODE
)
&&
(
script_flags
&
SCRIPT_FLAG_ALLOW_OOM
))
{
/* Note: we don't need to test the no-writes flag here and set this run_ctx flag,
* since only write commands can are deny-oom. */
run_ctx
->
flags
|=
SCRIPT_ALLOW_OOM
;
}
...
...
@@ -236,7 +260,6 @@ void scriptResetRun(scriptRunCtx *run_ctx) {
/* After the script done, remove the MULTI state. */
run_ctx
->
c
->
flags
&=
~
CLIENT_MULTI
;
server
.
in_script
=
0
;
server
.
script_caller
=
NULL
;
if
(
scriptIsTimedout
())
{
...
...
@@ -323,16 +346,23 @@ static int scriptVerifyACL(client *c, sds *err) {
}
static
int
scriptVerifyWriteCommandAllow
(
scriptRunCtx
*
run_ctx
,
char
**
err
)
{
if
(
!
(
run_ctx
->
c
->
cmd
->
flags
&
CMD_WRITE
))
{
return
C_OK
;
}
if
(
run_ctx
->
flags
&
SCRIPT_READ_ONLY
)
{
/* We know its a write command, on a read only run we do not allow it. */
/* A write command, on an RO command or an RO script is rejected ASAP.
* Note: For scripts, we consider may-replicate commands as write commands.
* This also makes it possible to allow read-only scripts to be run during
* CLIENT PAUSE WRITE. */
if
(
run_ctx
->
flags
&
SCRIPT_READ_ONLY
&&
(
run_ctx
->
c
->
cmd
->
flags
&
(
CMD_WRITE
|
CMD_MAY_REPLICATE
)))
{
*
err
=
sdsnew
(
"Write commands are not allowed from read-only scripts."
);
return
C_ERR
;
}
/* The other checks below are on the server state and are only relevant for
* write commands, return if this is not a write command. */
if
(
!
(
run_ctx
->
c
->
cmd
->
flags
&
CMD_WRITE
))
return
C_OK
;
/* Write commands are forbidden against read-only slaves, or if a
* command marked as non-deterministic was already called in the context
* of this script. */
...
...
@@ -362,16 +392,6 @@ static int scriptVerifyWriteCommandAllow(scriptRunCtx *run_ctx, char **err) {
return
C_OK
;
}
static
int
scriptVerifyMayReplicate
(
scriptRunCtx
*
run_ctx
,
char
**
err
)
{
if
(
run_ctx
->
c
->
cmd
->
flags
&
CMD_MAY_REPLICATE
&&
server
.
client_pause_type
==
CLIENT_PAUSE_WRITE
)
{
*
err
=
sdsnew
(
"May-replicate commands are not allowed when client pause write."
);
return
C_ERR
;
}
return
C_OK
;
}
static
int
scriptVerifyOOM
(
scriptRunCtx
*
run_ctx
,
char
**
err
)
{
if
(
run_ctx
->
flags
&
SCRIPT_ALLOW_OOM
)
{
/* Allow running any command even if OOM reached */
...
...
@@ -386,7 +406,7 @@ static int scriptVerifyOOM(scriptRunCtx *run_ctx, char **err) {
if
(
server
.
maxmemory
&&
/* Maxmemory is actually enabled. */
!
mustObeyClient
(
run_ctx
->
original_client
)
&&
/* Don't care about mem for replicas or AOF. */
!
(
run_ctx
->
flags
&
SCRIPT_WRITE_DIRTY
)
&&
/* Script had no side effects so far. */
server
.
script_oom
&&
/* Detected OOM when script start. */
server
.
pre_command_oom_state
&&
/* Detected OOM when script start. */
(
run_ctx
->
c
->
cmd
->
flags
&
CMD_DENYOOM
))
{
*
err
=
sdsdup
(
shared
.
oomerr
->
ptr
);
...
...
@@ -526,10 +546,6 @@ void scriptCall(scriptRunCtx *run_ctx, robj* *argv, int argc, sds *err) {
goto
error
;
}
if
(
scriptVerifyMayReplicate
(
run_ctx
,
err
)
!=
C_OK
)
{
goto
error
;
}
if
(
scriptVerifyOOM
(
run_ctx
,
err
)
!=
C_OK
)
{
goto
error
;
}
...
...
src/script.h
View file @
a5d2df34
...
...
@@ -93,6 +93,7 @@ typedef struct scriptFlag {
extern
scriptFlag
scripts_flags_def
[];
uint64_t
scriptFlagsToCmdFlags
(
uint64_t
cmd_flags
,
uint64_t
script_flags
);
int
scriptPrepareForRun
(
scriptRunCtx
*
r_ctx
,
client
*
engine_client
,
client
*
caller
,
const
char
*
funcname
,
uint64_t
script_flags
,
int
ro
);
void
scriptResetRun
(
scriptRunCtx
*
r_ctx
);
int
scriptSetResp
(
scriptRunCtx
*
r_ctx
,
int
resp
);
...
...
src/script_lua.c
View file @
a5d2df34
...
...
@@ -1651,8 +1651,11 @@ void luaCallFunction(scriptRunCtx* run_ctx, lua_State *lua, robj** keys, size_t
* {err='<error msg>', source='<source file>', line=<line>}
* We can construct the error message from this information */
if
(
!
lua_istable
(
lua
,
-
1
))
{
/* Should not happened, and we should considered assert it */
addReplyErrorFormat
(
c
,
"Error running script (call to %s)
\n
"
,
run_ctx
->
funcname
);
const
char
*
msg
=
"execution failure"
;
if
(
lua_isstring
(
lua
,
-
1
))
{
msg
=
lua_tostring
(
lua
,
-
1
);
}
addReplyErrorFormat
(
c
,
"Error running script %s, %.100s
\n
"
,
run_ctx
->
funcname
,
msg
);
}
else
{
errorInfo
err_info
=
{
0
};
sds
final_msg
=
sdsempty
();
...
...
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