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
d375595d
Unverified
Commit
d375595d
authored
Apr 27, 2022
by
Oran Agra
Committed by
GitHub
Apr 27, 2022
Browse files
Merge pull request #10652 from oranagra/redis-7.0.0
Redis 7.0.0
parents
fb4e0d40
c1f30206
Changes
124
Hide whitespace changes
Inline
Side-by-side
src/script.h
View file @
d375595d
...
@@ -64,6 +64,7 @@
...
@@ -64,6 +64,7 @@
#define SCRIPT_READ_ONLY (1ULL<<5)
/* indicate that the current script should only perform read commands */
#define SCRIPT_READ_ONLY (1ULL<<5)
/* indicate that the current script should only perform read commands */
#define SCRIPT_ALLOW_OOM (1ULL<<6)
/* indicate to allow any command even if OOM reached */
#define SCRIPT_ALLOW_OOM (1ULL<<6)
/* indicate to allow any command even if OOM reached */
#define SCRIPT_EVAL_MODE (1ULL<<7)
/* Indicate that the current script called from legacy Lua */
#define SCRIPT_EVAL_MODE (1ULL<<7)
/* Indicate that the current script called from legacy Lua */
#define SCRIPT_ALLOW_CROSS_SLOT (1ULL<<8)
/* Indicate that the current script may access keys from multiple slots */
typedef
struct
scriptRunCtx
scriptRunCtx
;
typedef
struct
scriptRunCtx
scriptRunCtx
;
struct
scriptRunCtx
{
struct
scriptRunCtx
{
...
@@ -82,6 +83,7 @@ struct scriptRunCtx {
...
@@ -82,6 +83,7 @@ struct scriptRunCtx {
#define SCRIPT_FLAG_ALLOW_STALE (1ULL<<2)
#define SCRIPT_FLAG_ALLOW_STALE (1ULL<<2)
#define SCRIPT_FLAG_NO_CLUSTER (1ULL<<3)
#define SCRIPT_FLAG_NO_CLUSTER (1ULL<<3)
#define SCRIPT_FLAG_EVAL_COMPAT_MODE (1ULL<<4)
/* EVAL Script backwards compatible behavior, no shebang provided */
#define SCRIPT_FLAG_EVAL_COMPAT_MODE (1ULL<<4)
/* EVAL Script backwards compatible behavior, no shebang provided */
#define SCRIPT_FLAG_ALLOW_CROSS_SLOT (1ULL<<5)
/* Defines a script flags */
/* Defines a script flags */
typedef
struct
scriptFlag
{
typedef
struct
scriptFlag
{
...
...
src/script_lua.c
View file @
d375595d
...
@@ -41,6 +41,97 @@
...
@@ -41,6 +41,97 @@
#include <ctype.h>
#include <ctype.h>
#include <math.h>
#include <math.h>
/* Globals that are added by the Lua libraries */
static
char
*
libraries_allow_list
[]
=
{
"string"
,
"cjson"
,
"bit"
,
"cmsgpack"
,
"math"
,
"table"
,
"struct"
,
NULL
,
};
/* Redis Lua API globals */
static
char
*
redis_api_allow_list
[]
=
{
"redis"
,
"__redis__err__handler"
,
/* error handler for eval, currently located on globals.
Should move to registry. */
NULL
,
};
/* Lua builtins */
static
char
*
lua_builtins_allow_list
[]
=
{
"xpcall"
,
"tostring"
,
"getfenv"
,
"setmetatable"
,
"next"
,
"assert"
,
"tonumber"
,
"rawequal"
,
"collectgarbage"
,
"getmetatable"
,
"rawset"
,
"pcall"
,
"coroutine"
,
"type"
,
"_G"
,
"select"
,
"unpack"
,
"gcinfo"
,
"pairs"
,
"rawget"
,
"loadstring"
,
"ipairs"
,
"_VERSION"
,
"setfenv"
,
"load"
,
"error"
,
NULL
,
};
/* Lua builtins which are not documented on the Lua documentation */
static
char
*
lua_builtins_not_documented_allow_list
[]
=
{
"newproxy"
,
NULL
,
};
/* Lua builtins which are allowed on initialization but will be removed right after */
static
char
*
lua_builtins_removed_after_initialization_allow_list
[]
=
{
"debug"
,
/* debug will be set to nil after the error handler will be created */
NULL
,
};
/* Those allow lists was created from the globals that was
* available to the user when the allow lists was first introduce.
* Because we do not want to break backward compatibility we keep
* all the globals. The allow lists will prevent us from accidentally
* creating unwanted globals in the future.
*
* Also notice that the allow list is only checked on start time,
* after that the global table is locked so not need to check anything.*/
static
char
**
allow_lists
[]
=
{
libraries_allow_list
,
redis_api_allow_list
,
lua_builtins_allow_list
,
lua_builtins_not_documented_allow_list
,
lua_builtins_removed_after_initialization_allow_list
,
NULL
,
};
/* Deny list contains elements which we know we do not want to add to globals
* and there is no need to print a warning message form them. We will print a
* log message only if an element was added to the globals and the element is
* not on the allow list nor on the back list. */
static
char
*
deny_list
[]
=
{
"dofile"
,
"loadfile"
,
"print"
,
NULL
,
};
static
int
redis_math_random
(
lua_State
*
L
);
static
int
redis_math_random
(
lua_State
*
L
);
static
int
redis_math_randomseed
(
lua_State
*
L
);
static
int
redis_math_randomseed
(
lua_State
*
L
);
static
void
redisProtocolToLuaType_Int
(
void
*
ctx
,
long
long
val
,
const
char
*
proto
,
size_t
proto_len
);
static
void
redisProtocolToLuaType_Int
(
void
*
ctx
,
long
long
val
,
const
char
*
proto
,
size_t
proto_len
);
...
@@ -1113,15 +1204,6 @@ static void luaLoadLibraries(lua_State *lua) {
...
@@ -1113,15 +1204,6 @@ static void luaLoadLibraries(lua_State *lua) {
#endif
#endif
}
}
/* Remove a functions that we don't want to expose to the Redis scripting
* environment. */
static
void
luaRemoveUnsupportedFunctions
(
lua_State
*
lua
)
{
lua_pushnil
(
lua
);
lua_setglobal
(
lua
,
"loadfile"
);
lua_pushnil
(
lua
);
lua_setglobal
(
lua
,
"dofile"
);
}
/* Return sds of the string value located on stack at the given index.
/* Return sds of the string value located on stack at the given index.
* Return NULL if the value is not a string. */
* Return NULL if the value is not a string. */
sds
luaGetStringSds
(
lua_State
*
lua
,
int
index
)
{
sds
luaGetStringSds
(
lua_State
*
lua
,
int
index
)
{
...
@@ -1135,107 +1217,120 @@ sds luaGetStringSds(lua_State *lua, int index) {
...
@@ -1135,107 +1217,120 @@ sds luaGetStringSds(lua_State *lua, int index) {
return
str_sds
;
return
str_sds
;
}
}
/* This function installs metamethods in the global table _G that prevent
static
int
luaProtectedTableError
(
lua_State
*
lua
)
{
* the creation of globals accidentally.
int
argc
=
lua_gettop
(
lua
);
*
if
(
argc
!=
2
)
{
* It should be the last to be called in the scripting engine initialization
serverLog
(
LL_WARNING
,
"malicious code trying to call luaProtectedTableError with wrong arguments"
);
* sequence, because it may interact with creation of globals.
luaL_error
(
lua
,
"Wrong number of arguments to luaProtectedTableError"
);
*
}
* On Legacy Lua (eval) we need to check 'w ~= \"main\"' otherwise we will not be able
if
(
!
lua_isstring
(
lua
,
-
1
)
&&
!
lua_isnumber
(
lua
,
-
1
))
{
* to create the global 'function <sha> ()' variable. On Functions Lua engine we do not use
luaL_error
(
lua
,
"Second argument to luaProtectedTableError must be a string or number"
);
* this trick so it's not needed. */
}
void
luaEnableGlobalsProtection
(
lua_State
*
lua
,
int
is_eval
)
{
const
char
*
variable_name
=
lua_tostring
(
lua
,
-
1
);
char
*
s
[
32
];
luaL_error
(
lua
,
"Script attempted to access nonexistent global variable '%s'"
,
variable_name
);
sds
code
=
sdsempty
();
return
0
;
int
j
=
0
;
/* strict.lua from: http://metalua.luaforge.net/src/lib/strict.lua.html.
* Modified to be adapted to Redis. */
s
[
j
++
]
=
"local dbg=debug
\n
"
;
s
[
j
++
]
=
"local mt = {}
\n
"
;
s
[
j
++
]
=
"setmetatable(_G, mt)
\n
"
;
s
[
j
++
]
=
"mt.__newindex = function (t, n, v)
\n
"
;
s
[
j
++
]
=
" if dbg.getinfo(2) then
\n
"
;
s
[
j
++
]
=
" local w = dbg.getinfo(2,
\"
S
\"
).what
\n
"
;
s
[
j
++
]
=
is_eval
?
" if w ~=
\"
main
\"
and w ~=
\"
C
\"
then
\n
"
:
" if w ~=
\"
C
\"
then
\n
"
;
s
[
j
++
]
=
" error(
\"
Script attempted to create global variable '
\"
..tostring(n)..
\"
'
\"
, 2)
\n
"
;
s
[
j
++
]
=
" end
\n
"
;
s
[
j
++
]
=
" end
\n
"
;
s
[
j
++
]
=
" rawset(t, n, v)
\n
"
;
s
[
j
++
]
=
"end
\n
"
;
s
[
j
++
]
=
"mt.__index = function (t, n)
\n
"
;
s
[
j
++
]
=
" if dbg.getinfo(2) and dbg.getinfo(2,
\"
S
\"
).what ~=
\"
C
\"
then
\n
"
;
s
[
j
++
]
=
" error(
\"
Script attempted to access nonexistent global variable '
\"
..tostring(n)..
\"
'
\"
, 2)
\n
"
;
s
[
j
++
]
=
" end
\n
"
;
s
[
j
++
]
=
" return rawget(t, n)
\n
"
;
s
[
j
++
]
=
"end
\n
"
;
s
[
j
++
]
=
"debug = nil
\n
"
;
s
[
j
++
]
=
NULL
;
for
(
j
=
0
;
s
[
j
]
!=
NULL
;
j
++
)
code
=
sdscatlen
(
code
,
s
[
j
],
strlen
(
s
[
j
]));
luaL_loadbuffer
(
lua
,
code
,
sdslen
(
code
),
"@enable_strict_lua"
);
lua_pcall
(
lua
,
0
,
0
,
0
);
sdsfree
(
code
);
}
}
/* Create a global protection function and put it to registry.
/* Set a special metatable on the table on the top of the stack.
* This need to be called once in the lua_State lifetime.
* The metatable will raise an error if the user tries to fetch
* After called it is possible to use luaSetGlobalProtection
* an un-existing value.
* to set global protection on a give table.
*
*
* The function assumes the Lua stack have a least enough
* The function assumes the Lua stack have a least enough
* space to push 2 element, its up to the caller to verify
* space to push 2 element, its up to the caller to verify
* this before calling this function.
* this before calling this function. */
*
void
luaSetErrorMetatable
(
lua_State
*
lua
)
{
* Notice, the difference between this and luaEnableGlobalsProtection
lua_newtable
(
lua
);
/* push metatable */
* is that luaEnableGlobalsProtection is enabling global protection
lua_pushcfunction
(
lua
,
luaProtectedTableError
);
/* push get error handler */
* on the current Lua globals. This registering a global protection
lua_setfield
(
lua
,
-
2
,
"__index"
);
* function that later can be applied on any table. */
lua_setmetatable
(
lua
,
-
2
);
void
luaRegisterGlobalProtectionFunction
(
lua_State
*
lua
)
{
lua_pushstring
(
lua
,
REGISTRY_SET_GLOBALS_PROTECTION_NAME
);
char
*
global_protection_func
=
"local dbg = debug
\n
"
"local globals_protection = function (t)
\n
"
" local mt = {}
\n
"
" setmetatable(t, mt)
\n
"
" mt.__newindex = function (t, n, v)
\n
"
" if dbg.getinfo(2) then
\n
"
" local w = dbg.getinfo(2,
\"
S
\"
).what
\n
"
" if w ~=
\"
C
\"
then
\n
"
" error(
\"
Script attempted to create global variable '
\"
..tostring(n)..
\"
'
\"
, 2)
\n
"
" end"
" end"
" rawset(t, n, v)
\n
"
" end
\n
"
" mt.__index = function (t, n)
\n
"
" if dbg.getinfo(2) and dbg.getinfo(2,
\"
S
\"
).what ~=
\"
C
\"
then
\n
"
" error(
\"
Script attempted to access nonexistent global variable '
\"
..tostring(n)..
\"
'
\"
, 2)
\n
"
" end
\n
"
" return rawget(t, n)
\n
"
" end
\n
"
"end
\n
"
"return globals_protection"
;
int
res
=
luaL_loadbuffer
(
lua
,
global_protection_func
,
strlen
(
global_protection_func
),
"@global_protection_def"
);
serverAssert
(
res
==
0
);
res
=
lua_pcall
(
lua
,
0
,
1
,
0
);
serverAssert
(
res
==
0
);
lua_settable
(
lua
,
LUA_REGISTRYINDEX
);
}
}
/* Set global protection on a given table.
static
int
luaNewIndexAllowList
(
lua_State
*
lua
)
{
* The table need to be located on the top of the lua stack.
int
argc
=
lua_gettop
(
lua
);
* After called, it will no longer be possible to set
if
(
argc
!=
3
)
{
* new items on the table. The function is not removing
serverLog
(
LL_WARNING
,
"malicious code trying to call luaProtectedTableError with wrong arguments"
);
* the table from the top of the stack!
luaL_error
(
lua
,
"Wrong number of arguments to luaNewIndexAllowList"
);
}
if
(
!
lua_istable
(
lua
,
-
3
))
{
luaL_error
(
lua
,
"first argument to luaNewIndexAllowList must be a table"
);
}
if
(
!
lua_isstring
(
lua
,
-
2
)
&&
!
lua_isnumber
(
lua
,
-
2
))
{
luaL_error
(
lua
,
"Second argument to luaNewIndexAllowList must be a string or number"
);
}
const
char
*
variable_name
=
lua_tostring
(
lua
,
-
2
);
/* check if the key is in our allow list */
char
***
allow_l
=
allow_lists
;
for
(;
*
allow_l
;
++
allow_l
){
char
**
c
=
*
allow_l
;
for
(;
*
c
;
++
c
)
{
if
(
strcmp
(
*
c
,
variable_name
)
==
0
)
{
break
;
}
}
if
(
*
c
)
{
break
;
}
}
if
(
!*
allow_l
)
{
/* Search the value on the back list, if its there we know that it was removed
* on purpose and there is no need to print a warning. */
char
**
c
=
deny_list
;
for
(
;
*
c
;
++
c
)
{
if
(
strcmp
(
*
c
,
variable_name
)
==
0
)
{
break
;
}
}
if
(
!*
c
)
{
serverLog
(
LL_WARNING
,
"A key '%s' was added to Lua globals which is not on the globals allow list nor listed on the deny list."
,
variable_name
);
}
}
else
{
lua_rawset
(
lua
,
-
3
);
}
return
0
;
}
/* Set a metatable with '__newindex' function that verify that
* the new index appears on our globals while list.
*
*
* The function assumes the Lua stack have a least enough
* The metatable is set on the table which located on the top
* space to push 2 element, its up to the caller to verify
* of the stack.
* this before calling this function. */
*/
void
luaSetGlobalProtection
(
lua_State
*
lua
)
{
void
luaSetAllowListProtection
(
lua_State
*
lua
)
{
lua_pushstring
(
lua
,
REGISTRY_SET_GLOBALS_PROTECTION_NAME
);
lua_newtable
(
lua
);
/* push metatable */
lua_gettable
(
lua
,
LUA_REGISTRYINDEX
);
lua_pushcfunction
(
lua
,
luaNewIndexAllowList
);
/* push get error handler */
lua_pushvalue
(
lua
,
-
2
);
lua_setfield
(
lua
,
-
2
,
"__newindex"
);
int
res
=
lua_pcall
(
lua
,
1
,
0
,
0
);
lua_setmetatable
(
lua
,
-
2
);
serverAssert
(
res
==
0
);
}
/* Set the readonly flag on the table located on the top of the stack
* and recursively call this function on each table located on the original
* table. Also, recursively call this function on the metatables.*/
void
luaSetTableProtectionRecursively
(
lua_State
*
lua
)
{
/* This protect us from a loop in case we already visited the table
* For example, globals has '_G' key which is pointing back to globals. */
if
(
lua_isreadonlytable
(
lua
,
-
1
))
{
return
;
}
/* protect the current table */
lua_enablereadonlytable
(
lua
,
-
1
,
1
);
lua_checkstack
(
lua
,
2
);
lua_pushnil
(
lua
);
/* Use nil to start iteration. */
while
(
lua_next
(
lua
,
-
2
))
{
/* Stack now: table, key, value */
if
(
lua_istable
(
lua
,
-
1
))
{
luaSetTableProtectionRecursively
(
lua
);
}
lua_pop
(
lua
,
1
);
}
/* protect the metatable if exists */
if
(
lua_getmetatable
(
lua
,
-
1
))
{
luaSetTableProtectionRecursively
(
lua
);
lua_pop
(
lua
,
1
);
/* pop the metatable */
}
}
}
void
luaRegisterVersion
(
lua_State
*
lua
)
{
void
luaRegisterVersion
(
lua_State
*
lua
)
{
...
@@ -1272,8 +1367,11 @@ void luaRegisterLogFunction(lua_State* lua) {
...
@@ -1272,8 +1367,11 @@ void luaRegisterLogFunction(lua_State* lua) {
}
}
void
luaRegisterRedisAPI
(
lua_State
*
lua
)
{
void
luaRegisterRedisAPI
(
lua_State
*
lua
)
{
lua_pushvalue
(
lua
,
LUA_GLOBALSINDEX
);
luaSetAllowListProtection
(
lua
);
lua_pop
(
lua
,
1
);
luaLoadLibraries
(
lua
);
luaLoadLibraries
(
lua
);
luaRemoveUnsupportedFunctions
(
lua
);
lua_pushcfunction
(
lua
,
luaRedisPcall
);
lua_pushcfunction
(
lua
,
luaRedisPcall
);
lua_setglobal
(
lua
,
"pcall"
);
lua_setglobal
(
lua
,
"pcall"
);
...
@@ -1504,9 +1602,19 @@ void luaCallFunction(scriptRunCtx* run_ctx, lua_State *lua, robj** keys, size_t
...
@@ -1504,9 +1602,19 @@ void luaCallFunction(scriptRunCtx* run_ctx, lua_State *lua, robj** keys, size_t
* EVAL received. */
* EVAL received. */
luaCreateArray
(
lua
,
keys
,
nkeys
);
luaCreateArray
(
lua
,
keys
,
nkeys
);
/* On eval, keys and arguments are globals. */
/* On eval, keys and arguments are globals. */
if
(
run_ctx
->
flags
&
SCRIPT_EVAL_MODE
)
lua_setglobal
(
lua
,
"KEYS"
);
if
(
run_ctx
->
flags
&
SCRIPT_EVAL_MODE
){
/* open global protection to set KEYS */
lua_enablereadonlytable
(
lua
,
LUA_GLOBALSINDEX
,
0
);
lua_setglobal
(
lua
,
"KEYS"
);
lua_enablereadonlytable
(
lua
,
LUA_GLOBALSINDEX
,
1
);
}
luaCreateArray
(
lua
,
args
,
nargs
);
luaCreateArray
(
lua
,
args
,
nargs
);
if
(
run_ctx
->
flags
&
SCRIPT_EVAL_MODE
)
lua_setglobal
(
lua
,
"ARGV"
);
if
(
run_ctx
->
flags
&
SCRIPT_EVAL_MODE
){
/* open global protection to set ARGV */
lua_enablereadonlytable
(
lua
,
LUA_GLOBALSINDEX
,
0
);
lua_setglobal
(
lua
,
"ARGV"
);
lua_enablereadonlytable
(
lua
,
LUA_GLOBALSINDEX
,
1
);
}
/* At this point whether this script was never seen before or if it was
/* At this point whether this script was never seen before or if it was
* already defined, we can call it.
* already defined, we can call it.
...
...
src/script_lua.h
View file @
d375595d
...
@@ -67,9 +67,10 @@ typedef struct errorInfo {
...
@@ -67,9 +67,10 @@ typedef struct errorInfo {
void
luaRegisterRedisAPI
(
lua_State
*
lua
);
void
luaRegisterRedisAPI
(
lua_State
*
lua
);
sds
luaGetStringSds
(
lua_State
*
lua
,
int
index
);
sds
luaGetStringSds
(
lua_State
*
lua
,
int
index
);
void
luaEnableGlobalsProtection
(
lua_State
*
lua
,
int
is_eval
);
void
luaRegisterGlobalProtectionFunction
(
lua_State
*
lua
);
void
luaRegisterGlobalProtectionFunction
(
lua_State
*
lua
);
void
luaSetGlobalProtection
(
lua_State
*
lua
);
void
luaSetErrorMetatable
(
lua_State
*
lua
);
void
luaSetAllowListProtection
(
lua_State
*
lua
);
void
luaSetTableProtectionRecursively
(
lua_State
*
lua
);
void
luaRegisterLogFunction
(
lua_State
*
lua
);
void
luaRegisterLogFunction
(
lua_State
*
lua
);
void
luaRegisterVersion
(
lua_State
*
lua
);
void
luaRegisterVersion
(
lua_State
*
lua
);
void
luaPushErrorBuff
(
lua_State
*
lua
,
sds
err_buff
);
void
luaPushErrorBuff
(
lua_State
*
lua
,
sds
err_buff
);
...
...
src/sentinel.c
View file @
d375595d
...
@@ -705,7 +705,7 @@ void sentinelEvent(int level, char *type, sentinelRedisInstance *ri,
...
@@ -705,7 +705,7 @@ void sentinelEvent(int level, char *type, sentinelRedisInstance *ri,
if
(
level
!=
LL_DEBUG
)
{
if
(
level
!=
LL_DEBUG
)
{
channel
=
createStringObject
(
type
,
strlen
(
type
));
channel
=
createStringObject
(
type
,
strlen
(
type
));
payload
=
createStringObject
(
msg
,
strlen
(
msg
));
payload
=
createStringObject
(
msg
,
strlen
(
msg
));
pubsubPublishMessage
(
channel
,
payload
);
pubsubPublishMessage
(
channel
,
payload
,
0
);
decrRefCount
(
channel
);
decrRefCount
(
channel
);
decrRefCount
(
payload
);
decrRefCount
(
payload
);
}
}
...
...
src/server.c
View file @
d375595d
...
@@ -36,7 +36,6 @@
...
@@ -36,7 +36,6 @@
#include "atomicvar.h"
#include "atomicvar.h"
#include "mt19937-64.h"
#include "mt19937-64.h"
#include "functions.h"
#include "functions.h"
#include "hdr_alloc.h"
#include <time.h>
#include <time.h>
#include <signal.h>
#include <signal.h>
...
@@ -1016,18 +1015,8 @@ void databasesCron(void) {
...
@@ -1016,18 +1015,8 @@ void databasesCron(void) {
}
}
}
}
/* We take a cached value of the unix time in the global state because with
static
inline
void
updateCachedTimeWithUs
(
int
update_daylight_info
,
const
long
long
ustime
)
{
* virtual memory and aging there is to store the current time in objects at
server
.
ustime
=
ustime
;
* every object access, and accuracy is not needed. To access a global var is
* a lot faster than calling time(NULL).
*
* This function should be fast because it is called at every command execution
* in call(), so it is possible to decide if to update the daylight saving
* info or not using the 'update_daylight_info' argument. Normally we update
* such info only when calling this function from serverCron() but not when
* calling it from call(). */
void
updateCachedTime
(
int
update_daylight_info
)
{
server
.
ustime
=
ustime
();
server
.
mstime
=
server
.
ustime
/
1000
;
server
.
mstime
=
server
.
ustime
/
1000
;
time_t
unixtime
=
server
.
mstime
/
1000
;
time_t
unixtime
=
server
.
mstime
/
1000
;
atomicSet
(
server
.
unixtime
,
unixtime
);
atomicSet
(
server
.
unixtime
,
unixtime
);
...
@@ -1045,6 +1034,21 @@ void updateCachedTime(int update_daylight_info) {
...
@@ -1045,6 +1034,21 @@ void updateCachedTime(int update_daylight_info) {
}
}
}
}
/* We take a cached value of the unix time in the global state because with
* virtual memory and aging there is to store the current time in objects at
* every object access, and accuracy is not needed. To access a global var is
* a lot faster than calling time(NULL).
*
* This function should be fast because it is called at every command execution
* in call(), so it is possible to decide if to update the daylight saving
* info or not using the 'update_daylight_info' argument. Normally we update
* such info only when calling this function from serverCron() but not when
* calling it from call(). */
void
updateCachedTime
(
int
update_daylight_info
)
{
const
long
long
us
=
ustime
();
updateCachedTimeWithUs
(
update_daylight_info
,
us
);
}
void
checkChildrenDone
(
void
)
{
void
checkChildrenDone
(
void
)
{
int
statloc
=
0
;
int
statloc
=
0
;
pid_t
pid
;
pid_t
pid
;
...
@@ -1209,10 +1213,16 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -1209,10 +1213,16 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
cronUpdateMemoryStats
();
cronUpdateMemoryStats
();
/* We received a SIGTERM, shutting down here in a safe way, as it is
/* We received a SIGTERM
or SIGINT
, shutting down here in a safe way, as it is
* not ok doing so inside the signal handler. */
* not ok doing so inside the signal handler. */
if
(
server
.
shutdown_asap
&&
!
isShutdownInitiated
())
{
if
(
server
.
shutdown_asap
&&
!
isShutdownInitiated
())
{
if
(
prepareForShutdown
(
SHUTDOWN_NOFLAGS
)
==
C_OK
)
exit
(
0
);
int
shutdownFlags
=
SHUTDOWN_NOFLAGS
;
if
(
server
.
last_sig_received
==
SIGINT
&&
server
.
shutdown_on_sigint
)
shutdownFlags
=
server
.
shutdown_on_sigint
;
else
if
(
server
.
last_sig_received
==
SIGTERM
&&
server
.
shutdown_on_sigterm
)
shutdownFlags
=
server
.
shutdown_on_sigterm
;
if
(
prepareForShutdown
(
shutdownFlags
)
==
C_OK
)
exit
(
0
);
}
else
if
(
isShutdownInitiated
())
{
}
else
if
(
isShutdownInitiated
())
{
if
(
server
.
mstime
>=
server
.
shutdown_mstime
||
isReadyToShutdown
())
{
if
(
server
.
mstime
>=
server
.
shutdown_mstime
||
isReadyToShutdown
())
{
if
(
finishShutdown
()
==
C_OK
)
exit
(
0
);
if
(
finishShutdown
()
==
C_OK
)
exit
(
0
);
...
@@ -1296,13 +1306,12 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -1296,13 +1306,12 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
if
(
server
.
aof_state
==
AOF_ON
&&
if
(
server
.
aof_state
==
AOF_ON
&&
!
hasActiveChildProcess
()
&&
!
hasActiveChildProcess
()
&&
server
.
aof_rewrite_perc
&&
server
.
aof_rewrite_perc
&&
server
.
aof_current_size
>
server
.
aof_rewrite_min_size
&&
server
.
aof_current_size
>
server
.
aof_rewrite_min_size
)
!
aofRewriteLimited
())
{
{
long
long
base
=
server
.
aof_rewrite_base_size
?
long
long
base
=
server
.
aof_rewrite_base_size
?
server
.
aof_rewrite_base_size
:
1
;
server
.
aof_rewrite_base_size
:
1
;
long
long
growth
=
(
server
.
aof_current_size
*
100
/
base
)
-
100
;
long
long
growth
=
(
server
.
aof_current_size
*
100
/
base
)
-
100
;
if
(
growth
>=
server
.
aof_rewrite_perc
)
{
if
(
growth
>=
server
.
aof_rewrite_perc
&&
!
aofRewriteLimited
()
)
{
serverLog
(
LL_NOTICE
,
"Starting automatic rewriting of AOF on %lld%% growth"
,
growth
);
serverLog
(
LL_NOTICE
,
"Starting automatic rewriting of AOF on %lld%% growth"
,
growth
);
rewriteAppendOnlyFileBackground
();
rewriteAppendOnlyFileBackground
();
}
}
...
@@ -1326,8 +1335,11 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -1326,8 +1335,11 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
* however to try every second is enough in case of 'hz' is set to
* however to try every second is enough in case of 'hz' is set to
* a higher frequency. */
* a higher frequency. */
run_with_period
(
1000
)
{
run_with_period
(
1000
)
{
if
(
server
.
aof_state
==
AOF_ON
&&
server
.
aof_last_write_status
==
C_ERR
)
if
((
server
.
aof_state
==
AOF_ON
||
server
.
aof_state
==
AOF_WAIT_REWRITE
)
&&
flushAppendOnlyFile
(
0
);
server
.
aof_last_write_status
==
C_ERR
)
{
flushAppendOnlyFile
(
0
);
}
}
}
/* Clear the paused clients state if needed. */
/* Clear the paused clients state if needed. */
...
@@ -1466,6 +1478,7 @@ void whileBlockedCron() {
...
@@ -1466,6 +1478,7 @@ void whileBlockedCron() {
if
(
prepareForShutdown
(
SHUTDOWN_NOSAVE
)
==
C_OK
)
exit
(
0
);
if
(
prepareForShutdown
(
SHUTDOWN_NOSAVE
)
==
C_OK
)
exit
(
0
);
serverLog
(
LL_WARNING
,
"SIGTERM received but errors trying to shut down the server, check the logs for more information"
);
serverLog
(
LL_WARNING
,
"SIGTERM received but errors trying to shut down the server, check the logs for more information"
);
server
.
shutdown_asap
=
0
;
server
.
shutdown_asap
=
0
;
server
.
last_sig_received
=
0
;
}
}
}
}
...
@@ -1509,6 +1522,8 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
...
@@ -1509,6 +1522,8 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
uint64_t
processed
=
0
;
uint64_t
processed
=
0
;
processed
+=
handleClientsWithPendingReadsUsingThreads
();
processed
+=
handleClientsWithPendingReadsUsingThreads
();
processed
+=
tlsProcessPendingData
();
processed
+=
tlsProcessPendingData
();
if
(
server
.
aof_state
==
AOF_ON
||
server
.
aof_state
==
AOF_WAIT_REWRITE
)
flushAppendOnlyFile
(
0
);
processed
+=
handleClientsWithPendingWrites
();
processed
+=
handleClientsWithPendingWrites
();
processed
+=
freeClientsInAsyncFreeQueue
();
processed
+=
freeClientsInAsyncFreeQueue
();
server
.
events_processed_while_blocked
+=
processed
;
server
.
events_processed_while_blocked
+=
processed
;
...
@@ -1584,15 +1599,21 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
...
@@ -1584,15 +1599,21 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
* client side caching protocol in broadcasting (BCAST) mode. */
* client side caching protocol in broadcasting (BCAST) mode. */
trackingBroadcastInvalidationMessages
();
trackingBroadcastInvalidationMessages
();
/* Write the AOF buffer on disk */
/* Try to process blocked clients every once in while.
*
* Example: A module calls RM_SignalKeyAsReady from within a timer callback
* (So we don't visit processCommand() at all).
*
* must be done before flushAppendOnlyFile, in case of appendfsync=always,
* since the unblocked clients may write data. */
handleClientsBlockedOnKeys
();
/* Write the AOF buffer on disk,
* must be done before handleClientsWithPendingWritesUsingThreads,
* in case of appendfsync=always. */
if
(
server
.
aof_state
==
AOF_ON
||
server
.
aof_state
==
AOF_WAIT_REWRITE
)
if
(
server
.
aof_state
==
AOF_ON
||
server
.
aof_state
==
AOF_WAIT_REWRITE
)
flushAppendOnlyFile
(
0
);
flushAppendOnlyFile
(
0
);
/* Try to process blocked clients every once in while. Example: A module
* calls RM_SignalKeyAsReady from within a timer callback (So we don't
* visit processCommand() at all). */
handleClientsBlockedOnKeys
();
/* Handle writes with pending output buffers. */
/* Handle writes with pending output buffers. */
handleClientsWithPendingWritesUsingThreads
();
handleClientsWithPendingWritesUsingThreads
();
...
@@ -1878,15 +1899,6 @@ void initServerConfig(void) {
...
@@ -1878,15 +1899,6 @@ void initServerConfig(void) {
appendServerSaveParams
(
300
,
100
);
/* save after 5 minutes and 100 changes */
appendServerSaveParams
(
300
,
100
);
/* save after 5 minutes and 100 changes */
appendServerSaveParams
(
60
,
10000
);
/* save after 1 minute and 10000 changes */
appendServerSaveParams
(
60
,
10000
);
/* save after 1 minute and 10000 changes */
/* Specify the allocation function for the hdr histogram */
hdrAllocFuncs
hdrallocfn
=
{
.
mallocFn
=
zmalloc
,
.
callocFn
=
zcalloc_num
,
.
reallocFn
=
zrealloc
,
.
freeFn
=
zfree
,
};
hdrSetAllocators
(
&
hdrallocfn
);
/* Replication related */
/* Replication related */
server
.
masterhost
=
NULL
;
server
.
masterhost
=
NULL
;
server
.
masterport
=
6379
;
server
.
masterport
=
6379
;
...
@@ -2286,6 +2298,7 @@ int listenToPort(int port, socketFds *sfd) {
...
@@ -2286,6 +2298,7 @@ int listenToPort(int port, socketFds *sfd) {
closeSocketListeners
(
sfd
);
closeSocketListeners
(
sfd
);
return
C_ERR
;
return
C_ERR
;
}
}
if
(
server
.
socket_mark_id
>
0
)
anetSetSockMarkId
(
NULL
,
sfd
->
fd
[
sfd
->
count
],
server
.
socket_mark_id
);
anetNonBlock
(
NULL
,
sfd
->
fd
[
sfd
->
count
]);
anetNonBlock
(
NULL
,
sfd
->
fd
[
sfd
->
count
]);
anetCloexec
(
sfd
->
fd
[
sfd
->
count
]);
anetCloexec
(
sfd
->
fd
[
sfd
->
count
]);
sfd
->
count
++
;
sfd
->
count
++
;
...
@@ -2338,6 +2351,7 @@ void resetServerStats(void) {
...
@@ -2338,6 +2351,7 @@ void resetServerStats(void) {
}
}
server
.
stat_aof_rewrites
=
0
;
server
.
stat_aof_rewrites
=
0
;
server
.
stat_rdb_saves
=
0
;
server
.
stat_rdb_saves
=
0
;
server
.
stat_aofrw_consecutive_failures
=
0
;
atomicSet
(
server
.
stat_net_input_bytes
,
0
);
atomicSet
(
server
.
stat_net_input_bytes
,
0
);
atomicSet
(
server
.
stat_net_output_bytes
,
0
);
atomicSet
(
server
.
stat_net_output_bytes
,
0
);
server
.
stat_unexpected_error_replies
=
0
;
server
.
stat_unexpected_error_replies
=
0
;
...
@@ -2539,6 +2553,7 @@ void initServer(void) {
...
@@ -2539,6 +2553,7 @@ void initServer(void) {
server
.
aof_last_write_status
=
C_OK
;
server
.
aof_last_write_status
=
C_OK
;
server
.
aof_last_write_errno
=
0
;
server
.
aof_last_write_errno
=
0
;
server
.
repl_good_slaves_count
=
0
;
server
.
repl_good_slaves_count
=
0
;
server
.
last_sig_received
=
0
;
/* Create the timer callback, this is our way to process many background
/* Create the timer callback, this is our way to process many background
* operations incrementally, like clients timeout, eviction of unaccessed
* operations incrementally, like clients timeout, eviction of unaccessed
...
@@ -2978,6 +2993,11 @@ struct redisCommand *lookupCommandOrOriginal(robj **argv ,int argc) {
...
@@ -2978,6 +2993,11 @@ struct redisCommand *lookupCommandOrOriginal(robj **argv ,int argc) {
return
cmd
;
return
cmd
;
}
}
/* Commands arriving from the master client or AOF client, should never be rejected. */
int
mustObeyClient
(
client
*
c
)
{
return
c
->
id
==
CLIENT_ID_AOF
||
c
->
flags
&
CLIENT_MASTER
;
}
static
int
shouldPropagate
(
int
target
)
{
static
int
shouldPropagate
(
int
target
)
{
if
(
!
server
.
replication_allowed
||
target
==
PROPAGATE_NONE
||
server
.
loading
)
if
(
!
server
.
replication_allowed
||
target
==
PROPAGATE_NONE
||
server
.
loading
)
return
0
;
return
0
;
...
@@ -3205,7 +3225,6 @@ int incrCommandStatsOnError(struct redisCommand *cmd, int flags) {
...
@@ -3205,7 +3225,6 @@ int incrCommandStatsOnError(struct redisCommand *cmd, int flags) {
*/
*/
void
call
(
client
*
c
,
int
flags
)
{
void
call
(
client
*
c
,
int
flags
)
{
long
long
dirty
;
long
long
dirty
;
monotime
call_timer
;
uint64_t
client_old_flags
=
c
->
flags
;
uint64_t
client_old_flags
=
c
->
flags
;
struct
redisCommand
*
real_cmd
=
c
->
realcmd
;
struct
redisCommand
*
real_cmd
=
c
->
realcmd
;
...
@@ -3230,22 +3249,34 @@ void call(client *c, int flags) {
...
@@ -3230,22 +3249,34 @@ void call(client *c, int flags) {
dirty
=
server
.
dirty
;
dirty
=
server
.
dirty
;
incrCommandStatsOnError
(
NULL
,
0
);
incrCommandStatsOnError
(
NULL
,
0
);
const
long
long
call_timer
=
ustime
();
/* Update cache time, in case we have nested calls we want to
/* Update cache time, in case we have nested calls we want to
* update only on the first call*/
* update only on the first call*/
if
(
server
.
fixed_time_expire
++
==
0
)
{
if
(
server
.
fixed_time_expire
++
==
0
)
{
updateCachedTime
(
0
);
updateCachedTime
WithUs
(
0
,
call_timer
);
}
}
server
.
in_nested_call
++
;
elapsedStart
(
&
call_timer
);
monotime
monotonic_start
=
0
;
if
(
monotonicGetType
()
==
MONOTONIC_CLOCK_HW
)
monotonic_start
=
getMonotonicUs
();
server
.
in_nested_call
++
;
c
->
cmd
->
proc
(
c
);
c
->
cmd
->
proc
(
c
);
const
long
duration
=
elapsedUs
(
call_timer
);
server
.
in_nested_call
--
;
/* In order to avoid performance implication due to querying the clock using a system call 3 times,
* we use a monotonic clock, when we are sure its cost is very low, and fall back to non-monotonic call otherwise. */
ustime_t
duration
;
if
(
monotonicGetType
()
==
MONOTONIC_CLOCK_HW
)
duration
=
getMonotonicUs
()
-
monotonic_start
;
else
duration
=
ustime
()
-
call_timer
;
c
->
duration
=
duration
;
c
->
duration
=
duration
;
dirty
=
server
.
dirty
-
dirty
;
dirty
=
server
.
dirty
-
dirty
;
if
(
dirty
<
0
)
dirty
=
0
;
if
(
dirty
<
0
)
dirty
=
0
;
server
.
in_nested_call
--
;
/* Update failed command calls if required. */
/* Update failed command calls if required. */
if
(
!
incrCommandStatsOnError
(
real_cmd
,
ERROR_COMMAND_FAILED
)
&&
c
->
deferred_reply_errors
)
{
if
(
!
incrCommandStatsOnError
(
real_cmd
,
ERROR_COMMAND_FAILED
)
&&
c
->
deferred_reply_errors
)
{
...
@@ -3410,6 +3441,8 @@ void rejectCommand(client *c, robj *reply) {
...
@@ -3410,6 +3441,8 @@ void rejectCommand(client *c, robj *reply) {
}
}
void
rejectCommandSds
(
client
*
c
,
sds
s
)
{
void
rejectCommandSds
(
client
*
c
,
sds
s
)
{
flagTransaction
(
c
);
if
(
c
->
cmd
)
c
->
cmd
->
rejected_calls
++
;
if
(
c
->
cmd
&&
c
->
cmd
->
proc
==
execCommand
)
{
if
(
c
->
cmd
&&
c
->
cmd
->
proc
==
execCommand
)
{
execCommandAbort
(
c
,
s
);
execCommandAbort
(
c
,
s
);
sdsfree
(
s
);
sdsfree
(
s
);
...
@@ -3420,8 +3453,6 @@ void rejectCommandSds(client *c, sds s) {
...
@@ -3420,8 +3453,6 @@ void rejectCommandSds(client *c, sds s) {
}
}
void
rejectCommandFormat
(
client
*
c
,
const
char
*
fmt
,
...)
{
void
rejectCommandFormat
(
client
*
c
,
const
char
*
fmt
,
...)
{
if
(
c
->
cmd
)
c
->
cmd
->
rejected_calls
++
;
flagTransaction
(
c
);
va_list
ap
;
va_list
ap
;
va_start
(
ap
,
fmt
);
va_start
(
ap
,
fmt
);
sds
s
=
sdscatvprintf
(
sdsempty
(),
fmt
,
ap
);
sds
s
=
sdscatvprintf
(
sdsempty
(),
fmt
,
ap
);
...
@@ -3475,6 +3506,54 @@ void populateCommandMovableKeys(struct redisCommand *cmd) {
...
@@ -3475,6 +3506,54 @@ void populateCommandMovableKeys(struct redisCommand *cmd) {
cmd
->
flags
|=
CMD_MOVABLE_KEYS
;
cmd
->
flags
|=
CMD_MOVABLE_KEYS
;
}
}
/* Check if c->cmd exists, fills `err` with details in case it doesn't.
* Return 1 if exists. */
int
commandCheckExistence
(
client
*
c
,
sds
*
err
)
{
if
(
c
->
cmd
)
return
1
;
if
(
!
err
)
return
0
;
if
(
isContainerCommandBySds
(
c
->
argv
[
0
]
->
ptr
))
{
/* If we can't find the command but argv[0] by itself is a command
* it means we're dealing with an invalid subcommand. Print Help. */
sds
cmd
=
sdsnew
((
char
*
)
c
->
argv
[
0
]
->
ptr
);
sdstoupper
(
cmd
);
*
err
=
sdsnew
(
NULL
);
*
err
=
sdscatprintf
(
*
err
,
"unknown subcommand '%.128s'. Try %s HELP."
,
(
char
*
)
c
->
argv
[
1
]
->
ptr
,
cmd
);
sdsfree
(
cmd
);
}
else
{
sds
args
=
sdsempty
();
int
i
;
for
(
i
=
1
;
i
<
c
->
argc
&&
sdslen
(
args
)
<
128
;
i
++
)
args
=
sdscatprintf
(
args
,
"'%.*s' "
,
128
-
(
int
)
sdslen
(
args
),
(
char
*
)
c
->
argv
[
i
]
->
ptr
);
*
err
=
sdsnew
(
NULL
);
*
err
=
sdscatprintf
(
*
err
,
"unknown command '%.128s', with args beginning with: %s"
,
(
char
*
)
c
->
argv
[
0
]
->
ptr
,
args
);
sdsfree
(
args
);
}
/* Make sure there are no newlines in the string, otherwise invalid protocol
* is emitted (The args come from the user, they may contain any character). */
sdsmapchars
(
*
err
,
"
\r\n
"
,
" "
,
2
);
return
0
;
}
/* Check if c->argc is valid for c->cmd, fills `err` with details in case it isn't.
* Return 1 if valid. */
int
commandCheckArity
(
client
*
c
,
sds
*
err
)
{
if
((
c
->
cmd
->
arity
>
0
&&
c
->
cmd
->
arity
!=
c
->
argc
)
||
(
c
->
argc
<
-
c
->
cmd
->
arity
))
{
if
(
err
)
{
*
err
=
sdsnew
(
NULL
);
*
err
=
sdscatprintf
(
*
err
,
"wrong number of arguments for '%s' command"
,
c
->
cmd
->
fullname
);
}
return
0
;
}
return
1
;
}
/* If this function gets called we already read a whole
/* If this function gets called we already read a whole
* command, arguments are in the client argv/argc fields.
* command, arguments are in the client argv/argc fields.
* processCommand() execute the command or prepare the
* processCommand() execute the command or prepare the
...
@@ -3514,29 +3593,13 @@ int processCommand(client *c) {
...
@@ -3514,29 +3593,13 @@ int processCommand(client *c) {
/* Now lookup the command and check ASAP about trivial error conditions
/* Now lookup the command and check ASAP about trivial error conditions
* such as wrong arity, bad command name and so forth. */
* such as wrong arity, bad command name and so forth. */
c
->
cmd
=
c
->
lastcmd
=
c
->
realcmd
=
lookupCommand
(
c
->
argv
,
c
->
argc
);
c
->
cmd
=
c
->
lastcmd
=
c
->
realcmd
=
lookupCommand
(
c
->
argv
,
c
->
argc
);
if
(
!
c
->
cmd
)
{
sds
err
;
if
(
isContainerCommandBySds
(
c
->
argv
[
0
]
->
ptr
))
{
if
(
!
commandCheckExistence
(
c
,
&
err
))
{
/* If we can't find the command but argv[0] by itself is a command
rejectCommandSds
(
c
,
err
);
* it means we're dealing with an invalid subcommand. Print Help. */
sds
cmd
=
sdsnew
((
char
*
)
c
->
argv
[
0
]
->
ptr
);
sdstoupper
(
cmd
);
rejectCommandFormat
(
c
,
"Unknown subcommand '%.128s'. Try %s HELP."
,
(
char
*
)
c
->
argv
[
1
]
->
ptr
,
cmd
);
sdsfree
(
cmd
);
return
C_OK
;
}
sds
args
=
sdsempty
();
int
i
;
for
(
i
=
1
;
i
<
c
->
argc
&&
sdslen
(
args
)
<
128
;
i
++
)
args
=
sdscatprintf
(
args
,
"'%.*s' "
,
128
-
(
int
)
sdslen
(
args
),
(
char
*
)
c
->
argv
[
i
]
->
ptr
);
rejectCommandFormat
(
c
,
"unknown command '%s', with args beginning with: %s"
,
(
char
*
)
c
->
argv
[
0
]
->
ptr
,
args
);
sdsfree
(
args
);
return
C_OK
;
return
C_OK
;
}
else
if
((
c
->
cmd
->
arity
>
0
&&
c
->
cmd
->
arity
!=
c
->
argc
)
||
}
(
c
->
argc
<
-
c
->
cmd
->
arity
))
if
(
!
commandCheckArity
(
c
,
&
err
))
{
{
rejectCommandSds
(
c
,
err
);
rejectCommandFormat
(
c
,
"wrong number of arguments for '%s' command"
,
c
->
cmd
->
fullname
);
return
C_OK
;
return
C_OK
;
}
}
...
@@ -3569,6 +3632,7 @@ int processCommand(client *c) {
...
@@ -3569,6 +3632,7 @@ int processCommand(client *c) {
(
c
->
cmd
->
proc
==
execCommand
&&
(
c
->
mstate
.
cmd_flags
&
(
CMD_WRITE
|
CMD_MAY_REPLICATE
)));
(
c
->
cmd
->
proc
==
execCommand
&&
(
c
->
mstate
.
cmd_flags
&
(
CMD_WRITE
|
CMD_MAY_REPLICATE
)));
int
is_deny_async_loading_command
=
(
c
->
cmd
->
flags
&
CMD_NO_ASYNC_LOADING
)
||
int
is_deny_async_loading_command
=
(
c
->
cmd
->
flags
&
CMD_NO_ASYNC_LOADING
)
||
(
c
->
cmd
->
proc
==
execCommand
&&
(
c
->
mstate
.
cmd_flags
&
CMD_NO_ASYNC_LOADING
));
(
c
->
cmd
->
proc
==
execCommand
&&
(
c
->
mstate
.
cmd_flags
&
CMD_NO_ASYNC_LOADING
));
int
obey_client
=
mustObeyClient
(
c
);
if
(
authRequired
(
c
))
{
if
(
authRequired
(
c
))
{
/* AUTH and HELLO and no auth commands are valid even in
/* AUTH and HELLO and no auth commands are valid even in
...
@@ -3620,23 +3684,20 @@ int processCommand(client *c) {
...
@@ -3620,23 +3684,20 @@ int processCommand(client *c) {
* 1) The sender of this command is our master.
* 1) The sender of this command is our master.
* 2) The command has no key arguments. */
* 2) The command has no key arguments. */
if
(
server
.
cluster_enabled
&&
if
(
server
.
cluster_enabled
&&
!
(
c
->
flags
&
CLIENT_MASTER
)
&&
!
mustObeyClient
(
c
)
&&
!
(
c
->
flags
&
CLIENT_SCRIPT
&&
server
.
script_caller
->
flags
&
CLIENT_MASTER
)
&&
!
(
!
(
c
->
cmd
->
flags
&
CMD_MOVABLE_KEYS
)
&&
c
->
cmd
->
key_specs_num
==
0
&&
!
(
!
(
c
->
cmd
->
flags
&
CMD_MOVABLE_KEYS
)
&&
c
->
cmd
->
key_specs_num
==
0
&&
c
->
cmd
->
proc
!=
execCommand
))
c
->
cmd
->
proc
!=
execCommand
))
{
{
int
hashslot
;
int
error_code
;
int
error_code
;
clusterNode
*
n
=
getNodeByQuery
(
c
,
c
->
cmd
,
c
->
argv
,
c
->
argc
,
clusterNode
*
n
=
getNodeByQuery
(
c
,
c
->
cmd
,
c
->
argv
,
c
->
argc
,
&
hash
slot
,
&
error_code
);
&
c
->
slot
,
&
error_code
);
if
(
n
==
NULL
||
n
!=
server
.
cluster
->
myself
)
{
if
(
n
==
NULL
||
n
!=
server
.
cluster
->
myself
)
{
if
(
c
->
cmd
->
proc
==
execCommand
)
{
if
(
c
->
cmd
->
proc
==
execCommand
)
{
discardTransaction
(
c
);
discardTransaction
(
c
);
}
else
{
}
else
{
flagTransaction
(
c
);
flagTransaction
(
c
);
}
}
clusterRedirectClient
(
c
,
n
,
hash
slot
,
error_code
);
clusterRedirectClient
(
c
,
n
,
c
->
slot
,
error_code
);
c
->
cmd
->
rejected_calls
++
;
c
->
cmd
->
rejected_calls
++
;
return
C_OK
;
return
C_OK
;
}
}
...
@@ -3706,15 +3767,29 @@ int processCommand(client *c) {
...
@@ -3706,15 +3767,29 @@ int processCommand(client *c) {
if
(
server
.
tracking_clients
)
trackingLimitUsedSlots
();
if
(
server
.
tracking_clients
)
trackingLimitUsedSlots
();
/* Don't accept write commands if there are problems persisting on disk
/* Don't accept write commands if there are problems persisting on disk
* and if this is a master instance. */
* unless coming from our master, in which case check the replica ignore
* disk write error config to either log or crash. */
int
deny_write_type
=
writeCommandsDeniedByDiskError
();
int
deny_write_type
=
writeCommandsDeniedByDiskError
();
if
(
deny_write_type
!=
DISK_ERROR_TYPE_NONE
&&
if
(
deny_write_type
!=
DISK_ERROR_TYPE_NONE
&&
server
.
masterhost
==
NULL
&&
(
is_write_command
||
c
->
cmd
->
proc
==
pingCommand
))
(
is_write_command
||
c
->
cmd
->
proc
==
pingCommand
))
{
{
sds
err
=
writeCommandsGetDiskErrorMessage
(
deny_write_type
);
if
(
obey_client
)
{
rejectCommandSds
(
c
,
err
);
if
(
!
server
.
repl_ignore_disk_write_error
&&
c
->
cmd
->
proc
!=
pingCommand
)
{
return
C_OK
;
serverPanic
(
"Replica was unable to write command to disk."
);
}
else
{
static
mstime_t
last_log_time_ms
=
0
;
const
mstime_t
log_interval_ms
=
10000
;
if
(
server
.
mstime
>
last_log_time_ms
+
log_interval_ms
)
{
last_log_time_ms
=
server
.
mstime
;
serverLog
(
LL_WARNING
,
"Replica is applying a command even though "
"it is unable to write to disk."
);
}
}
}
else
{
sds
err
=
writeCommandsGetDiskErrorMessage
(
deny_write_type
);
rejectCommandSds
(
c
,
err
);
return
C_OK
;
}
}
}
/* Don't accept write commands if there are not enough good slaves and
/* Don't accept write commands if there are not enough good slaves and
...
@@ -3727,7 +3802,7 @@ int processCommand(client *c) {
...
@@ -3727,7 +3802,7 @@ int processCommand(client *c) {
/* Don't accept write commands if this is a read only slave. But
/* Don't accept write commands if this is a read only slave. But
* accept write commands if this is our master. */
* accept write commands if this is our master. */
if
(
server
.
masterhost
&&
server
.
repl_slave_ro
&&
if
(
server
.
masterhost
&&
server
.
repl_slave_ro
&&
!
(
c
->
flags
&
CLIENT_MASTER
)
&&
!
obey_client
&&
is_write_command
)
is_write_command
)
{
{
rejectCommand
(
c
,
shared
.
roslaveerr
);
rejectCommand
(
c
,
shared
.
roslaveerr
);
...
@@ -3949,6 +4024,7 @@ static void cancelShutdown(void) {
...
@@ -3949,6 +4024,7 @@ static void cancelShutdown(void) {
server
.
shutdown_asap
=
0
;
server
.
shutdown_asap
=
0
;
server
.
shutdown_flags
=
0
;
server
.
shutdown_flags
=
0
;
server
.
shutdown_mstime
=
0
;
server
.
shutdown_mstime
=
0
;
server
.
last_sig_received
=
0
;
replyToClientsBlockedOnShutdown
();
replyToClientsBlockedOnShutdown
();
unpauseClients
(
PAUSE_DURING_SHUTDOWN
);
unpauseClients
(
PAUSE_DURING_SHUTDOWN
);
}
}
...
@@ -4309,6 +4385,7 @@ void addReplyCommandArgList(client *c, struct redisCommandArg *args, int num_arg
...
@@ -4309,6 +4385,7 @@ void addReplyCommandArgList(client *c, struct redisCommandArg *args, int num_arg
if
(
args
[
j
].
token
)
maplen
++
;
if
(
args
[
j
].
token
)
maplen
++
;
if
(
args
[
j
].
summary
)
maplen
++
;
if
(
args
[
j
].
summary
)
maplen
++
;
if
(
args
[
j
].
since
)
maplen
++
;
if
(
args
[
j
].
since
)
maplen
++
;
if
(
args
[
j
].
deprecated_since
)
maplen
++
;
if
(
args
[
j
].
flags
)
maplen
++
;
if
(
args
[
j
].
flags
)
maplen
++
;
if
(
args
[
j
].
type
==
ARG_TYPE_ONEOF
||
args
[
j
].
type
==
ARG_TYPE_BLOCK
)
if
(
args
[
j
].
type
==
ARG_TYPE_ONEOF
||
args
[
j
].
type
==
ARG_TYPE_BLOCK
)
maplen
++
;
maplen
++
;
...
@@ -4336,6 +4413,10 @@ void addReplyCommandArgList(client *c, struct redisCommandArg *args, int num_arg
...
@@ -4336,6 +4413,10 @@ void addReplyCommandArgList(client *c, struct redisCommandArg *args, int num_arg
addReplyBulkCString
(
c
,
"since"
);
addReplyBulkCString
(
c
,
"since"
);
addReplyBulkCString
(
c
,
args
[
j
].
since
);
addReplyBulkCString
(
c
,
args
[
j
].
since
);
}
}
if
(
args
[
j
].
deprecated_since
)
{
addReplyBulkCString
(
c
,
"deprecated_since"
);
addReplyBulkCString
(
c
,
args
[
j
].
deprecated_since
);
}
if
(
args
[
j
].
flags
)
{
if
(
args
[
j
].
flags
)
{
addReplyBulkCString
(
c
,
"flags"
);
addReplyBulkCString
(
c
,
"flags"
);
addReplyFlagsForArg
(
c
,
args
[
j
].
flags
);
addReplyFlagsForArg
(
c
,
args
[
j
].
flags
);
...
@@ -4562,6 +4643,7 @@ void addReplyCommandDocs(client *c, struct redisCommand *cmd) {
...
@@ -4562,6 +4643,7 @@ void addReplyCommandDocs(client *c, struct redisCommand *cmd) {
long
maplen
=
1
;
long
maplen
=
1
;
if
(
cmd
->
summary
)
maplen
++
;
if
(
cmd
->
summary
)
maplen
++
;
if
(
cmd
->
since
)
maplen
++
;
if
(
cmd
->
since
)
maplen
++
;
if
(
cmd
->
flags
&
CMD_MODULE
)
maplen
++
;
if
(
cmd
->
complexity
)
maplen
++
;
if
(
cmd
->
complexity
)
maplen
++
;
if
(
cmd
->
doc_flags
)
maplen
++
;
if
(
cmd
->
doc_flags
)
maplen
++
;
if
(
cmd
->
deprecated_since
)
maplen
++
;
if
(
cmd
->
deprecated_since
)
maplen
++
;
...
@@ -4588,6 +4670,10 @@ void addReplyCommandDocs(client *c, struct redisCommand *cmd) {
...
@@ -4588,6 +4670,10 @@ void addReplyCommandDocs(client *c, struct redisCommand *cmd) {
addReplyBulkCString
(
c
,
"complexity"
);
addReplyBulkCString
(
c
,
"complexity"
);
addReplyBulkCString
(
c
,
cmd
->
complexity
);
addReplyBulkCString
(
c
,
cmd
->
complexity
);
}
}
if
(
cmd
->
flags
&
CMD_MODULE
)
{
addReplyBulkCString
(
c
,
"module"
);
addReplyBulkCString
(
c
,
moduleNameFromCommand
(
cmd
));
}
if
(
cmd
->
doc_flags
)
{
if
(
cmd
->
doc_flags
)
{
addReplyBulkCString
(
c
,
"doc_flags"
);
addReplyBulkCString
(
c
,
"doc_flags"
);
addReplyDocFlagsForCommand
(
c
,
cmd
);
addReplyDocFlagsForCommand
(
c
,
cmd
);
...
@@ -5123,6 +5209,7 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
...
@@ -5123,6 +5209,7 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
"redis_mode:%s
\r\n
"
"redis_mode:%s
\r\n
"
"os:%s %s %s
\r\n
"
"os:%s %s %s
\r\n
"
"arch_bits:%i
\r\n
"
"arch_bits:%i
\r\n
"
"monotonic_clock:%s
\r\n
"
"multiplexing_api:%s
\r\n
"
"multiplexing_api:%s
\r\n
"
"atomicvar_api:%s
\r\n
"
"atomicvar_api:%s
\r\n
"
"gcc_version:%i.%i.%i
\r\n
"
"gcc_version:%i.%i.%i
\r\n
"
...
@@ -5146,6 +5233,7 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
...
@@ -5146,6 +5233,7 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
mode
,
mode
,
name
.
sysname
,
name
.
release
,
name
.
machine
,
name
.
sysname
,
name
.
release
,
name
.
machine
,
server
.
arch_bits
,
server
.
arch_bits
,
monotonicInfoString
(),
aeGetApiName
(),
aeGetApiName
(),
REDIS_ATOMIC_API
,
REDIS_ATOMIC_API
,
#ifdef __GNUC__
#ifdef __GNUC__
...
@@ -5383,6 +5471,7 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
...
@@ -5383,6 +5471,7 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
"aof_current_rewrite_time_sec:%jd
\r\n
"
"aof_current_rewrite_time_sec:%jd
\r\n
"
"aof_last_bgrewrite_status:%s
\r\n
"
"aof_last_bgrewrite_status:%s
\r\n
"
"aof_rewrites:%lld
\r\n
"
"aof_rewrites:%lld
\r\n
"
"aof_rewrites_consecutive_failures:%lld
\r\n
"
"aof_last_write_status:%s
\r\n
"
"aof_last_write_status:%s
\r\n
"
"aof_last_cow_size:%zu
\r\n
"
"aof_last_cow_size:%zu
\r\n
"
"module_fork_in_progress:%d
\r\n
"
"module_fork_in_progress:%d
\r\n
"
...
@@ -5414,6 +5503,7 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
...
@@ -5414,6 +5503,7 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
-
1
:
time
(
NULL
)
-
server
.
aof_rewrite_time_start
),
-
1
:
time
(
NULL
)
-
server
.
aof_rewrite_time_start
),
(
server
.
aof_lastbgrewrite_status
==
C_OK
)
?
"ok"
:
"err"
,
(
server
.
aof_lastbgrewrite_status
==
C_OK
)
?
"ok"
:
"err"
,
server
.
stat_aof_rewrites
,
server
.
stat_aof_rewrites
,
server
.
stat_aofrw_consecutive_failures
,
(
server
.
aof_last_write_status
==
C_OK
&&
(
server
.
aof_last_write_status
==
C_OK
&&
aof_bio_fsync_status
==
C_OK
)
?
"ok"
:
"err"
,
aof_bio_fsync_status
==
C_OK
)
?
"ok"
:
"err"
,
server
.
stat_aof_cow_bytes
,
server
.
stat_aof_cow_bytes
,
...
@@ -6227,6 +6317,7 @@ static void sigShutdownHandler(int sig) {
...
@@ -6227,6 +6317,7 @@ static void sigShutdownHandler(int sig) {
serverLogFromHandler
(
LL_WARNING
,
msg
);
serverLogFromHandler
(
LL_WARNING
,
msg
);
server
.
shutdown_asap
=
1
;
server
.
shutdown_asap
=
1
;
server
.
last_sig_received
=
sig
;
}
}
void
setupSignalHandlers
(
void
)
{
void
setupSignalHandlers
(
void
)
{
...
...
src/server.h
View file @
d375595d
...
@@ -603,6 +603,7 @@ typedef enum {
...
@@ -603,6 +603,7 @@ typedef enum {
#define NOTIFY_KEY_MISS (1<<11)
/* m (Note: This one is excluded from NOTIFY_ALL on purpose) */
#define NOTIFY_KEY_MISS (1<<11)
/* m (Note: This one is excluded from NOTIFY_ALL on purpose) */
#define NOTIFY_LOADED (1<<12)
/* module only key space notification, indicate a key loaded from rdb */
#define NOTIFY_LOADED (1<<12)
/* module only key space notification, indicate a key loaded from rdb */
#define NOTIFY_MODULE (1<<13)
/* d, module key space notification */
#define NOTIFY_MODULE (1<<13)
/* d, module key space notification */
#define NOTIFY_NEW (1<<14)
/* n, new key notification */
#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM | NOTIFY_MODULE)
/* A flag */
#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM | NOTIFY_MODULE)
/* A flag */
/* Using the following macro you can run code inside serverCron() with the
/* Using the following macro you can run code inside serverCron() with the
...
@@ -1061,7 +1062,7 @@ typedef struct replBacklog {
...
@@ -1061,7 +1062,7 @@ typedef struct replBacklog {
listNode
*
ref_repl_buf_node
;
/* Referenced node of replication buffer blocks,
listNode
*
ref_repl_buf_node
;
/* Referenced node of replication buffer blocks,
* see the definition of replBufBlock. */
* see the definition of replBufBlock. */
size_t
unindexed_count
;
/* The count from last creating index block. */
size_t
unindexed_count
;
/* The count from last creating index block. */
rax
*
blocks_index
;
/* The index of re
o
crded blocks of replication
rax
*
blocks_index
;
/* The index of rec
o
rded blocks of replication
* buffer for quickly searching replication
* buffer for quickly searching replication
* offset on partial resynchronization. */
* offset on partial resynchronization. */
long
long
histlen
;
/* Backlog actual data length */
long
long
histlen
;
/* Backlog actual data length */
...
@@ -1106,6 +1107,7 @@ typedef struct client {
...
@@ -1106,6 +1107,7 @@ typedef struct client {
buffer or object being sent. */
buffer or object being sent. */
time_t
ctime
;
/* Client creation time. */
time_t
ctime
;
/* Client creation time. */
long
duration
;
/* Current command duration. Used for measuring latency of blocking/non-blocking cmds */
long
duration
;
/* Current command duration. Used for measuring latency of blocking/non-blocking cmds */
int
slot
;
/* The slot the client is executing against. Set to -1 if no slot is being used */
time_t
lastinteraction
;
/* Time of the last interaction, used for timeout */
time_t
lastinteraction
;
/* Time of the last interaction, used for timeout */
time_t
obuf_soft_limit_reached_time
;
time_t
obuf_soft_limit_reached_time
;
uint64_t
flags
;
/* Client flags: CLIENT_* macros. */
uint64_t
flags
;
/* Client flags: CLIENT_* macros. */
...
@@ -1323,6 +1325,15 @@ struct redisMemOverhead {
...
@@ -1323,6 +1325,15 @@ struct redisMemOverhead {
}
*
db
;
}
*
db
;
};
};
/* Replication error behavior determines the replica behavior
* when it receives an error over the replication stream. In
* either case the error is logged. */
typedef
enum
{
PROPAGATION_ERR_BEHAVIOR_IGNORE
=
0
,
PROPAGATION_ERR_BEHAVIOR_PANIC
,
PROPAGATION_ERR_BEHAVIOR_PANIC_ON_REPLICAS
}
replicationErrorBehavior
;
/* This structure can be optionally passed to RDB save/load functions in
/* This structure can be optionally passed to RDB save/load functions in
* order to implement additional functionalities, by storing and loading
* order to implement additional functionalities, by storing and loading
* metadata to the RDB file.
* metadata to the RDB file.
...
@@ -1451,6 +1462,7 @@ struct redisServer {
...
@@ -1451,6 +1462,7 @@ struct redisServer {
redisAtomic
unsigned
int
lruclock
;
/* Clock for LRU eviction */
redisAtomic
unsigned
int
lruclock
;
/* Clock for LRU eviction */
volatile
sig_atomic_t
shutdown_asap
;
/* Shutdown ordered by signal handler. */
volatile
sig_atomic_t
shutdown_asap
;
/* Shutdown ordered by signal handler. */
mstime_t
shutdown_mstime
;
/* Timestamp to limit graceful shutdown. */
mstime_t
shutdown_mstime
;
/* Timestamp to limit graceful shutdown. */
int
last_sig_received
;
/* Indicates the last SIGNAL received, if any (e.g., SIGINT or SIGTERM). */
int
shutdown_flags
;
/* Flags passed to prepareForShutdown(). */
int
shutdown_flags
;
/* Flags passed to prepareForShutdown(). */
int
activerehashing
;
/* Incremental rehash in serverCron() */
int
activerehashing
;
/* Incremental rehash in serverCron() */
int
active_defrag_running
;
/* Active defragmentation running (holds current scan aggressiveness) */
int
active_defrag_running
;
/* Active defragmentation running (holds current scan aggressiveness) */
...
@@ -1493,6 +1505,7 @@ struct redisServer {
...
@@ -1493,6 +1505,7 @@ struct redisServer {
socketFds
ipfd
;
/* TCP socket file descriptors */
socketFds
ipfd
;
/* TCP socket file descriptors */
socketFds
tlsfd
;
/* TLS socket file descriptors */
socketFds
tlsfd
;
/* TLS socket file descriptors */
int
sofd
;
/* Unix socket file descriptor */
int
sofd
;
/* Unix socket file descriptor */
uint32_t
socket_mark_id
;
/* ID for listen socket marking */
socketFds
cfd
;
/* Cluster bus listening socket */
socketFds
cfd
;
/* Cluster bus listening socket */
list
*
clients
;
/* List of active clients */
list
*
clients
;
/* List of active clients */
list
*
clients_to_close
;
/* Clients to close asynchronously */
list
*
clients_to_close
;
/* Clients to close asynchronously */
...
@@ -1555,6 +1568,7 @@ struct redisServer {
...
@@ -1555,6 +1568,7 @@ struct redisServer {
monotime
stat_last_active_defrag_time
;
/* Timestamp of current active defrag start */
monotime
stat_last_active_defrag_time
;
/* Timestamp of current active defrag start */
size_t
stat_peak_memory
;
/* Max used memory record */
size_t
stat_peak_memory
;
/* Max used memory record */
long
long
stat_aof_rewrites
;
/* number of aof file rewrites performed */
long
long
stat_aof_rewrites
;
/* number of aof file rewrites performed */
long
long
stat_aofrw_consecutive_failures
;
/* The number of consecutive failures of aofrw */
long
long
stat_rdb_saves
;
/* number of rdb saves performed */
long
long
stat_rdb_saves
;
/* number of rdb saves performed */
long
long
stat_fork_time
;
/* Time needed to perform latest fork() */
long
long
stat_fork_time
;
/* Time needed to perform latest fork() */
double
stat_fork_rate
;
/* Fork rate in GB/sec. */
double
stat_fork_rate
;
/* Fork rate in GB/sec. */
...
@@ -1717,6 +1731,8 @@ struct redisServer {
...
@@ -1717,6 +1731,8 @@ struct redisServer {
* abort(). useful for Valgrind. */
* abort(). useful for Valgrind. */
/* Shutdown */
/* Shutdown */
int
shutdown_timeout
;
/* Graceful shutdown time limit in seconds. */
int
shutdown_timeout
;
/* Graceful shutdown time limit in seconds. */
int
shutdown_on_sigint
;
/* Shutdown flags configured for SIGINT. */
int
shutdown_on_sigterm
;
/* Shutdown flags configured for SIGTERM. */
/* Replication (master) */
/* Replication (master) */
char
replid
[
CONFIG_RUN_ID_SIZE
+
1
];
/* My current replication ID. */
char
replid
[
CONFIG_RUN_ID_SIZE
+
1
];
/* My current replication ID. */
...
@@ -1769,6 +1785,10 @@ struct redisServer {
...
@@ -1769,6 +1785,10 @@ struct redisServer {
int
replica_announced
;
/* If true, replica is announced by Sentinel */
int
replica_announced
;
/* If true, replica is announced by Sentinel */
int
slave_announce_port
;
/* Give the master this listening port. */
int
slave_announce_port
;
/* Give the master this listening port. */
char
*
slave_announce_ip
;
/* Give the master this ip address. */
char
*
slave_announce_ip
;
/* Give the master this ip address. */
int
propagation_error_behavior
;
/* Configures the behavior of the replica
* when it receives an error on the replication stream */
int
repl_ignore_disk_write_error
;
/* Configures whether replicas panic when unable to
* persist writes to AOF. */
/* The following two fields is where we store master PSYNC replid/offset
/* The following two fields is where we store master PSYNC replid/offset
* while the PSYNC is in progress. At the end we'll copy the fields into
* while the PSYNC is in progress. At the end we'll copy the fields into
* the server->master client structure. */
* the server->master client structure. */
...
@@ -2042,6 +2062,7 @@ typedef struct redisCommandArg {
...
@@ -2042,6 +2062,7 @@ typedef struct redisCommandArg {
const
char
*
summary
;
const
char
*
summary
;
const
char
*
since
;
const
char
*
since
;
int
flags
;
int
flags
;
const
char
*
deprecated_since
;
struct
redisCommandArg
*
subargs
;
struct
redisCommandArg
*
subargs
;
/* runtime populated data */
/* runtime populated data */
int
num_args
;
int
num_args
;
...
@@ -2349,6 +2370,7 @@ int moduleGetCommandChannelsViaAPI(struct redisCommand *cmd, robj **argv, int ar
...
@@ -2349,6 +2370,7 @@ int moduleGetCommandChannelsViaAPI(struct redisCommand *cmd, robj **argv, int ar
moduleType
*
moduleTypeLookupModuleByID
(
uint64_t
id
);
moduleType
*
moduleTypeLookupModuleByID
(
uint64_t
id
);
void
moduleTypeNameByID
(
char
*
name
,
uint64_t
moduleid
);
void
moduleTypeNameByID
(
char
*
name
,
uint64_t
moduleid
);
const
char
*
moduleTypeModuleName
(
moduleType
*
mt
);
const
char
*
moduleTypeModuleName
(
moduleType
*
mt
);
const
char
*
moduleNameFromCommand
(
struct
redisCommand
*
cmd
);
void
moduleFreeContext
(
struct
RedisModuleCtx
*
ctx
);
void
moduleFreeContext
(
struct
RedisModuleCtx
*
ctx
);
void
unblockClientFromModule
(
client
*
c
);
void
unblockClientFromModule
(
client
*
c
);
void
moduleHandleBlockedClients
(
void
);
void
moduleHandleBlockedClients
(
void
);
...
@@ -2509,7 +2531,7 @@ void unprotectClient(client *c);
...
@@ -2509,7 +2531,7 @@ void unprotectClient(client *c);
void
initThreadedIO
(
void
);
void
initThreadedIO
(
void
);
client
*
lookupClientByID
(
uint64_t
id
);
client
*
lookupClientByID
(
uint64_t
id
);
int
authRequired
(
client
*
c
);
int
authRequired
(
client
*
c
);
void
c
lientIn
stallWriteHandler
(
client
*
c
);
void
putC
lientIn
PendingWriteQueue
(
client
*
c
);
#ifdef __GNUC__
#ifdef __GNUC__
void
addReplyErrorFormatEx
(
client
*
c
,
int
flags
,
const
char
*
fmt
,
...)
void
addReplyErrorFormatEx
(
client
*
c
,
int
flags
,
const
char
*
fmt
,
...)
...
@@ -2860,6 +2882,8 @@ struct redisCommand *lookupCommandBySds(sds s);
...
@@ -2860,6 +2882,8 @@ struct redisCommand *lookupCommandBySds(sds s);
struct
redisCommand
*
lookupCommandByCStringLogic
(
dict
*
commands
,
const
char
*
s
);
struct
redisCommand
*
lookupCommandByCStringLogic
(
dict
*
commands
,
const
char
*
s
);
struct
redisCommand
*
lookupCommandByCString
(
const
char
*
s
);
struct
redisCommand
*
lookupCommandByCString
(
const
char
*
s
);
struct
redisCommand
*
lookupCommandOrOriginal
(
robj
**
argv
,
int
argc
);
struct
redisCommand
*
lookupCommandOrOriginal
(
robj
**
argv
,
int
argc
);
int
commandCheckExistence
(
client
*
c
,
sds
*
err
);
int
commandCheckArity
(
client
*
c
,
sds
*
err
);
void
startCommandExecution
();
void
startCommandExecution
();
int
incrCommandStatsOnError
(
struct
redisCommand
*
cmd
,
int
flags
);
int
incrCommandStatsOnError
(
struct
redisCommand
*
cmd
,
int
flags
);
void
call
(
client
*
c
,
int
flags
);
void
call
(
client
*
c
,
int
flags
);
...
@@ -2877,7 +2901,7 @@ int prepareForShutdown(int flags);
...
@@ -2877,7 +2901,7 @@ int prepareForShutdown(int flags);
void
replyToClientsBlockedOnShutdown
(
void
);
void
replyToClientsBlockedOnShutdown
(
void
);
int
abortShutdown
(
void
);
int
abortShutdown
(
void
);
void
afterCommand
(
client
*
c
);
void
afterCommand
(
client
*
c
);
int
inNestedCall
(
void
);
int
mustObeyClient
(
client
*
c
);
#ifdef __GNUC__
#ifdef __GNUC__
void
_serverLog
(
int
level
,
const
char
*
fmt
,
...)
void
_serverLog
(
int
level
,
const
char
*
fmt
,
...)
__attribute__
((
format
(
printf
,
2
,
3
)));
__attribute__
((
format
(
printf
,
2
,
3
)));
...
@@ -2962,8 +2986,8 @@ int pubsubUnsubscribeAllChannels(client *c, int notify);
...
@@ -2962,8 +2986,8 @@ int pubsubUnsubscribeAllChannels(client *c, int notify);
int
pubsubUnsubscribeShardAllChannels
(
client
*
c
,
int
notify
);
int
pubsubUnsubscribeShardAllChannels
(
client
*
c
,
int
notify
);
void
pubsubUnsubscribeShardChannels
(
robj
**
channels
,
unsigned
int
count
);
void
pubsubUnsubscribeShardChannels
(
robj
**
channels
,
unsigned
int
count
);
int
pubsubUnsubscribeAllPatterns
(
client
*
c
,
int
notify
);
int
pubsubUnsubscribeAllPatterns
(
client
*
c
,
int
notify
);
int
pubsubPublishMessage
(
robj
*
channel
,
robj
*
message
);
int
pubsubPublishMessage
(
robj
*
channel
,
robj
*
message
,
int
sharded
);
int
pubsubPublishMessage
Shard
(
robj
*
channel
,
robj
*
message
);
int
pubsubPublishMessage
AndPropagateToCluster
(
robj
*
channel
,
robj
*
message
,
int
sharded
);
void
addReplyPubsubMessage
(
client
*
c
,
robj
*
channel
,
robj
*
msg
);
void
addReplyPubsubMessage
(
client
*
c
,
robj
*
channel
,
robj
*
msg
);
int
serverPubsubSubscriptionCount
();
int
serverPubsubSubscriptionCount
();
int
serverPubsubShardSubscriptionCount
();
int
serverPubsubShardSubscriptionCount
();
...
...
src/t_hash.c
View file @
d375595d
...
@@ -146,39 +146,24 @@ robj *hashTypeGetValueObject(robj *o, sds field) {
...
@@ -146,39 +146,24 @@ robj *hashTypeGetValueObject(robj *o, sds field) {
* exist. */
* exist. */
size_t
hashTypeGetValueLength
(
robj
*
o
,
sds
field
)
{
size_t
hashTypeGetValueLength
(
robj
*
o
,
sds
field
)
{
size_t
len
=
0
;
size_t
len
=
0
;
if
(
o
->
encoding
==
OBJ_ENCODING_LISTPACK
)
{
unsigned
char
*
vstr
=
NULL
;
unsigned
char
*
vstr
=
NULL
;
unsigned
int
vlen
=
UINT_MAX
;
unsigned
int
vlen
=
UINT_MAX
;
long
long
vll
=
LLONG_MAX
;
long
long
vll
=
LLONG_MAX
;
if
(
hashTypeGetFromListpack
(
o
,
field
,
&
vstr
,
&
vlen
,
&
vll
)
==
0
)
if
(
hashTypeGetValue
(
o
,
field
,
&
vstr
,
&
vlen
,
&
vll
)
==
C_OK
)
len
=
vstr
?
vlen
:
sdigits10
(
vll
);
len
=
vstr
?
vlen
:
sdigits10
(
vll
);
}
else
if
(
o
->
encoding
==
OBJ_ENCODING_HT
)
{
sds
aux
;
if
((
aux
=
hashTypeGetFromHashTable
(
o
,
field
))
!=
NULL
)
len
=
sdslen
(
aux
);
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
return
len
;
return
len
;
}
}
/* Test if the specified field exists in the given hash. Returns 1 if the field
/* Test if the specified field exists in the given hash. Returns 1 if the field
* exists, and 0 when it doesn't. */
* exists, and 0 when it doesn't. */
int
hashTypeExists
(
robj
*
o
,
sds
field
)
{
int
hashTypeExists
(
robj
*
o
,
sds
field
)
{
if
(
o
->
encoding
==
OBJ_ENCODING_LISTPACK
)
{
unsigned
char
*
vstr
=
NULL
;
unsigned
char
*
vstr
=
NULL
;
unsigned
int
vlen
=
UINT_MAX
;
unsigned
int
vlen
=
UINT_MAX
;
long
long
vll
=
LLONG_MAX
;
long
long
vll
=
LLONG_MAX
;
if
(
hashTypeGetFromListpack
(
o
,
field
,
&
vstr
,
&
vlen
,
&
vll
)
==
0
)
return
1
;
return
hashTypeGetValue
(
o
,
field
,
&
vstr
,
&
vlen
,
&
vll
)
==
C_OK
;
}
else
if
(
o
->
encoding
==
OBJ_ENCODING_HT
)
{
if
(
hashTypeGetFromHashTable
(
o
,
field
)
!=
NULL
)
return
1
;
}
else
{
serverPanic
(
"Unknown hash encoding"
);
}
return
0
;
}
}
/* Add a new field, overwrite the old with the new value if it already exists.
/* Add a new field, overwrite the old with the new value if it already exists.
...
@@ -205,6 +190,14 @@ int hashTypeExists(robj *o, sds field) {
...
@@ -205,6 +190,14 @@ int hashTypeExists(robj *o, sds field) {
int
hashTypeSet
(
robj
*
o
,
sds
field
,
sds
value
,
int
flags
)
{
int
hashTypeSet
(
robj
*
o
,
sds
field
,
sds
value
,
int
flags
)
{
int
update
=
0
;
int
update
=
0
;
/* Check if the field is too long for listpack, and convert before adding the item.
* This is needed for HINCRBY* case since in other commands this is handled early by
* hashTypeTryConversion, so this check will be a NOP. */
if
(
o
->
encoding
==
OBJ_ENCODING_LISTPACK
)
{
if
(
sdslen
(
field
)
>
server
.
hash_max_listpack_value
||
sdslen
(
value
)
>
server
.
hash_max_listpack_value
)
hashTypeConvert
(
o
,
OBJ_ENCODING_HT
);
}
if
(
o
->
encoding
==
OBJ_ENCODING_LISTPACK
)
{
if
(
o
->
encoding
==
OBJ_ENCODING_LISTPACK
)
{
unsigned
char
*
zl
,
*
fptr
,
*
vptr
;
unsigned
char
*
zl
,
*
fptr
,
*
vptr
;
...
@@ -717,37 +710,23 @@ void hincrbyfloatCommand(client *c) {
...
@@ -717,37 +710,23 @@ void hincrbyfloatCommand(client *c) {
}
}
static
void
addHashFieldToReply
(
client
*
c
,
robj
*
o
,
sds
field
)
{
static
void
addHashFieldToReply
(
client
*
c
,
robj
*
o
,
sds
field
)
{
int
ret
;
if
(
o
==
NULL
)
{
if
(
o
==
NULL
)
{
addReplyNull
(
c
);
addReplyNull
(
c
);
return
;
return
;
}
}
if
(
o
->
encoding
==
OBJ_ENCODING_LISTPACK
)
{
unsigned
char
*
vstr
=
NULL
;
unsigned
char
*
vstr
=
NULL
;
unsigned
int
vlen
=
UINT_MAX
;
unsigned
int
vlen
=
UINT_MAX
;
long
long
vll
=
LLONG_MAX
;
long
long
vll
=
LLONG_MAX
;
ret
=
hashTypeGet
FromListpack
(
o
,
field
,
&
vstr
,
&
vlen
,
&
vll
)
;
if
(
hashTypeGet
Value
(
o
,
field
,
&
vstr
,
&
vlen
,
&
vll
)
==
C_OK
)
{
if
(
ret
<
0
)
{
if
(
vstr
)
{
addReply
N
ul
l
(
c
);
addReply
B
ul
kCBuffer
(
c
,
vstr
,
vlen
);
}
else
{
}
else
{
if
(
vstr
)
{
addReplyBulkLongLong
(
c
,
vll
);
addReplyBulkCBuffer
(
c
,
vstr
,
vlen
);
}
else
{
addReplyBulkLongLong
(
c
,
vll
);
}
}
}
}
else
if
(
o
->
encoding
==
OBJ_ENCODING_HT
)
{
sds
value
=
hashTypeGetFromHashTable
(
o
,
field
);
if
(
value
==
NULL
)
addReplyNull
(
c
);
else
addReplyBulkCBuffer
(
c
,
value
,
sdslen
(
value
));
}
else
{
}
else
{
serverPanic
(
"Unknown hash encoding"
);
addReplyNull
(
c
);
}
}
}
}
...
@@ -907,7 +886,7 @@ void hscanCommand(client *c) {
...
@@ -907,7 +886,7 @@ void hscanCommand(client *c) {
scanGenericCommand
(
c
,
o
,
cursor
);
scanGenericCommand
(
c
,
o
,
cursor
);
}
}
static
void
h
a
rndfieldReplyWithListpack
(
client
*
c
,
unsigned
int
count
,
listpackEntry
*
keys
,
listpackEntry
*
vals
)
{
static
void
hr
a
ndfieldReplyWithListpack
(
client
*
c
,
unsigned
int
count
,
listpackEntry
*
keys
,
listpackEntry
*
vals
)
{
for
(
unsigned
long
i
=
0
;
i
<
count
;
i
++
)
{
for
(
unsigned
long
i
=
0
;
i
<
count
;
i
++
)
{
if
(
vals
&&
c
->
resp
>
2
)
if
(
vals
&&
c
->
resp
>
2
)
addReplyArrayLen
(
c
,
2
);
addReplyArrayLen
(
c
,
2
);
...
@@ -990,7 +969,7 @@ void hrandfieldWithCountCommand(client *c, long l, int withvalues) {
...
@@ -990,7 +969,7 @@ void hrandfieldWithCountCommand(client *c, long l, int withvalues) {
sample_count
=
count
>
limit
?
limit
:
count
;
sample_count
=
count
>
limit
?
limit
:
count
;
count
-=
sample_count
;
count
-=
sample_count
;
lpRandomPairs
(
hash
->
ptr
,
sample_count
,
keys
,
vals
);
lpRandomPairs
(
hash
->
ptr
,
sample_count
,
keys
,
vals
);
h
a
rndfieldReplyWithListpack
(
c
,
sample_count
,
keys
,
vals
);
hr
a
ndfieldReplyWithListpack
(
c
,
sample_count
,
keys
,
vals
);
}
}
zfree
(
keys
);
zfree
(
keys
);
zfree
(
vals
);
zfree
(
vals
);
...
@@ -1092,7 +1071,7 @@ void hrandfieldWithCountCommand(client *c, long l, int withvalues) {
...
@@ -1092,7 +1071,7 @@ void hrandfieldWithCountCommand(client *c, long l, int withvalues) {
if
(
withvalues
)
if
(
withvalues
)
vals
=
zmalloc
(
sizeof
(
listpackEntry
)
*
count
);
vals
=
zmalloc
(
sizeof
(
listpackEntry
)
*
count
);
serverAssert
(
lpRandomPairsUnique
(
hash
->
ptr
,
count
,
keys
,
vals
)
==
count
);
serverAssert
(
lpRandomPairsUnique
(
hash
->
ptr
,
count
,
keys
,
vals
)
==
count
);
h
a
rndfieldReplyWithListpack
(
c
,
count
,
keys
,
vals
);
hr
a
ndfieldReplyWithListpack
(
c
,
count
,
keys
,
vals
);
zfree
(
keys
);
zfree
(
keys
);
zfree
(
vals
);
zfree
(
vals
);
return
;
return
;
...
...
src/t_stream.c
View file @
d375595d
...
@@ -1000,7 +1000,7 @@ static int streamParseAddOrTrimArgsOrReply(client *c, streamAddTrimArgs *args, i
...
@@ -1000,7 +1000,7 @@ static int streamParseAddOrTrimArgsOrReply(client *c, streamAddTrimArgs *args, i
return
-
1
;
return
-
1
;
}
}
if
(
c
==
server
.
master
||
c
->
id
==
CLIENT_ID_AOF
)
{
if
(
mustObeyClient
(
c
)
)
{
/* If command came from master or from AOF we must not enforce maxnodes
/* If command came from master or from AOF we must not enforce maxnodes
* (The maxlen/minid argument was re-written to make sure there's no
* (The maxlen/minid argument was re-written to make sure there's no
* inconsistency). */
* inconsistency). */
...
@@ -1370,24 +1370,35 @@ void streamLastValidID(stream *s, streamID *maxid)
...
@@ -1370,24 +1370,35 @@ void streamLastValidID(stream *s, streamID *maxid)
streamIteratorStop
(
&
si
);
streamIteratorStop
(
&
si
);
}
}
/* Maximum size for a stream ID string. In theory 20*2+1 should be enough,
* But to avoid chance for off by one issues and null-term, in case this will
* be used as parsing buffer, we use a slightly larger buffer. On the other
* hand considering sds header is gonna add 4 bytes, we wanna keep below the
* allocator's 48 bytes bin. */
#define STREAM_ID_STR_LEN 44
sds
createStreamIDString
(
streamID
*
id
)
{
/* Optimization: pre-allocate a big enough buffer to avoid reallocs. */
sds
str
=
sdsnewlen
(
SDS_NOINIT
,
STREAM_ID_STR_LEN
);
sdssetlen
(
str
,
0
);
return
sdscatfmt
(
str
,
"%U-%U"
,
id
->
ms
,
id
->
seq
);
}
/* Emit a reply in the client output buffer by formatting a Stream ID
/* Emit a reply in the client output buffer by formatting a Stream ID
* in the standard <ms>-<seq> format, using the simple string protocol
* in the standard <ms>-<seq> format, using the simple string protocol
* of REPL. */
* of REPL. */
void
addReplyStreamID
(
client
*
c
,
streamID
*
id
)
{
void
addReplyStreamID
(
client
*
c
,
streamID
*
id
)
{
sds
replyid
=
sdscatfmt
(
sdsempty
(),
"%U-%U"
,
id
->
ms
,
id
->
seq
);
addReplyBulkSds
(
c
,
createStreamIDString
(
id
));
addReplyBulkSds
(
c
,
replyid
);
}
}
void
setDeferredReplyStreamID
(
client
*
c
,
void
*
dr
,
streamID
*
id
)
{
void
setDeferredReplyStreamID
(
client
*
c
,
void
*
dr
,
streamID
*
id
)
{
sds
replyid
=
sdscatfmt
(
sdsempty
(),
"%U-%U"
,
id
->
ms
,
id
->
seq
);
setDeferredReplyBulkSds
(
c
,
dr
,
createStreamIDString
(
id
));
setDeferredReplyBulkSds
(
c
,
dr
,
replyid
);
}
}
/* Similar to the above function, but just creates an object, usually useful
/* Similar to the above function, but just creates an object, usually useful
* for replication purposes to create arguments. */
* for replication purposes to create arguments. */
robj
*
createObjectFromStreamID
(
streamID
*
id
)
{
robj
*
createObjectFromStreamID
(
streamID
*
id
)
{
return
createObject
(
OBJ_STRING
,
sdscatfmt
(
sdsempty
(),
"%U-%U"
,
return
createObject
(
OBJ_STRING
,
createStreamIDString
(
id
));
id
->
ms
,
id
->
seq
));
}
}
/* Returns non-zero if the ID is 0-0. */
/* Returns non-zero if the ID is 0-0. */
...
@@ -2025,7 +2036,8 @@ void xaddCommand(client *c) {
...
@@ -2025,7 +2036,8 @@ void xaddCommand(client *c) {
addReplyError
(
c
,
"Elements are too large to be stored"
);
addReplyError
(
c
,
"Elements are too large to be stored"
);
return
;
return
;
}
}
addReplyStreamID
(
c
,
&
id
);
sds
replyid
=
createStreamIDString
(
&
id
);
addReplyBulkCBuffer
(
c
,
replyid
,
sdslen
(
replyid
));
signalModifiedKey
(
c
,
c
->
db
,
c
->
argv
[
1
]);
signalModifiedKey
(
c
,
c
->
db
,
c
->
argv
[
1
]);
notifyKeyspaceEvent
(
NOTIFY_STREAM
,
"xadd"
,
c
->
argv
[
1
],
c
->
db
->
id
);
notifyKeyspaceEvent
(
NOTIFY_STREAM
,
"xadd"
,
c
->
argv
[
1
],
c
->
db
->
id
);
...
@@ -2050,9 +2062,11 @@ void xaddCommand(client *c) {
...
@@ -2050,9 +2062,11 @@ void xaddCommand(client *c) {
/* Let's rewrite the ID argument with the one actually generated for
/* Let's rewrite the ID argument with the one actually generated for
* AOF/replication propagation. */
* AOF/replication propagation. */
if
(
!
parsed_args
.
id_given
||
!
parsed_args
.
seq_given
)
{
if
(
!
parsed_args
.
id_given
||
!
parsed_args
.
seq_given
)
{
robj
*
idarg
=
createObject
FromStreamID
(
&
id
);
robj
*
idarg
=
createObject
(
OBJ_STRING
,
reply
id
);
rewriteClientCommandArgument
(
c
,
idpos
,
idarg
);
rewriteClientCommandArgument
(
c
,
idpos
,
idarg
);
decrRefCount
(
idarg
);
decrRefCount
(
idarg
);
}
else
{
sdsfree
(
replyid
);
}
}
/* We need to signal to blocked clients that there is new data on this
/* We need to signal to blocked clients that there is new data on this
...
...
src/t_string.c
View file @
d375595d
...
@@ -38,7 +38,7 @@ int getGenericCommand(client *c);
...
@@ -38,7 +38,7 @@ int getGenericCommand(client *c);
*----------------------------------------------------------------------------*/
*----------------------------------------------------------------------------*/
static
int
checkStringLength
(
client
*
c
,
long
long
size
)
{
static
int
checkStringLength
(
client
*
c
,
long
long
size
)
{
if
(
!
(
c
->
flags
&
CLIENT_MASTER
)
&&
size
>
server
.
proto_max_bulk_len
)
{
if
(
!
mustObeyClient
(
c
)
&&
size
>
server
.
proto_max_bulk_len
)
{
addReplyError
(
c
,
"string exceeds maximum allowed size (proto-max-bulk-len)"
);
addReplyError
(
c
,
"string exceeds maximum allowed size (proto-max-bulk-len)"
);
return
C_ERR
;
return
C_ERR
;
}
}
...
@@ -792,7 +792,7 @@ void lcsCommand(client *c) {
...
@@ -792,7 +792,7 @@ void lcsCommand(client *c) {
/* Setup an uint32_t array to store at LCS[i,j] the length of the
/* Setup an uint32_t array to store at LCS[i,j] the length of the
* LCS A0..i-1, B0..j-1. Note that we have a linear array here, so
* LCS A0..i-1, B0..j-1. Note that we have a linear array here, so
* we index it as LCS[j+(blen+1)*
j
] */
* we index it as LCS[j+(blen+1)*
i
] */
#define LCS(A,B) lcs[(B)+((A)*(blen+1))]
#define LCS(A,B) lcs[(B)+((A)*(blen+1))]
/* Try to allocate the LCS table, and abort on overflow or insufficient memory. */
/* Try to allocate the LCS table, and abort on overflow or insufficient memory. */
...
...
src/t_zset.c
View file @
d375595d
...
@@ -1029,17 +1029,25 @@ unsigned char *zzlInsertAt(unsigned char *zl, unsigned char *eptr, sds ele, doub
...
@@ -1029,17 +1029,25 @@ unsigned char *zzlInsertAt(unsigned char *zl, unsigned char *eptr, sds ele, doub
unsigned
char
*
sptr
;
unsigned
char
*
sptr
;
char
scorebuf
[
MAX_D2STRING_CHARS
];
char
scorebuf
[
MAX_D2STRING_CHARS
];
int
scorelen
;
int
scorelen
;
long
long
lscore
;
scorelen
=
d2string
(
scorebuf
,
sizeof
(
scorebuf
),
score
);
int
score_is_long
=
double2ll
(
score
,
&
lscore
);
if
(
!
score_is_long
)
scorelen
=
d2string
(
scorebuf
,
sizeof
(
scorebuf
),
score
);
if
(
eptr
==
NULL
)
{
if
(
eptr
==
NULL
)
{
zl
=
lpAppend
(
zl
,(
unsigned
char
*
)
ele
,
sdslen
(
ele
));
zl
=
lpAppend
(
zl
,(
unsigned
char
*
)
ele
,
sdslen
(
ele
));
zl
=
lpAppend
(
zl
,(
unsigned
char
*
)
scorebuf
,
scorelen
);
if
(
score_is_long
)
zl
=
lpAppendInteger
(
zl
,
lscore
);
else
zl
=
lpAppend
(
zl
,(
unsigned
char
*
)
scorebuf
,
scorelen
);
}
else
{
}
else
{
/* Insert member before the element 'eptr'. */
/* Insert member before the element 'eptr'. */
zl
=
lpInsertString
(
zl
,(
unsigned
char
*
)
ele
,
sdslen
(
ele
),
eptr
,
LP_BEFORE
,
&
sptr
);
zl
=
lpInsertString
(
zl
,(
unsigned
char
*
)
ele
,
sdslen
(
ele
),
eptr
,
LP_BEFORE
,
&
sptr
);
/* Insert score after the member. */
/* Insert score after the member. */
zl
=
lpInsertString
(
zl
,(
unsigned
char
*
)
scorebuf
,
scorelen
,
sptr
,
LP_AFTER
,
NULL
);
if
(
score_is_long
)
zl
=
lpInsertInteger
(
zl
,
lscore
,
sptr
,
LP_AFTER
,
NULL
);
else
zl
=
lpInsertString
(
zl
,(
unsigned
char
*
)
scorebuf
,
scorelen
,
sptr
,
LP_AFTER
,
NULL
);
}
}
return
zl
;
return
zl
;
}
}
...
@@ -3964,7 +3972,7 @@ void zpopminCommand(client *c) {
...
@@ -3964,7 +3972,7 @@ void zpopminCommand(client *c) {
zpopMinMaxCommand
(
c
,
ZSET_MIN
);
zpopMinMaxCommand
(
c
,
ZSET_MIN
);
}
}
/* Z
MAX
POP key [<count>] */
/* ZPOP
MAX
key [<count>] */
void
zpopmaxCommand
(
client
*
c
)
{
void
zpopmaxCommand
(
client
*
c
)
{
zpopMinMaxCommand
(
c
,
ZSET_MAX
);
zpopMinMaxCommand
(
c
,
ZSET_MAX
);
}
}
...
@@ -4351,12 +4359,12 @@ void zmpopGenericCommand(client *c, int numkeys_idx, int is_block) {
...
@@ -4351,12 +4359,12 @@ void zmpopGenericCommand(client *c, int numkeys_idx, int is_block) {
}
}
}
}
/* ZMPOP numkeys [<key> ...] MIN|MAX [COUNT count] */
/* ZMPOP numkeys
key
[<key> ...] MIN|MAX [COUNT count] */
void
zmpopCommand
(
client
*
c
)
{
void
zmpopCommand
(
client
*
c
)
{
zmpopGenericCommand
(
c
,
1
,
0
);
zmpopGenericCommand
(
c
,
1
,
0
);
}
}
/* BZMPOP timeout numkeys [<key> ...] MIN|MAX [COUNT count] */
/* BZMPOP timeout numkeys
key
[<key> ...] MIN|MAX [COUNT count] */
void
bzmpopCommand
(
client
*
c
)
{
void
bzmpopCommand
(
client
*
c
)
{
zmpopGenericCommand
(
c
,
2
,
1
);
zmpopGenericCommand
(
c
,
2
,
1
);
}
}
src/timeout.c
View file @
d375595d
...
@@ -58,7 +58,7 @@ int clientsCronHandleTimeout(client *c, mstime_t now_ms) {
...
@@ -58,7 +58,7 @@ int clientsCronHandleTimeout(client *c, mstime_t now_ms) {
if
(
server
.
maxidletime
&&
if
(
server
.
maxidletime
&&
/* This handles the idle clients connection timeout if set. */
/* This handles the idle clients connection timeout if set. */
!
(
c
->
flags
&
CLIENT_SLAVE
)
&&
/* No timeout for slaves and monitors */
!
(
c
->
flags
&
CLIENT_SLAVE
)
&&
/* No timeout for slaves and monitors */
!
(
c
->
flags
&
CLIENT_MASTER
)
&&
/* No timeout for masters */
!
mustObeyClient
(
c
)
&&
/* No timeout for masters
and AOF
*/
!
(
c
->
flags
&
CLIENT_BLOCKED
)
&&
/* No timeout for BLPOP */
!
(
c
->
flags
&
CLIENT_BLOCKED
)
&&
/* No timeout for BLPOP */
!
(
c
->
flags
&
CLIENT_PUBSUB
)
&&
/* No timeout for Pub/Sub clients */
!
(
c
->
flags
&
CLIENT_PUBSUB
)
&&
/* No timeout for Pub/Sub clients */
(
now
-
c
->
lastinteraction
>
server
.
maxidletime
))
(
now
-
c
->
lastinteraction
>
server
.
maxidletime
))
...
...
src/util.c
View file @
d375595d
...
@@ -552,6 +552,36 @@ int string2d(const char *s, size_t slen, double *dp) {
...
@@ -552,6 +552,36 @@ int string2d(const char *s, size_t slen, double *dp) {
return
1
;
return
1
;
}
}
/* Returns 1 if the double value can safely be represented in long long without
* precision loss, in which case the corresponding long long is stored in the out variable. */
int
double2ll
(
double
d
,
long
long
*
out
)
{
#if (DBL_MANT_DIG >= 52) && (DBL_MANT_DIG <= 63) && (LLONG_MAX == 0x7fffffffffffffffLL)
/* Check if the float is in a safe range to be casted into a
* long long. We are assuming that long long is 64 bit here.
* Also we are assuming that there are no implementations around where
* double has precision < 52 bit.
*
* Under this assumptions we test if a double is inside a range
* where casting to long long is safe. Then using two castings we
* make sure the decimal part is zero. If all this is true we can use
* integer without precision loss.
*
* Note that numbers above 2^52 and below 2^63 use all the fraction bits as real part,
* and the exponent bits are positive, which means the "decimal" part must be 0.
* i.e. all double values in that range are representable as a long without precision loss,
* but not all long values in that range can be represented as a double.
* we only care about the first part here. */
if
(
d
<
(
double
)(
-
LLONG_MAX
/
2
)
||
d
>
(
double
)(
LLONG_MAX
/
2
))
return
0
;
long
long
ll
=
d
;
if
(
ll
==
d
)
{
*
out
=
ll
;
return
1
;
}
#endif
return
0
;
}
/* Convert a double to a string representation. Returns the number of bytes
/* Convert a double to a string representation. Returns the number of bytes
* required. The representation should always be parsable by strtod(3).
* required. The representation should always be parsable by strtod(3).
* This function does not support human-friendly formatting like ld2string
* This function does not support human-friendly formatting like ld2string
...
@@ -572,22 +602,11 @@ int d2string(char *buf, size_t len, double value) {
...
@@ -572,22 +602,11 @@ int d2string(char *buf, size_t len, double value) {
else
else
len
=
snprintf
(
buf
,
len
,
"0"
);
len
=
snprintf
(
buf
,
len
,
"0"
);
}
else
{
}
else
{
#if (DBL_MANT_DIG >= 52) && (LLONG_MAX == 0x7fffffffffffffffLL)
long
long
lvalue
;
/* Check if the float is in a safe range to be casted into a
/* Integer printing function is much faster, check if we can safely use it. */
* long long. We are assuming that long long is 64 bit here.
if
(
double2ll
(
value
,
&
lvalue
))
* Also we are assuming that there are no implementations around where
len
=
ll2string
(
buf
,
len
,
lvalue
);
* double has precision < 52 bit.
*
* Under this assumptions we test if a double is inside an interval
* where casting to long long is safe. Then using two castings we
* make sure the decimal part is zero. If all this is true we use
* integer printing function that is much faster. */
double
min
=
-
4503599627370495
;
/* (2^52)-1 */
double
max
=
4503599627370496
;
/* -(2^52) */
if
(
value
>
min
&&
value
<
max
&&
value
==
((
double
)((
long
long
)
value
)))
len
=
ll2string
(
buf
,
len
,(
long
long
)
value
);
else
else
#endif
len
=
snprintf
(
buf
,
len
,
"%.17g"
,
value
);
len
=
snprintf
(
buf
,
len
,
"%.17g"
,
value
);
}
}
...
...
src/util.h
View file @
d375595d
...
@@ -75,6 +75,7 @@ int string2d(const char *s, size_t slen, double *dp);
...
@@ -75,6 +75,7 @@ int string2d(const char *s, size_t slen, double *dp);
int
trimDoubleString
(
char
*
buf
,
size_t
len
);
int
trimDoubleString
(
char
*
buf
,
size_t
len
);
int
d2string
(
char
*
buf
,
size_t
len
,
double
value
);
int
d2string
(
char
*
buf
,
size_t
len
,
double
value
);
int
ld2string
(
char
*
buf
,
size_t
len
,
long
double
value
,
ld2string_mode
mode
);
int
ld2string
(
char
*
buf
,
size_t
len
,
long
double
value
,
ld2string_mode
mode
);
int
double2ll
(
double
d
,
long
long
*
out
);
int
yesnotoi
(
char
*
s
);
int
yesnotoi
(
char
*
s
);
sds
getAbsolutePath
(
char
*
filename
);
sds
getAbsolutePath
(
char
*
filename
);
long
getTimeZone
(
void
);
long
getTimeZone
(
void
);
...
...
src/version.h
View file @
d375595d
#define REDIS_VERSION "
6.9.242
"
#define REDIS_VERSION "
7.0.0
"
#define REDIS_VERSION_NUM 0x000
609f2
#define REDIS_VERSION_NUM 0x000
70000
tests/assets/default.conf
View file @
d375595d
...
@@ -30,3 +30,5 @@ activerehashing yes
...
@@ -30,3 +30,5 @@ activerehashing yes
enable
-
protected
-
configs
yes
enable
-
protected
-
configs
yes
enable
-
debug
-
command
yes
enable
-
debug
-
command
yes
enable
-
module
-
command
yes
enable
-
module
-
command
yes
propagation
-
error
-
behavior
panic
\ No newline at end of file
tests/integration/aof-multi-part.tcl
View file @
d375595d
...
@@ -1104,7 +1104,11 @@ tags {"external:skip"} {
...
@@ -1104,7 +1104,11 @@ tags {"external:skip"} {
# Set a key so that AOFRW can be delayed
# Set a key so that AOFRW can be delayed
r set k v
r set k v
# Let AOFRW fail two times, this will trigger AOFRW limit
# Let AOFRW fail 3 times, this will trigger AOFRW limit
r bgrewriteaof
catch
{
exec kill -9
[
get_child_pid 0
]}
waitForBgrewriteaof r
r bgrewriteaof
r bgrewriteaof
catch
{
exec kill -9
[
get_child_pid 0
]}
catch
{
exec kill -9
[
get_child_pid 0
]}
waitForBgrewriteaof r
waitForBgrewriteaof r
...
@@ -1118,6 +1122,7 @@ tags {"external:skip"} {
...
@@ -1118,6 +1122,7 @@ tags {"external:skip"} {
{
file appendonly.aof.6.incr.aof seq 6 type i
}
{
file appendonly.aof.6.incr.aof seq 6 type i
}
{
file appendonly.aof.7.incr.aof seq 7 type i
}
{
file appendonly.aof.7.incr.aof seq 7 type i
}
{
file appendonly.aof.8.incr.aof seq 8 type i
}
{
file appendonly.aof.8.incr.aof seq 8 type i
}
{
file appendonly.aof.9.incr.aof seq 9 type i
}
}
}
# Write 1KB data to trigger AOFRW
# Write 1KB data to trigger AOFRW
...
@@ -1137,6 +1142,7 @@ tags {"external:skip"} {
...
@@ -1137,6 +1142,7 @@ tags {"external:skip"} {
{
file appendonly.aof.6.incr.aof seq 6 type i
}
{
file appendonly.aof.6.incr.aof seq 6 type i
}
{
file appendonly.aof.7.incr.aof seq 7 type i
}
{
file appendonly.aof.7.incr.aof seq 7 type i
}
{
file appendonly.aof.8.incr.aof seq 8 type i
}
{
file appendonly.aof.8.incr.aof seq 8 type i
}
{
file appendonly.aof.9.incr.aof seq 9 type i
}
}
}
# Turn off auto rewrite
# Turn off auto rewrite
...
@@ -1154,11 +1160,11 @@ tags {"external:skip"} {
...
@@ -1154,11 +1160,11 @@ tags {"external:skip"} {
waitForBgrewriteaof r
waitForBgrewriteaof r
# Can create New INCR AOF
# Can create New INCR AOF
assert_equal 1
[
check_file_exist $aof_dirpath
"
${aof_basename}
.
9
${::incr_aof_sufix}${::aof_format_suffix}
"
]
assert_equal 1
[
check_file_exist $aof_dirpath
"
${aof_basename}
.
10
${::incr_aof_sufix}${::aof_format_suffix}
"
]
assert_aof_manifest_content $aof_manifest_file
{
assert_aof_manifest_content $aof_manifest_file
{
{
file appendonly.aof.11.base.rdb seq 11 type b
}
{
file appendonly.aof.11.base.rdb seq 11 type b
}
{
file appendonly.aof.
9
.incr.aof seq
9
type i
}
{
file appendonly.aof.
10
.incr.aof seq
10
type i
}
}
}
set d1
[
r debug digest
]
set d1
[
r debug digest
]
...
@@ -1166,5 +1172,161 @@ tags {"external:skip"} {
...
@@ -1166,5 +1172,161 @@ tags {"external:skip"} {
set d2
[
r debug digest
]
set d2
[
r debug digest
]
assert
{
$d1
eq $d2
}
assert
{
$d1
eq $d2
}
}
}
start_server
{
overrides
{
aof-use-rdb-preamble
{
yes
}
appendonly
{
no
}}}
{
set dir
[
get_redis_dir
]
set aof_basename
"appendonly.aof"
set aof_dirname
"appendonlydir"
set aof_dirpath
"
$dir
/
$aof
_dirname"
set aof_manifest_name
"
$aof
_basename
$::manifest
_suffix"
set aof_manifest_file
"
$dir
/
$aof
_dirname/
$aof
_manifest_name"
set master
[
srv 0 client
]
set master_host
[
srv 0 host
]
set master_port
[
srv 0 port
]
test
"AOF will open a temporary INCR AOF to accumulate data until the first AOFRW success when AOF is dynamically enabled"
{
r config set save
""
# Increase AOFRW execution time to give us enough time to kill it
r config set rdb-key-save-delay 10000000
# Start write load
set load_handle0
[
start_write_load $master_host $master_port 10
]
wait_for_condition 50 100
{
[
r dbsize
]
> 0
}
else
{
fail
"No write load detected."
}
# Enable AOF will trigger an initialized AOFRW
r config set appendonly yes
# Let AOFRW fail
assert_equal 1
[
s aof_rewrite_in_progress
]
set pid1
[
get_child_pid 0
]
catch
{
exec kill -9 $pid1
}
# Wait for AOFRW to exit and delete temp incr aof
wait_for_condition 1000 100
{
[
count_log_message 0
"Removing the temp incr aof file"
]
== 1
}
else
{
fail
"temp aof did not delete"
}
# Make sure manifest file is not created
assert_equal 0
[
check_file_exist $aof_dirpath $aof_manifest_name
]
# Make sure BASE AOF is not created
assert_equal 0
[
check_file_exist $aof_dirpath
"
${aof_basename}
.1
${::base_aof_sufix}${::rdb_format_suffix}
"
]
# Make sure the next AOFRW has started
wait_for_condition 1000 50
{
[
s aof_rewrite_in_progress
]
== 1
}
else
{
fail
"aof rewrite did not scheduled"
}
# Do a successful AOFRW
set total_forks
[
s total_forks
]
r config set rdb-key-save-delay 0
catch
{
exec kill -9
[
get_child_pid 0
]}
# Make sure the next AOFRW has started
wait_for_condition 1000 10
{
[
s total_forks
]
==
[
expr $total_forks + 1
]
}
else
{
fail
"aof rewrite did not scheduled"
}
waitForBgrewriteaof r
assert_equal 2
[
count_log_message 0
"Removing the temp incr aof file"
]
# BASE and INCR AOF are successfully created
assert_aof_manifest_content $aof_manifest_file
{
{
file appendonly.aof.1.base.rdb seq 1 type b
}
{
file appendonly.aof.1.incr.aof seq 1 type i
}
}
stop_write_load $load_handle0
wait_load_handlers_disconnected
set d1
[
r debug digest
]
r debug loadaof
set d2
[
r debug digest
]
assert
{
$d1
eq $d2
}
# Dynamic disable AOF again
r config set appendonly no
# Disabling AOF does not delete previous AOF files
r debug loadaof
set d2
[
r debug digest
]
assert
{
$d1
eq $d2
}
assert_equal 0
[
s rdb_changes_since_last_save
]
r config set rdb-key-save-delay 10000000
set load_handle0
[
start_write_load $master_host $master_port 10
]
wait_for_condition 50 100
{
[
s rdb_changes_since_last_save
]
> 0
}
else
{
fail
"No write load detected."
}
# Re-enable AOF
r config set appendonly yes
# Let AOFRW fail
assert_equal 1
[
s aof_rewrite_in_progress
]
set pid1
[
get_child_pid 0
]
catch
{
exec kill -9 $pid1
}
# Wait for AOFRW to exit and delete temp incr aof
wait_for_condition 1000 100
{
[
count_log_message 0
"Removing the temp incr aof file"
]
== 3
}
else
{
fail
"temp aof did not delete 3 times"
}
# Make sure no new incr AOF was created
assert_aof_manifest_content $aof_manifest_file
{
{
file appendonly.aof.1.base.rdb seq 1 type b
}
{
file appendonly.aof.1.incr.aof seq 1 type i
}
}
# Make sure the next AOFRW has started
wait_for_condition 1000 50
{
[
s aof_rewrite_in_progress
]
== 1
}
else
{
fail
"aof rewrite did not scheduled"
}
# Do a successful AOFRW
set total_forks
[
s total_forks
]
r config set rdb-key-save-delay 0
catch
{
exec kill -9
[
get_child_pid 0
]}
wait_for_condition 1000 10
{
[
s total_forks
]
==
[
expr $total_forks + 1
]
}
else
{
fail
"aof rewrite did not scheduled"
}
waitForBgrewriteaof r
assert_equal 4
[
count_log_message 0
"Removing the temp incr aof file"
]
# New BASE and INCR AOF are successfully created
assert_aof_manifest_content $aof_manifest_file
{
{
file appendonly.aof.2.base.rdb seq 2 type b
}
{
file appendonly.aof.2.incr.aof seq 2 type i
}
}
stop_write_load $load_handle0
wait_load_handlers_disconnected
set d1
[
r debug digest
]
r debug loadaof
set d2
[
r debug digest
]
assert
{
$d1
eq $d2
}
}
}
}
}
}
}
tests/integration/corrupt-dump.tcl
View file @
d375595d
...
@@ -632,7 +632,6 @@ test {corrupt payload: fuzzer findings - stream PEL without consumer} {
...
@@ -632,7 +632,6 @@ test {corrupt payload: fuzzer findings - stream PEL without consumer} {
r debug set-skip-checksum-validation 1
r debug set-skip-checksum-validation 1
catch
{
r restore _stream 0
"
\x0F\x01\x10\x00\x00\x01\x7B\x08\xF0\xB2\x34\x00\x00\x00\x00\x00\x00\x00\x00\xC3\x3B\x40\x42\x19\x42\x00\x00\x00\x18\x00\x02\x01\x01\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x00\x20\x10\x00\x00\x20\x01\x00\x01\x20\x03\x02\x05\x01\x03\x20\x05\x40\x00\x04\x82\x5F\x31\x03\x05\x60\x19\x80\x32\x02\x05\x01\xFF\x02\x81\x00\x00\x01\x7B\x08\xF0\xB2\x34\x02\x01\x07\x6D\x79\x67\x72\x6F\x75\x70\x81\x00\x00\x01\x7B\x08\xF0\xB2\x34\x01\x01\x00\x00\x01\x7B\x08\xF0\xB2\x34\x00\x00\x00\x00\x00\x00\x00\x01\x35\xB2\xF0\x08\x7B\x01\x00\x00\x01\x01\x13\x41\x6C\x69\x63\x65\x35\xB2\xF0\x08\x7B\x01\x00\x00\x01\x00\x00\x01\x7B\x08\xF0\xB2\x34\x00\x00\x00\x00\x00\x00\x00\x01\x09\x00\x28\x2F\xE0\xC5\x04\xBB\xA7\x31
"
}
err
catch
{
r restore _stream 0
"
\x0F\x01\x10\x00\x00\x01\x7B\x08\xF0\xB2\x34\x00\x00\x00\x00\x00\x00\x00\x00\xC3\x3B\x40\x42\x19\x42\x00\x00\x00\x18\x00\x02\x01\x01\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x00\x20\x10\x00\x00\x20\x01\x00\x01\x20\x03\x02\x05\x01\x03\x20\x05\x40\x00\x04\x82\x5F\x31\x03\x05\x60\x19\x80\x32\x02\x05\x01\xFF\x02\x81\x00\x00\x01\x7B\x08\xF0\xB2\x34\x02\x01\x07\x6D\x79\x67\x72\x6F\x75\x70\x81\x00\x00\x01\x7B\x08\xF0\xB2\x34\x01\x01\x00\x00\x01\x7B\x08\xF0\xB2\x34\x00\x00\x00\x00\x00\x00\x00\x01\x35\xB2\xF0\x08\x7B\x01\x00\x00\x01\x01\x13\x41\x6C\x69\x63\x65\x35\xB2\xF0\x08\x7B\x01\x00\x00\x01\x00\x00\x01\x7B\x08\xF0\xB2\x34\x00\x00\x00\x00\x00\x00\x00\x01\x09\x00\x28\x2F\xE0\xC5\x04\xBB\xA7\x31
"
}
err
assert_match
"*Bad data format*"
$err
assert_match
"*Bad data format*"
$err
#catch
{
r XINFO STREAM _stream FULL
}
r ping
r ping
}
}
}
}
...
@@ -674,7 +673,6 @@ test {corrupt payload: fuzzer findings - stream with non-integer entry id} {
...
@@ -674,7 +673,6 @@ test {corrupt payload: fuzzer findings - stream with non-integer entry id} {
r config set sanitize-dump-payload yes
r config set sanitize-dump-payload yes
r debug set-skip-checksum-validation 1
r debug set-skip-checksum-validation 1
catch
{
r restore _streambig 0
"
\x0F\x03\x10\x00\x00\x01\x7B\x13\x34\xC3\xB2\x00\x00\x00\x00\x00\x00\x00\x00\xC3\x40\x4F\x40\x5C\x18\x5C\x00\x00\x00\x24\x00\x05\x01\x00\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x40\x10\x00\x80\x20\x01\x00\x01\x20\x03\x00\x05\x20\x1C\x40\x09\x05\x01\x01\x82\x5F\x31\x03\x80\x0D\x00\x02\x20\x0D\x00\x02\xA0\x19\x00\x03\x20\x0B\x02\x82\x5F\x33\xA0\x19\x00\x04\x20\x0D\x00\x04\x20\x19\x00\xFF\x10\x00\x00\x01\x7B\x13\x34\xC3\xB2\x00\x00\x00\x00\x00\x00\x00\x05\xC3\x40\x56\x40\x61\x18\x61\x00\x00\x00\x24\x00\x05\x01\x00\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x40\x10\x00\x00\x20\x01\x06\x01\x01\x82\x5F\x35\x03\x05\x20\x1E\x40\x0B\x03\x01\x01\x06\x01\x40\x0B\x03\x01\x01\xDF\xFB\x20\x05\x02\x82\x5F\x37\x60\x1A\x20\x0E\x00\xFC\x20\x05\x00\x08\xC0\x1B\x00\xFD\x20\x0C\x02\x82\x5F\x39\x20\x1B\x00\xFF\x10\x00\x00\x01\x7B\x13\x34\xC3\xB3\x00\x00\x00\x00\x00\x00\x00\x03\xC3\x3D\x40\x4A\x18\x4A\x00\x00\x00\x15\x00\x02\x01\x00\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x40\x10\x00\x00\x20\x01\x40\x00\x00\x05\x60\x07\x02\xDF\xFD\x02\xC0\x23\x09\x01\x01\x86\x75\x6E\x69\x71\x75\x65\x07\xA0\x2D\x02\x08\x01\xFF\x0C\x81\x00\x00\x01\x7B\x13\x34\xC3\xB4\x00\x00\x09\x00\x9D\xBD\xD5\xB9\x33\xC4\xC5\xFF
"
}
err
catch
{
r restore _streambig 0
"
\x0F\x03\x10\x00\x00\x01\x7B\x13\x34\xC3\xB2\x00\x00\x00\x00\x00\x00\x00\x00\xC3\x40\x4F\x40\x5C\x18\x5C\x00\x00\x00\x24\x00\x05\x01\x00\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x40\x10\x00\x80\x20\x01\x00\x01\x20\x03\x00\x05\x20\x1C\x40\x09\x05\x01\x01\x82\x5F\x31\x03\x80\x0D\x00\x02\x20\x0D\x00\x02\xA0\x19\x00\x03\x20\x0B\x02\x82\x5F\x33\xA0\x19\x00\x04\x20\x0D\x00\x04\x20\x19\x00\xFF\x10\x00\x00\x01\x7B\x13\x34\xC3\xB2\x00\x00\x00\x00\x00\x00\x00\x05\xC3\x40\x56\x40\x61\x18\x61\x00\x00\x00\x24\x00\x05\x01\x00\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x40\x10\x00\x00\x20\x01\x06\x01\x01\x82\x5F\x35\x03\x05\x20\x1E\x40\x0B\x03\x01\x01\x06\x01\x40\x0B\x03\x01\x01\xDF\xFB\x20\x05\x02\x82\x5F\x37\x60\x1A\x20\x0E\x00\xFC\x20\x05\x00\x08\xC0\x1B\x00\xFD\x20\x0C\x02\x82\x5F\x39\x20\x1B\x00\xFF\x10\x00\x00\x01\x7B\x13\x34\xC3\xB3\x00\x00\x00\x00\x00\x00\x00\x03\xC3\x3D\x40\x4A\x18\x4A\x00\x00\x00\x15\x00\x02\x01\x00\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x40\x10\x00\x00\x20\x01\x40\x00\x00\x05\x60\x07\x02\xDF\xFD\x02\xC0\x23\x09\x01\x01\x86\x75\x6E\x69\x71\x75\x65\x07\xA0\x2D\x02\x08\x01\xFF\x0C\x81\x00\x00\x01\x7B\x13\x34\xC3\xB4\x00\x00\x09\x00\x9D\xBD\xD5\xB9\x33\xC4\xC5\xFF
"
}
err
#catch
{
r XINFO STREAM _streambig FULL
}
assert_match
"*Bad data format*"
$err
assert_match
"*Bad data format*"
$err
r ping
r ping
}
}
...
@@ -782,5 +780,15 @@ test {corrupt payload: fuzzer findings - zset zslInsert with a NAN score} {
...
@@ -782,5 +780,15 @@ test {corrupt payload: fuzzer findings - zset zslInsert with a NAN score} {
}
}
}
}
test
{
corrupt payload: fuzzer findings - streamLastValidID panic
}
{
start_server
[
list overrides
[
list loglevel verbose use-exit-on-panic yes crash-memcheck-enabled no
]
]
{
r config set sanitize-dump-payload yes
r debug set-skip-checksum-validation 1
catch
{
r restore _streambig 0
"
\x13\xC0\x10\x00\x00\x01\x80\x20\x48\xA0\x33\x00\x00\x00\x00\x00\x00\x00\x00\xC3\x40\x4F\x40\x5C\x18\x5C\x00\x00\x00\x24\x00\x05\x01\x00\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x40\x10\x00\x00\x20\x01\x00\x01\x20\x03\x00\x05\x20\x1C\x40\x09\x05\x01\x01\x82\x5F\x31\x03\x80\x0D\x00\x02\x20\x0D\x00\x02\xA0\x19\x00\x03\x20\x0B\x02\x82\x5F\x33\x60\x19\x40\x2F\x02\x01\x01\x04\x20\x19\x00\xFF\x10\x00\x00\x01\x80\x20\x48\xA0\x34\x00\x00\x00\x00\x00\x00\x00\x01\xC3\x40\x51\x40\x5E\x18\x5E\x00\x00\x00\x24\x00\x05\x01\x00\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x40\x10\x00\x00\x20\x01\x06\x01\x01\x82\x5F\x35\x03\x05\x20\x1E\x40\x0B\x03\x01\x01\x06\x01\x80\x0B\x00\x02\x20\x0B\x02\x82\x5F\x37\xA0\x19\x00\x03\x20\x0D\x00\x08\xA0\x19\x00\x04\x20\x0B\x02\x82\x5F\x39\x20\x19\x00\xFF\x10\x00\x00\x01\x80\x20\x48\xA0\x34\x00\x00\x00\x00\x00\x00\x00\x06\xC3\x3D\x40\x4A\x18\x4A\x00\x00\x00\x15\x00\x02\x01\x00\x01\x02\x01\x84\x69\x74\x65\x6D\x05\x85\x76\x61\x6C\x75\x65\x06\x40\x10\x00\x00\x20\x01\x40\x00\x00\x05\x60\x07\x02\xDF\xFA\x02\xC0\x23\x09\x01\x01\x86\x75\x6E\x69\x71\x75\x65\x07\xA0\x2D\x02\x08\x01\xFF\x0C\x81\x00\x00\x01\x80\x20\x48\xA0\x35\x00\x81\x00\x00\x01\x80\x20\x48\xA0\x33\x00\x00\x00\x0C\x00\x0A\x00\x34\x8B\x0E\x5B\x42\xCD\xD6\x08
"
}
err
assert_match
"*Bad data format*"
$err
r ping
}
}
}
;
# tags
}
;
# tags
tests/integration/replication-4.tcl
View file @
d375595d
...
@@ -195,3 +195,48 @@ start_server {tags {"repl external:skip"}} {
...
@@ -195,3 +195,48 @@ start_server {tags {"repl external:skip"}} {
}
}
}
}
}
}
start_server
{
tags
{
"repl external:skip"
}}
{
start_server
{}
{
set master
[
srv -1 client
]
set master_host
[
srv -1 host
]
set master_port
[
srv -1 port
]
set replica
[
srv 0 client
]
test
{
First server should have role slave after SLAVEOF
}
{
$replica slaveof $master_host $master_port
wait_for_condition 50 100
{
[
s 0 role
]
eq
{
slave
}
}
else
{
fail
"Replication not started."
}
wait_for_sync $replica
}
test
{
Data divergence can happen under default conditions
}
{
$replica config set propagation-error-behavior ignore
$master debug replicate fake-command-1
# Wait for replication to normalize
$master set foo bar2
$master wait 1 2000
# Make sure we triggered the error, by finding the critical
# message and the fake command.
assert_equal
[
count_log_message 0
"fake-command-1"
]
1
assert_equal
[
count_log_message 0
"== CRITICAL =="
]
1
}
test
{
Data divergence is allowed on writable replicas
}
{
$replica config set replica-read-only no
$replica set number2 foo
$master incrby number2 1
$master wait 1 2000
assert_equal
[
$master
get number2
]
1
assert_equal
[
$replica
get number2
]
foo
assert_equal
[
count_log_message 0
"incrby"
]
1
}
}
}
tests/modules/Makefile
View file @
d375595d
...
@@ -49,6 +49,7 @@ TEST_MODULES = \
...
@@ -49,6 +49,7 @@ TEST_MODULES = \
hash.so
\
hash.so
\
zset.so
\
zset.so
\
stream.so
\
stream.so
\
mallocsize.so
\
aclcheck.so
\
aclcheck.so
\
list.so
\
list.so
\
subcommands.so
\
subcommands.so
\
...
@@ -56,7 +57,8 @@ TEST_MODULES = \
...
@@ -56,7 +57,8 @@ TEST_MODULES = \
cmdintrospection.so
\
cmdintrospection.so
\
eventloop.so
\
eventloop.so
\
moduleconfigs.so
\
moduleconfigs.so
\
moduleconfigstwo.so
moduleconfigstwo.so
\
publish.so
.PHONY
:
all
.PHONY
:
all
...
@@ -69,7 +71,7 @@ all: $(TEST_MODULES)
...
@@ -69,7 +71,7 @@ all: $(TEST_MODULES)
$(CC)
-I
../../src
$(CFLAGS)
$(SHOBJ_CFLAGS)
-fPIC
-c
$<
-o
$@
$(CC)
-I
../../src
$(CFLAGS)
$(SHOBJ_CFLAGS)
-fPIC
-c
$<
-o
$@
%.so
:
%.xo
%.so
:
%.xo
$(LD)
-o
$@
$
<
$(SHOBJ_LDFLAGS)
$(LDFLAGS)
$(LIBS)
$(LD)
-o
$@
$
^
$(SHOBJ_LDFLAGS)
$(LDFLAGS)
$(LIBS)
.PHONY
:
clean
.PHONY
:
clean
...
...
tests/modules/aclcheck.c
View file @
d375595d
...
@@ -92,7 +92,7 @@ int rm_call_aclcheck_cmd(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModule
...
@@ -92,7 +92,7 @@ int rm_call_aclcheck_cmd(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModule
if
(
ret
!=
0
)
{
if
(
ret
!=
0
)
{
RedisModule_ReplyWithError
(
ctx
,
"DENIED CMD"
);
RedisModule_ReplyWithError
(
ctx
,
"DENIED CMD"
);
/* Add entry to ACL log */
/* Add entry to ACL log */
RedisModule_ACLAddLogEntry
(
ctx
,
user
,
argv
[
1
]);
RedisModule_ACLAddLogEntry
(
ctx
,
user
,
argv
[
1
]
,
REDISMODULE_ACL_LOG_CMD
);
return
REDISMODULE_OK
;
return
REDISMODULE_OK
;
}
}
...
...
Prev
1
2
3
4
5
6
7
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