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
61297585
Unverified
Commit
61297585
authored
Sep 27, 2019
by
Salvatore Sanfilippo
Committed by
GitHub
Sep 27, 2019
Browse files
Merge branch 'unstable' into modules_fork
parents
83e87bac
fddc4757
Changes
82
Expand all
Show whitespace changes
Inline
Side-by-side
src/cluster.c
View file @
61297585
...
...
@@ -138,6 +138,7 @@ int clusterLoadConfig(char *filename) {
/* Handle the special "vars" line. Don't pretend it is the last
* line even if it actually is when generated by Redis. */
if
(
strcasecmp
(
argv
[
0
],
"vars"
)
==
0
)
{
if
(
!
(
argc
%
2
))
goto
fmterr
;
for
(
j
=
1
;
j
<
argc
;
j
+=
2
)
{
if
(
strcasecmp
(
argv
[
j
],
"currentEpoch"
)
==
0
)
{
server
.
cluster
->
currentEpoch
=
...
...
@@ -4251,12 +4252,9 @@ NULL
}
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"nodes"
)
&&
c
->
argc
==
2
)
{
/* CLUSTER NODES */
robj
*
o
;
sds
ci
=
clusterGenNodesDescription
(
0
);
o
=
createObject
(
OBJ_STRING
,
ci
);
addReplyBulk
(
c
,
o
);
decrRefCount
(
o
);
sds
nodes
=
clusterGenNodesDescription
(
0
);
addReplyVerbatim
(
c
,
nodes
,
sdslen
(
nodes
),
"txt"
);
sdsfree
(
nodes
);
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"myid"
)
&&
c
->
argc
==
2
)
{
/* CLUSTER MYID */
addReplyBulkCBuffer
(
c
,
myself
->
name
,
CLUSTER_NAMELEN
);
...
...
@@ -4498,10 +4496,8 @@ NULL
"cluster_stats_messages_received:%lld
\r\n
"
,
tot_msg_received
);
/* Produce the reply protocol. */
addReplySds
(
c
,
sdscatprintf
(
sdsempty
(),
"$%lu
\r\n
"
,
(
unsigned
long
)
sdslen
(
info
)));
addReplySds
(
c
,
info
);
addReply
(
c
,
shared
.
crlf
);
addReplyVerbatim
(
c
,
info
,
sdslen
(
info
),
"txt"
);
sdsfree
(
info
);
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"saveconfig"
)
&&
c
->
argc
==
2
)
{
int
retval
=
clusterSaveConfig
(
1
);
...
...
@@ -4832,7 +4828,7 @@ int verifyDumpPayload(unsigned char *p, size_t len) {
* DUMP is actually not used by Redis Cluster but it is the obvious
* complement of RESTORE and can be useful for different applications. */
void
dumpCommand
(
client
*
c
)
{
robj
*
o
,
*
dumpobj
;
robj
*
o
;
rio
payload
;
/* Check if the key is here. */
...
...
@@ -4845,9 +4841,7 @@ void dumpCommand(client *c) {
createDumpPayload
(
&
payload
,
o
,
c
->
argv
[
1
]);
/* Transfer to the client */
dumpobj
=
createObject
(
OBJ_STRING
,
payload
.
io
.
buffer
.
ptr
);
addReplyBulk
(
c
,
dumpobj
);
decrRefCount
(
dumpobj
);
addReplyBulkSds
(
c
,
payload
.
io
.
buffer
.
ptr
);
return
;
}
...
...
src/config.c
View file @
61297585
...
...
@@ -672,6 +672,10 @@ void loadServerConfigFromString(char *config) {
server
.
lua_time_limit
=
strtoll
(
argv
[
1
],
NULL
,
10
);
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"lua-replicate-commands"
)
&&
argc
==
2
)
{
server
.
lua_always_replicate_commands
=
yesnotoi
(
argv
[
1
]);
if
(
server
.
lua_always_replicate_commands
==
-
1
)
{
err
=
"argument must be 'yes' or 'no'"
;
goto
loaderr
;
}
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"slowlog-log-slower-than"
)
&&
argc
==
2
)
{
...
...
@@ -686,6 +690,17 @@ void loadServerConfigFromString(char *config) {
}
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"slowlog-max-len"
)
&&
argc
==
2
)
{
server
.
slowlog_max_len
=
strtoll
(
argv
[
1
],
NULL
,
10
);
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"tracking-table-max-fill"
)
&&
argc
==
2
)
{
server
.
tracking_table_max_fill
=
strtoll
(
argv
[
1
],
NULL
,
10
);
if
(
server
.
tracking_table_max_fill
>
100
||
server
.
tracking_table_max_fill
<
0
)
{
err
=
"The tracking table fill percentage must be an "
"integer between 0 and 100"
;
goto
loaderr
;
}
}
else
if
(
!
strcasecmp
(
argv
[
0
],
"client-output-buffer-limit"
)
&&
argc
==
5
)
{
...
...
@@ -1133,6 +1148,8 @@ void configSetCommand(client *c) {
"slowlog-max-len"
,
ll
,
0
,
LONG_MAX
)
{
/* Cast to unsigned. */
server
.
slowlog_max_len
=
(
unsigned
long
)
ll
;
}
config_set_numerical_field
(
"tracking-table-max-fill"
,
server
.
tracking_table_max_fill
,
0
,
100
)
{
}
config_set_numerical_field
(
"latency-monitor-threshold"
,
server
.
latency_monitor_threshold
,
0
,
LLONG_MAX
){
}
config_set_numerical_field
(
...
...
@@ -1338,8 +1355,8 @@ void configGetCommand(client *c) {
server
.
slowlog_log_slower_than
);
config_get_numerical_field
(
"latency-monitor-threshold"
,
server
.
latency_monitor_threshold
);
config_get_numerical_field
(
"slowlog-max-len"
,
server
.
slowlog
_max_
len
);
config_get_numerical_field
(
"slowlog-max-len"
,
server
.
slowlog_max_len
);
config_get_numerical_field
(
"tracking-table-max-fill"
,
server
.
tracking_table
_max_
fill
);
config_get_numerical_field
(
"port"
,
server
.
port
);
config_get_numerical_field
(
"cluster-announce-port"
,
server
.
cluster_announce_port
);
config_get_numerical_field
(
"cluster-announce-bus-port"
,
server
.
cluster_announce_bus_port
);
...
...
@@ -1470,12 +1487,10 @@ void configGetCommand(client *c) {
matches
++
;
}
if
(
stringmatch
(
pattern
,
"notify-keyspace-events"
,
1
))
{
robj
*
flagsobj
=
createObject
(
OBJ_STRING
,
keyspaceEventsFlagsToString
(
server
.
notify_keyspace_events
));
sds
flags
=
keyspaceEventsFlagsToString
(
server
.
notify_keyspace_events
);
addReplyBulkCString
(
c
,
"notify-keyspace-events"
);
addReplyBulk
(
c
,
flagsobj
);
decrRefCount
(
flagsobj
);
addReplyBulkSds
(
c
,
flags
);
matches
++
;
}
if
(
stringmatch
(
pattern
,
"bind"
,
1
))
{
...
...
@@ -2167,6 +2182,7 @@ int rewriteConfig(char *path) {
rewriteConfigNumericalOption
(
state
,
"slowlog-log-slower-than"
,
server
.
slowlog_log_slower_than
,
CONFIG_DEFAULT_SLOWLOG_LOG_SLOWER_THAN
);
rewriteConfigNumericalOption
(
state
,
"latency-monitor-threshold"
,
server
.
latency_monitor_threshold
,
CONFIG_DEFAULT_LATENCY_MONITOR_THRESHOLD
);
rewriteConfigNumericalOption
(
state
,
"slowlog-max-len"
,
server
.
slowlog_max_len
,
CONFIG_DEFAULT_SLOWLOG_MAX_LEN
);
rewriteConfigNumericalOption
(
state
,
"tracking-table-max-fill"
,
server
.
tracking_table_max_fill
,
CONFIG_DEFAULT_TRACKING_TABLE_MAX_FILL
);
rewriteConfigNotifykeyspaceeventsOption
(
state
);
rewriteConfigNumericalOption
(
state
,
"hash-max-ziplist-entries"
,
server
.
hash_max_ziplist_entries
,
OBJ_HASH_MAX_ZIPLIST_ENTRIES
);
rewriteConfigNumericalOption
(
state
,
"hash-max-ziplist-value"
,
server
.
hash_max_ziplist_value
,
OBJ_HASH_MAX_ZIPLIST_VALUE
);
...
...
src/db.c
View file @
61297585
...
...
@@ -350,6 +350,11 @@ long long emptyDbGeneric(redisDb *dbarray, int dbnum, int flags, void(callback)(
return
-
1
;
}
/* Make sure the WATCHed keys are affected by the FLUSH* commands.
* Note that we need to call the function while the keys are still
* there. */
signalFlushedDb
(
dbnum
);
int
startdb
,
enddb
;
if
(
dbnum
==
-
1
)
{
startdb
=
0
;
...
...
@@ -409,11 +414,12 @@ long long dbTotalServerKeyCount() {
void
signalModifiedKey
(
redisDb
*
db
,
robj
*
key
)
{
touchWatchedKey
(
db
,
key
);
if
(
server
.
tracking_clients
)
trackingInvalidateKey
(
key
);
trackingInvalidateKey
(
key
);
}
void
signalFlushedDb
(
int
dbid
)
{
touchWatchedKeysOnFlush
(
dbid
);
trackingInvalidateKeysOnFlush
(
dbid
);
}
/*-----------------------------------------------------------------------------
...
...
@@ -449,7 +455,6 @@ void flushdbCommand(client *c) {
int
flags
;
if
(
getFlushCommandFlags
(
c
,
&
flags
)
==
C_ERR
)
return
;
signalFlushedDb
(
c
->
db
->
id
);
server
.
dirty
+=
emptyDb
(
c
->
db
->
id
,
flags
,
NULL
);
addReply
(
c
,
shared
.
ok
);
}
...
...
@@ -461,7 +466,6 @@ void flushallCommand(client *c) {
int
flags
;
if
(
getFlushCommandFlags
(
c
,
&
flags
)
==
C_ERR
)
return
;
signalFlushedDb
(
-
1
);
server
.
dirty
+=
emptyDb
(
-
1
,
flags
,
NULL
);
addReply
(
c
,
shared
.
ok
);
if
(
server
.
rdb_child_pid
!=
-
1
)
killRDBChild
();
...
...
src/debug.c
View file @
61297585
...
...
@@ -638,7 +638,8 @@ NULL
dictGetStats
(
buf
,
sizeof
(
buf
),
server
.
db
[
dbid
].
expires
);
stats
=
sdscat
(
stats
,
buf
);
addReplyBulkSds
(
c
,
stats
);
addReplyVerbatim
(
c
,
stats
,
sdslen
(
stats
),
"txt"
);
sdsfree
(
stats
);
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"htstats-key"
)
&&
c
->
argc
==
3
)
{
robj
*
o
;
dict
*
ht
=
NULL
;
...
...
@@ -665,7 +666,7 @@ NULL
}
else
{
char
buf
[
4096
];
dictGetStats
(
buf
,
sizeof
(
buf
),
ht
);
addReply
BulkCString
(
c
,
buf
);
addReply
Verbatim
(
c
,
buf
,
strlen
(
buf
),
"txt"
);
}
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"change-repl-id"
)
&&
c
->
argc
==
2
)
{
serverLog
(
LL_WARNING
,
"Changing replication IDs after receiving DEBUG change-repl-id"
);
...
...
@@ -1110,6 +1111,33 @@ void logRegisters(ucontext_t *uc) {
(
unsigned
long
)
uc
->
uc_mcontext
.
mc_cs
);
logStackContent
((
void
**
)
uc
->
uc_mcontext
.
mc_rsp
);
#elif defined(__aarch64__)
/* Linux AArch64 */
serverLog
(
LL_WARNING
,
"
\n
"
"X18:%016lx X19:%016lx
\n
X20:%016lx X21:%016lx
\n
"
"X22:%016lx X23:%016lx
\n
X24:%016lx X25:%016lx
\n
"
"X26:%016lx X27:%016lx
\n
X28:%016lx X29:%016lx
\n
"
"X30:%016lx
\n
"
"pc:%016lx sp:%016lx
\n
pstate:%016lx fault_address:%016lx
\n
"
,
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
18
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
19
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
20
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
21
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
22
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
23
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
24
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
25
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
26
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
27
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
28
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
29
],
(
unsigned
long
)
uc
->
uc_mcontext
.
regs
[
30
],
(
unsigned
long
)
uc
->
uc_mcontext
.
pc
,
(
unsigned
long
)
uc
->
uc_mcontext
.
sp
,
(
unsigned
long
)
uc
->
uc_mcontext
.
pstate
,
(
unsigned
long
)
uc
->
uc_mcontext
.
fault_address
);
logStackContent
((
void
**
)
uc
->
uc_mcontext
.
sp
);
#else
serverLog
(
LL_WARNING
,
" Dumping of registers not supported for this OS/arch"
);
...
...
src/expire.c
View file @
61297585
...
...
@@ -64,7 +64,7 @@ int activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {
dbSyncDelete
(
db
,
keyobj
);
notifyKeyspaceEvent
(
NOTIFY_EXPIRED
,
"expired"
,
keyobj
,
db
->
id
);
if
(
server
.
tracking_clients
)
trackingInvalidateKey
(
keyobj
);
trackingInvalidateKey
(
keyobj
);
decrRefCount
(
keyobj
);
server
.
stat_expiredkeys
++
;
return
1
;
...
...
src/hyperloglog.c
View file @
61297585
...
...
@@ -700,7 +700,7 @@ int hllSparseSet(robj *o, long index, uint8_t count) {
p
+=
oplen
;
first
+=
span
;
}
if
(
span
==
0
)
return
-
1
;
/* Invalid format. */
if
(
span
==
0
||
p
>=
end
)
return
-
1
;
/* Invalid format. */
next
=
HLL_SPARSE_IS_XZERO
(
p
)
?
p
+
2
:
p
+
1
;
if
(
next
>=
end
)
next
=
NULL
;
...
...
@@ -1242,7 +1242,7 @@ void pfcountCommand(client *c) {
if
(
o
==
NULL
)
continue
;
/* Assume empty HLL for non existing var.*/
if
(
isHLLObjectOrReply
(
c
,
o
)
!=
C_OK
)
return
;
/* Merge with this HLL with our 'max' H
H
L by setting max[i]
/* Merge with this HLL with our 'max' H
L
L by setting max[i]
* to MAX(max[i],hll[i]). */
if
(
hllMerge
(
registers
,
o
)
==
C_ERR
)
{
addReplySds
(
c
,
sdsnew
(
invalid_hll_err
));
...
...
@@ -1329,7 +1329,7 @@ void pfmergeCommand(client *c) {
hdr
=
o
->
ptr
;
if
(
hdr
->
encoding
==
HLL_DENSE
)
use_dense
=
1
;
/* Merge with this HLL with our 'max' H
H
L by setting max[i]
/* Merge with this HLL with our 'max' H
L
L by setting max[i]
* to MAX(max[i],hll[i]). */
if
(
hllMerge
(
max
,
o
)
==
C_ERR
)
{
addReplySds
(
c
,
sdsnew
(
invalid_hll_err
));
...
...
src/latency.c
View file @
61297585
...
...
@@ -599,7 +599,7 @@ NULL
event
=
dictGetKey
(
de
);
graph
=
latencyCommandGenSparkeline
(
event
,
ts
);
addReply
BulkCString
(
c
,
graph
);
addReply
Verbatim
(
c
,
graph
,
sdslen
(
graph
),
"txt"
);
sdsfree
(
graph
);
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"latest"
)
&&
c
->
argc
==
2
)
{
/* LATENCY LATEST */
...
...
@@ -608,7 +608,7 @@ NULL
/* LATENCY DOCTOR */
sds
report
=
createLatencyReport
();
addReply
BulkCBuffer
(
c
,
report
,
sdslen
(
report
));
addReply
Verbatim
(
c
,
report
,
sdslen
(
report
)
,
"txt"
);
sdsfree
(
report
);
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"reset"
)
&&
c
->
argc
>=
2
)
{
/* LATENCY RESET */
...
...
src/lolwut.c
View file @
61297585
...
...
@@ -43,7 +43,8 @@ void lolwutUnstableCommand(client *c) {
sds
rendered
=
sdsnew
(
"Redis ver. "
);
rendered
=
sdscat
(
rendered
,
REDIS_VERSION
);
rendered
=
sdscatlen
(
rendered
,
"
\n
"
,
1
);
addReplyBulkSds
(
c
,
rendered
);
addReplyVerbatim
(
c
,
rendered
,
sdslen
(
rendered
),
"txt"
);
sdsfree
(
rendered
);
}
void
lolwutCommand
(
client
*
c
)
{
...
...
src/lolwut5.c
View file @
61297585
...
...
@@ -277,6 +277,7 @@ void lolwut5Command(client *c) {
"
\n
Georg Nees - schotter, plotter on paper, 1968. Redis ver. "
);
rendered
=
sdscat
(
rendered
,
REDIS_VERSION
);
rendered
=
sdscatlen
(
rendered
,
"
\n
"
,
1
);
addReplyBulkSds
(
c
,
rendered
);
addReplyVerbatim
(
c
,
rendered
,
sdslen
(
rendered
),
"txt"
);
sdsfree
(
rendered
);
lwFreeCanvas
(
canvas
);
}
src/module.c
View file @
61297585
...
...
@@ -29,6 +29,7 @@
#include "server.h"
#include "cluster.h"
#include "rdb.h"
#include <dlfcn.h>
#include <wait.h>
...
...
@@ -52,6 +53,7 @@ struct RedisModule {
list
*
using
;
/* List of modules we use some APIs of. */
list
*
filters
;
/* List of filters the module has registered. */
int
in_call
;
/* RM_Call() nesting level */
int
options
;
/* Module options and capabilities. */
};
typedef
struct
RedisModule
RedisModule
;
...
...
@@ -780,6 +782,19 @@ long long RM_Milliseconds(void) {
return
mstime
();
}
/* Set flags defining capabilities or behavior bit flags.
*
* REDISMODULE_OPTIONS_HANDLE_IO_ERRORS:
* Generally, modules don't need to bother with this, as the process will just
* terminate if a read error happens, however, setting this flag would allow
* repl-diskless-load to work if enabled.
* The module should use RedisModule_IsIOError after reads, before using the
* data that was read, and in case of error, propagate it upwards, and also be
* able to release the partially populated value and all it's allocations. */
void
RM_SetModuleOptions
(
RedisModuleCtx
*
ctx
,
int
options
)
{
ctx
->
module
->
options
=
options
;
}
/* --------------------------------------------------------------------------
* Automatic memory management for modules
* -------------------------------------------------------------------------- */
...
...
@@ -2397,7 +2412,7 @@ int RM_HashSet(RedisModuleKey *key, int flags, ...) {
*
* REDISMODULE_HASH_EXISTS: instead of setting the value of the field
* expecting a RedisModuleString pointer to pointer, the function just
* reports if the field e
s
ists or not and expects an integer pointer
* reports if the field e
x
ists or not and expects an integer pointer
* as the second element of each pair.
*
* Example of REDISMODULE_HASH_CFIELD:
...
...
@@ -3087,6 +3102,11 @@ moduleType *RM_CreateDataType(RedisModuleCtx *ctx, const char *name, int encver,
moduleTypeMemUsageFunc
mem_usage
;
moduleTypeDigestFunc
digest
;
moduleTypeFreeFunc
free
;
struct
{
moduleTypeAuxLoadFunc
aux_load
;
moduleTypeAuxSaveFunc
aux_save
;
int
aux_save_triggers
;
}
v2
;
}
*
tms
=
(
struct
typemethods
*
)
typemethods_ptr
;
moduleType
*
mt
=
zcalloc
(
sizeof
(
*
mt
));
...
...
@@ -3098,6 +3118,11 @@ moduleType *RM_CreateDataType(RedisModuleCtx *ctx, const char *name, int encver,
mt
->
mem_usage
=
tms
->
mem_usage
;
mt
->
digest
=
tms
->
digest
;
mt
->
free
=
tms
->
free
;
if
(
tms
->
version
>=
2
)
{
mt
->
aux_load
=
tms
->
v2
.
aux_load
;
mt
->
aux_save
=
tms
->
v2
.
aux_save
;
mt
->
aux_save_triggers
=
tms
->
v2
.
aux_save_triggers
;
}
memcpy
(
mt
->
name
,
name
,
sizeof
(
mt
->
name
));
listAddNodeTail
(
ctx
->
module
->
types
,
mt
);
return
mt
;
...
...
@@ -3148,9 +3173,14 @@ void *RM_ModuleTypeGetValue(RedisModuleKey *key) {
* RDB loading and saving functions
* -------------------------------------------------------------------------- */
/* Called when there is a load error in the context of a module. This cannot
* be recovered like for the built-in types. */
/* Called when there is a load error in the context of a module. On some
* modules this cannot be recovered, but if the module declared capability
* to handle errors, we'll raise a flag rather than exiting. */
void
moduleRDBLoadError
(
RedisModuleIO
*
io
)
{
if
(
io
->
ctx
->
module
->
options
&
REDISMODULE_OPTIONS_HANDLE_IO_ERRORS
)
{
io
->
error
=
1
;
return
;
}
serverLog
(
LL_WARNING
,
"Error loading data from RDB (short read or EOF). "
"Read performed by module '%s' about type '%s' "
...
...
@@ -3161,6 +3191,33 @@ void moduleRDBLoadError(RedisModuleIO *io) {
exit
(
1
);
}
/* Returns 0 if there's at least one registered data type that did not declare
* REDISMODULE_OPTIONS_HANDLE_IO_ERRORS, in which case diskless loading should
* be avoided since it could cause data loss. */
int
moduleAllDatatypesHandleErrors
()
{
dictIterator
*
di
=
dictGetIterator
(
modules
);
dictEntry
*
de
;
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
struct
RedisModule
*
module
=
dictGetVal
(
de
);
if
(
listLength
(
module
->
types
)
&&
!
(
module
->
options
&
REDISMODULE_OPTIONS_HANDLE_IO_ERRORS
))
{
dictReleaseIterator
(
di
);
return
0
;
}
}
dictReleaseIterator
(
di
);
return
1
;
}
/* Returns true if any previous IO API failed.
* for Load* APIs the REDISMODULE_OPTIONS_HANDLE_IO_ERRORS flag must be set with
* RediModule_SetModuleOptions first. */
int
RM_IsIOError
(
RedisModuleIO
*
io
)
{
return
io
->
error
;
}
/* Save an unsigned 64 bit value into the RDB file. This function should only
* be called in the context of the rdb_save method of modules implementing new
* data types. */
...
...
@@ -3184,6 +3241,7 @@ saveerr:
* be called in the context of the rdb_load method of modules implementing
* new data types. */
uint64_t
RM_LoadUnsigned
(
RedisModuleIO
*
io
)
{
if
(
io
->
error
)
return
0
;
if
(
io
->
ver
==
2
)
{
uint64_t
opcode
=
rdbLoadLen
(
io
->
rio
,
NULL
);
if
(
opcode
!=
RDB_MODULE_OPCODE_UINT
)
goto
loaderr
;
...
...
@@ -3195,7 +3253,7 @@ uint64_t RM_LoadUnsigned(RedisModuleIO *io) {
loaderr:
moduleRDBLoadError
(
io
);
return
0
;
/* Never reached. */
return
0
;
}
/* Like RedisModule_SaveUnsigned() but for signed 64 bit values. */
...
...
@@ -3254,6 +3312,7 @@ saveerr:
/* Implements RM_LoadString() and RM_LoadStringBuffer() */
void
*
moduleLoadString
(
RedisModuleIO
*
io
,
int
plain
,
size_t
*
lenptr
)
{
if
(
io
->
error
)
return
NULL
;
if
(
io
->
ver
==
2
)
{
uint64_t
opcode
=
rdbLoadLen
(
io
->
rio
,
NULL
);
if
(
opcode
!=
RDB_MODULE_OPCODE_STRING
)
goto
loaderr
;
...
...
@@ -3265,7 +3324,7 @@ void *moduleLoadString(RedisModuleIO *io, int plain, size_t *lenptr) {
loaderr:
moduleRDBLoadError
(
io
);
return
NULL
;
/* Never reached. */
return
NULL
;
}
/* In the context of the rdb_load method of a module data type, loads a string
...
...
@@ -3286,7 +3345,7 @@ RedisModuleString *RM_LoadString(RedisModuleIO *io) {
* RedisModule_Realloc() or RedisModule_Free().
*
* The size of the string is stored at '*lenptr' if not NULL.
* The returned string is not automatically NULL termi
a
nted, it is loaded
* The returned string is not automatically NULL termin
a
ted, it is loaded
* exactly as it was stored inisde the RDB file. */
char
*
RM_LoadStringBuffer
(
RedisModuleIO
*
io
,
size_t
*
lenptr
)
{
return
moduleLoadString
(
io
,
1
,
lenptr
);
...
...
@@ -3314,6 +3373,7 @@ saveerr:
/* In the context of the rdb_save method of a module data type, loads back the
* double value saved by RedisModule_SaveDouble(). */
double
RM_LoadDouble
(
RedisModuleIO
*
io
)
{
if
(
io
->
error
)
return
0
;
if
(
io
->
ver
==
2
)
{
uint64_t
opcode
=
rdbLoadLen
(
io
->
rio
,
NULL
);
if
(
opcode
!=
RDB_MODULE_OPCODE_DOUBLE
)
goto
loaderr
;
...
...
@@ -3325,7 +3385,7 @@ double RM_LoadDouble(RedisModuleIO *io) {
loaderr:
moduleRDBLoadError
(
io
);
return
0
;
/* Never reached. */
return
0
;
}
/* In the context of the rdb_save method of a module data type, saves a float
...
...
@@ -3350,6 +3410,7 @@ saveerr:
/* In the context of the rdb_save method of a module data type, loads back the
* float value saved by RedisModule_SaveFloat(). */
float
RM_LoadFloat
(
RedisModuleIO
*
io
)
{
if
(
io
->
error
)
return
0
;
if
(
io
->
ver
==
2
)
{
uint64_t
opcode
=
rdbLoadLen
(
io
->
rio
,
NULL
);
if
(
opcode
!=
RDB_MODULE_OPCODE_FLOAT
)
goto
loaderr
;
...
...
@@ -3361,7 +3422,37 @@ float RM_LoadFloat(RedisModuleIO *io) {
loaderr:
moduleRDBLoadError
(
io
);
return
0
;
/* Never reached. */
return
0
;
}
/* Iterate over modules, and trigger rdb aux saving for the ones modules types
* who asked for it. */
ssize_t
rdbSaveModulesAux
(
rio
*
rdb
,
int
when
)
{
size_t
total_written
=
0
;
dictIterator
*
di
=
dictGetIterator
(
modules
);
dictEntry
*
de
;
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
struct
RedisModule
*
module
=
dictGetVal
(
de
);
listIter
li
;
listNode
*
ln
;
listRewind
(
module
->
types
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
moduleType
*
mt
=
ln
->
value
;
if
(
!
mt
->
aux_save
||
!
(
mt
->
aux_save_triggers
&
when
))
continue
;
ssize_t
ret
=
rdbSaveSingleModuleAux
(
rdb
,
when
,
mt
);
if
(
ret
==-
1
)
{
dictReleaseIterator
(
di
);
return
-
1
;
}
total_written
+=
ret
;
}
}
dictReleaseIterator
(
di
);
return
total_written
;
}
/* --------------------------------------------------------------------------
...
...
@@ -3524,7 +3615,7 @@ void RM_LogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_li
if
(
level
<
server
.
verbosity
)
return
;
name_len
=
snprintf
(
msg
,
sizeof
(
msg
),
"<%s> "
,
module
->
name
);
name_len
=
snprintf
(
msg
,
sizeof
(
msg
),
"<%s> "
,
module
?
module
->
name
:
"module"
);
vsnprintf
(
msg
+
name_len
,
sizeof
(
msg
)
-
name_len
,
fmt
,
ap
);
serverLogRaw
(
level
,
msg
);
}
...
...
@@ -3542,13 +3633,15 @@ void RM_LogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_li
* There is a fixed limit to the length of the log line this function is able
* to emit, this limit is not specified but is guaranteed to be more than
* a few lines of text.
*
* The ctx argument may be NULL if cannot be provided in the context of the
* caller for instance threads or callbacks, in which case a generic "module"
* will be used instead of the module name.
*/
void
RM_Log
(
RedisModuleCtx
*
ctx
,
const
char
*
levelstr
,
const
char
*
fmt
,
...)
{
if
(
!
ctx
->
module
)
return
;
/* Can only log if module is initialized */
va_list
ap
;
va_start
(
ap
,
fmt
);
RM_LogRaw
(
ctx
->
module
,
levelstr
,
fmt
,
ap
);
RM_LogRaw
(
ctx
?
ctx
->
module
:
NULL
,
levelstr
,
fmt
,
ap
);
va_end
(
ap
);
}
...
...
@@ -3564,6 +3657,15 @@ void RM_LogIOError(RedisModuleIO *io, const char *levelstr, const char *fmt, ...
va_end
(
ap
);
}
/* Redis-like assert function.
*
* A failed assertion will shut down the server and produce logging information
* that looks identical to information generated by Redis itself.
*/
void
RM__Assert
(
const
char
*
estr
,
const
char
*
file
,
int
line
)
{
_serverAssert
(
estr
,
file
,
line
);
}
/* --------------------------------------------------------------------------
* Blocking clients from modules
* -------------------------------------------------------------------------- */
...
...
@@ -5362,6 +5464,62 @@ void addReplyLoadedModules(client *c) {
dictReleaseIterator
(
di
);
}
/* Helper for genModulesInfoString(): given a list of modules, return
* am SDS string in the form "[modulename|modulename2|...]" */
sds
genModulesInfoStringRenderModulesList
(
list
*
l
)
{
listIter
li
;
listNode
*
ln
;
listRewind
(
l
,
&
li
);
sds
output
=
sdsnew
(
"["
);
while
((
ln
=
listNext
(
&
li
)))
{
RedisModule
*
module
=
ln
->
value
;
output
=
sdscat
(
output
,
module
->
name
);
}
output
=
sdstrim
(
output
,
"|"
);
output
=
sdscat
(
output
,
"]"
);
return
output
;
}
/* Helper for genModulesInfoString(): render module options as an SDS string. */
sds
genModulesInfoStringRenderModuleOptions
(
struct
RedisModule
*
module
)
{
sds
output
=
sdsnew
(
"["
);
if
(
module
->
options
&
REDISMODULE_OPTIONS_HANDLE_IO_ERRORS
)
output
=
sdscat
(
output
,
"handle-io-errors|"
);
output
=
sdstrim
(
output
,
"|"
);
output
=
sdscat
(
output
,
"]"
);
return
output
;
}
/* Helper function for the INFO command: adds loaded modules as to info's
* output.
*
* After the call, the passed sds info string is no longer valid and all the
* references must be substituted with the new pointer returned by the call. */
sds
genModulesInfoString
(
sds
info
)
{
dictIterator
*
di
=
dictGetIterator
(
modules
);
dictEntry
*
de
;
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
sds
name
=
dictGetKey
(
de
);
struct
RedisModule
*
module
=
dictGetVal
(
de
);
sds
usedby
=
genModulesInfoStringRenderModulesList
(
module
->
usedby
);
sds
using
=
genModulesInfoStringRenderModulesList
(
module
->
using
);
sds
options
=
genModulesInfoStringRenderModuleOptions
(
module
);
info
=
sdscatprintf
(
info
,
"module:name=%s,ver=%d,api=%d,filters=%d,"
"usedby=%s,using=%s,options=%s
\r\n
"
,
name
,
module
->
ver
,
module
->
apiver
,
(
int
)
listLength
(
module
->
filters
),
usedby
,
using
,
options
);
sdsfree
(
usedby
);
sdsfree
(
using
);
sdsfree
(
options
);
}
dictReleaseIterator
(
di
);
return
info
;
}
/* Redis MODULE command.
*
* MODULE LOAD <path> [args...] */
...
...
@@ -5447,6 +5605,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API
(
ReplySetArrayLength
);
REGISTER_API
(
ReplyWithString
);
REGISTER_API
(
ReplyWithStringBuffer
);
REGISTER_API
(
ReplyWithCString
);
REGISTER_API
(
ReplyWithNull
);
REGISTER_API
(
ReplyWithCallReply
);
REGISTER_API
(
ReplyWithDouble
);
...
...
@@ -5509,6 +5668,8 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API
(
ModuleTypeSetValue
);
REGISTER_API
(
ModuleTypeGetType
);
REGISTER_API
(
ModuleTypeGetValue
);
REGISTER_API
(
IsIOError
);
REGISTER_API
(
SetModuleOptions
);
REGISTER_API
(
SaveUnsigned
);
REGISTER_API
(
LoadUnsigned
);
REGISTER_API
(
SaveSigned
);
...
...
@@ -5524,6 +5685,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API
(
EmitAOF
);
REGISTER_API
(
Log
);
REGISTER_API
(
LogIOError
);
REGISTER_API
(
_Assert
);
REGISTER_API
(
StringAppendBuffer
);
REGISTER_API
(
RetainString
);
REGISTER_API
(
StringCompare
);
...
...
src/multi.c
View file @
61297585
...
...
@@ -175,7 +175,19 @@ void execCommand(client *c) {
must_propagate
=
1
;
}
int
acl_retval
=
ACLCheckCommandPerm
(
c
);
if
(
acl_retval
!=
ACL_OK
)
{
addReplyErrorFormat
(
c
,
"-NOPERM ACLs rules changed between the moment the "
"transaction was accumulated and the EXEC call. "
"This command is no longer allowed for the "
"following reason: %s"
,
(
acl_retval
==
ACL_DENIED_CMD
)
?
"no permission to execute the command or subcommand"
:
"no permission to touch the specified keys"
);
}
else
{
call
(
c
,
server
.
loading
?
CMD_CALL_NONE
:
CMD_CALL_FULL
);
}
/* Commands may alter argc/argv, restore mstate. */
c
->
mstate
.
commands
[
j
].
argc
=
c
->
argc
;
...
...
src/networking.c
View file @
61297585
...
...
@@ -1990,7 +1990,7 @@ NULL
return
;
}
sds
o
=
getAllClientsInfoString
(
type
);
addReply
BulkCBuffer
(
c
,
o
,
sdslen
(
o
));
addReply
Verbatim
(
c
,
o
,
sdslen
(
o
)
,
"txt"
);
sdsfree
(
o
);
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"reply"
)
&&
c
->
argc
==
3
)
{
/* CLIENT REPLY ON|OFF|SKIP */
...
...
@@ -2468,17 +2468,27 @@ void flushSlavesOutputBuffers(void) {
listRewind
(
server
.
slaves
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
client
*
slave
=
listNodeValue
(
ln
);
int
events
;
/* Note that the following will not flush output buffers of slaves
* in STATE_ONLINE but having put_online_on_ack set to true: in this
* case the writable event is never installed, since the purpose
* of put_online_on_ack is to postpone the moment it is installed.
* This is what we want since slaves in this state should not receive
* writes before the first ACK. */
events
=
aeGetFileEvents
(
server
.
el
,
slave
->
fd
);
if
(
events
&
AE_WRITABLE
&&
slave
->
replstate
==
SLAVE_STATE_ONLINE
&&
int
events
=
aeGetFileEvents
(
server
.
el
,
slave
->
fd
);
int
can_receive_writes
=
(
events
&
AE_WRITABLE
)
||
(
slave
->
flags
&
CLIENT_PENDING_WRITE
);
/* We don't want to send the pending data to the replica in a few
* cases:
*
* 1. For some reason there is neither the write handler installed
* nor the client is flagged as to have pending writes: for some
* reason this replica may not be set to receive data. This is
* just for the sake of defensive programming.
*
* 2. The put_online_on_ack flag is true. To know why we don't want
* to send data to the replica in this case, please grep for the
* flag for this flag.
*
* 3. Obviously if the slave is not ONLINE.
*/
if
(
slave
->
replstate
==
SLAVE_STATE_ONLINE
&&
can_receive_writes
&&
!
slave
->
repl_put_online_on_ack
&&
clientHasPendingReplies
(
slave
))
{
writeToClient
(
slave
->
fd
,
slave
,
0
);
...
...
src/object.c
View file @
61297585
...
...
@@ -467,10 +467,15 @@ robj *tryObjectEncoding(robj *o) {
incrRefCount
(
shared
.
integers
[
value
]);
return
shared
.
integers
[
value
];
}
else
{
if
(
o
->
encoding
==
OBJ_ENCODING_RAW
)
sdsfree
(
o
->
ptr
);
if
(
o
->
encoding
==
OBJ_ENCODING_RAW
)
{
sdsfree
(
o
->
ptr
);
o
->
encoding
=
OBJ_ENCODING_INT
;
o
->
ptr
=
(
void
*
)
value
;
return
o
;
}
else
if
(
o
->
encoding
==
OBJ_ENCODING_EMBSTR
)
{
decrRefCount
(
o
);
return
createStringObjectFromLongLongForValue
(
value
);
}
}
}
...
...
@@ -1435,13 +1440,15 @@ NULL
#if defined(USE_JEMALLOC)
sds
info
=
sdsempty
();
je_malloc_stats_print
(
inputCatSds
,
&
info
,
NULL
);
addReplyBulkSds
(
c
,
info
);
addReplyVerbatim
(
c
,
info
,
sdslen
(
info
),
"txt"
);
sdsfree
(
info
);
#else
addReplyBulkCString
(
c
,
"Stats not supported for the current allocator"
);
#endif
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"doctor"
)
&&
c
->
argc
==
2
)
{
sds
report
=
getMemoryDoctorReport
();
addReplyBulkSds
(
c
,
report
);
addReplyVerbatim
(
c
,
report
,
sdslen
(
report
),
"txt"
);
sdsfree
(
report
);
}
else
if
(
!
strcasecmp
(
c
->
argv
[
1
]
->
ptr
,
"purge"
)
&&
c
->
argc
==
2
)
{
#if defined(USE_JEMALLOC)
char
tmp
[
32
];
...
...
src/rdb.c
View file @
61297585
This diff is collapsed.
Click to expand it.
src/rdb.h
View file @
61297585
...
...
@@ -145,6 +145,7 @@ size_t rdbSavedObjectLen(robj *o);
robj
*
rdbLoadObject
(
int
type
,
rio
*
rdb
,
robj
*
key
);
void
backgroundSaveDoneHandler
(
int
exitcode
,
int
bysignal
);
int
rdbSaveKeyValuePair
(
rio
*
rdb
,
robj
*
key
,
robj
*
val
,
long
long
expiretime
);
ssize_t
rdbSaveSingleModuleAux
(
rio
*
rdb
,
int
when
,
moduleType
*
mt
);
robj
*
rdbLoadStringObject
(
rio
*
rdb
);
ssize_t
rdbSaveStringObject
(
rio
*
rdb
,
robj
*
obj
);
ssize_t
rdbSaveRawString
(
rio
*
rdb
,
unsigned
char
*
s
,
size_t
len
);
...
...
src/redis-benchmark.c
View file @
61297585
...
...
@@ -104,6 +104,7 @@ static struct config {
int
is_fetching_slots
;
int
is_updating_slots
;
int
slots_last_update
;
int
enable_tracking
;
/* Thread mutexes to be used as fallbacks by atomicvar.h */
pthread_mutex_t
requests_issued_mutex
;
pthread_mutex_t
requests_finished_mutex
;
...
...
@@ -255,7 +256,7 @@ static redisConfig *getRedisConfig(const char *ip, int port,
goto
fail
;
}
if
(
config
.
auth
){
if
(
config
.
auth
)
{
void
*
authReply
=
NULL
;
redisAppendCommand
(
c
,
"AUTH %s"
,
config
.
auth
);
if
(
REDIS_OK
!=
redisGetReply
(
c
,
&
authReply
))
goto
fail
;
...
...
@@ -633,6 +634,14 @@ static client createClient(char *cmd, size_t len, client from, int thread_id) {
c
->
prefix_pending
++
;
}
if
(
config
.
enable_tracking
)
{
char
*
buf
=
NULL
;
int
len
=
redisFormatCommand
(
&
buf
,
"CLIENT TRACKING on"
);
c
->
obuf
=
sdscatlen
(
c
->
obuf
,
buf
,
len
);
free
(
buf
);
c
->
prefix_pending
++
;
}
/* If a DB number different than zero is selected, prefix our request
* buffer with the SELECT command, that will be discarded the first
* time the replies are received, so if the client is reused the
...
...
@@ -1350,6 +1359,8 @@ int parseOptions(int argc, const char **argv) {
}
else
if
(
config
.
num_threads
<
0
)
config
.
num_threads
=
0
;
}
else
if
(
!
strcmp
(
argv
[
i
],
"--cluster"
))
{
config
.
cluster_mode
=
1
;
}
else
if
(
!
strcmp
(
argv
[
i
],
"--enable-tracking"
))
{
config
.
enable_tracking
=
1
;
}
else
if
(
!
strcmp
(
argv
[
i
],
"--help"
))
{
exit_status
=
0
;
goto
usage
;
...
...
@@ -1380,6 +1391,7 @@ usage:
" --dbnum <db> SELECT the specified db number (default 0)
\n
"
" --threads <num> Enable multi-thread mode.
\n
"
" --cluster Enable cluster mode.
\n
"
" --enable-tracking Send CLIENT TRACKING on before starting benchmark.
\n
"
" -k <boolean> 1=keep alive 0=reconnect (default 1)
\n
"
" -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD
\n
"
" Using this option the benchmark will expand the string __rand_int__
\n
"
...
...
@@ -1504,6 +1516,7 @@ int main(int argc, const char **argv) {
config
.
is_fetching_slots
=
0
;
config
.
is_updating_slots
=
0
;
config
.
slots_last_update
=
0
;
config
.
enable_tracking
=
0
;
i
=
parseOptions
(
argc
,
argv
);
argc
-=
i
;
...
...
src/redis-check-rdb.c
View file @
61297585
...
...
@@ -216,14 +216,16 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
/* EXPIRETIME: load an expire associated with the next key
* to load. Note that after loading an expire we need to
* load the actual type, and continue. */
if
((
expiretime
=
rdbLoadTime
(
&
rdb
)
)
==
-
1
)
goto
eoferr
;
expiretime
=
rdbLoadTime
(
&
rdb
);
expiretime
*=
1000
;
if
(
rioGetReadError
(
&
rdb
))
goto
eoferr
;
continue
;
/* Read next opcode. */
}
else
if
(
type
==
RDB_OPCODE_EXPIRETIME_MS
)
{
/* EXPIRETIME_MS: milliseconds precision expire times introduced
* with RDB v3. Like EXPIRETIME but no with more precision. */
rdbstate
.
doing
=
RDB_CHECK_DOING_READ_EXPIRE
;
if
((
expiretime
=
rdbLoadMillisecondTime
(
&
rdb
,
rdbver
))
==
-
1
)
goto
eoferr
;
expiretime
=
rdbLoadMillisecondTime
(
&
rdb
,
rdbver
);
if
(
rioGetReadError
(
&
rdb
))
goto
eoferr
;
continue
;
/* Read next opcode. */
}
else
if
(
type
==
RDB_OPCODE_FREQ
)
{
/* FREQ: LFU frequency. */
...
...
src/redis-cli.c
View file @
61297585
...
...
@@ -218,6 +218,7 @@ static struct config {
int
hotkeys
;
int
stdinarg
;
/* get last arg from stdin. (-x option) */
char
*
auth
;
char
*
user
;
int
output
;
/* output mode, see OUTPUT_* defines */
sds
mb_delim
;
char
prompt
[
128
];
...
...
@@ -230,6 +231,7 @@ static struct config {
int
verbose
;
clusterManagerCommand
cluster_manager_command
;
int
no_auth_warning
;
int
resp3
;
}
config
;
/* User preferences. */
...
...
@@ -728,8 +730,13 @@ static int cliAuth(void) {
redisReply
*
reply
;
if
(
config
.
auth
==
NULL
)
return
REDIS_OK
;
if
(
config
.
user
==
NULL
)
reply
=
redisCommand
(
context
,
"AUTH %s"
,
config
.
auth
);
else
reply
=
redisCommand
(
context
,
"AUTH %s %s"
,
config
.
user
,
config
.
auth
);
if
(
reply
!=
NULL
)
{
if
(
reply
->
type
==
REDIS_REPLY_ERROR
)
fprintf
(
stderr
,
"Warning: AUTH failed
\n
"
);
freeReplyObject
(
reply
);
return
REDIS_OK
;
}
...
...
@@ -751,6 +758,21 @@ static int cliSelect(void) {
return
REDIS_ERR
;
}
/* Select RESP3 mode if redis-cli was started with the -3 option. */
static
int
cliSwitchProto
(
void
)
{
redisReply
*
reply
;
if
(
config
.
resp3
==
0
)
return
REDIS_OK
;
reply
=
redisCommand
(
context
,
"HELLO 3"
);
if
(
reply
!=
NULL
)
{
int
result
=
REDIS_OK
;
if
(
reply
->
type
==
REDIS_REPLY_ERROR
)
result
=
REDIS_ERR
;
freeReplyObject
(
reply
);
return
result
;
}
return
REDIS_ERR
;
}
/* Connect to the server. It is possible to pass certain flags to the function:
* CC_FORCE: The connection is performed even if there is already
* a connected socket.
...
...
@@ -788,11 +810,13 @@ static int cliConnect(int flags) {
* errors. */
anetKeepAlive
(
NULL
,
context
->
fd
,
REDIS_CLI_KEEPALIVE_INTERVAL
);
/* Do AUTH
and
select the right DB. */
/* Do AUTH
,
select the right DB
, switch to RESP3 if needed
. */
if
(
cliAuth
()
!=
REDIS_OK
)
return
REDIS_ERR
;
if
(
cliSelect
()
!=
REDIS_OK
)
return
REDIS_ERR
;
if
(
cliSwitchProto
()
!=
REDIS_OK
)
return
REDIS_ERR
;
}
return
REDIS_OK
;
}
...
...
@@ -819,10 +843,17 @@ static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
out
=
sdscatprintf
(
out
,
"(double) %s
\n
"
,
r
->
str
);
break
;
case
REDIS_REPLY_STRING
:
case
REDIS_REPLY_VERB
:
/* If you are producing output for the standard output we want
* a more interesting output with quoted characters and so forth */
* a more interesting output with quoted characters and so forth,
* unless it's a verbatim string type. */
if
(
r
->
type
==
REDIS_REPLY_STRING
)
{
out
=
sdscatrepr
(
out
,
r
->
str
,
r
->
len
);
out
=
sdscat
(
out
,
"
\n
"
);
}
else
{
out
=
sdscatlen
(
out
,
r
->
str
,
r
->
len
);
out
=
sdscat
(
out
,
"
\n
"
);
}
break
;
case
REDIS_REPLY_NIL
:
out
=
sdscat
(
out
,
"(nil)
\n
"
);
...
...
@@ -961,6 +992,7 @@ static sds cliFormatReplyRaw(redisReply *r) {
break
;
case
REDIS_REPLY_STATUS
:
case
REDIS_REPLY_STRING
:
case
REDIS_REPLY_VERB
:
if
(
r
->
type
==
REDIS_REPLY_STATUS
&&
config
.
eval_ldb
)
{
/* The Lua debugger replies with arrays of simple (status)
* strings. We colorize the output for more fun if this
...
...
@@ -980,9 +1012,15 @@ static sds cliFormatReplyRaw(redisReply *r) {
out
=
sdscatlen
(
out
,
r
->
str
,
r
->
len
);
}
break
;
case
REDIS_REPLY_BOOL
:
out
=
sdscat
(
out
,
r
->
integer
?
"(true)"
:
"(false)"
);
break
;
case
REDIS_REPLY_INTEGER
:
out
=
sdscatprintf
(
out
,
"%lld"
,
r
->
integer
);
break
;
case
REDIS_REPLY_DOUBLE
:
out
=
sdscatprintf
(
out
,
"%s"
,
r
->
str
);
break
;
case
REDIS_REPLY_ARRAY
:
for
(
i
=
0
;
i
<
r
->
elements
;
i
++
)
{
if
(
i
>
0
)
out
=
sdscat
(
out
,
config
.
mb_delim
);
...
...
@@ -991,6 +1029,19 @@ static sds cliFormatReplyRaw(redisReply *r) {
sdsfree
(
tmp
);
}
break
;
case
REDIS_REPLY_MAP
:
for
(
i
=
0
;
i
<
r
->
elements
;
i
+=
2
)
{
if
(
i
>
0
)
out
=
sdscat
(
out
,
config
.
mb_delim
);
tmp
=
cliFormatReplyRaw
(
r
->
element
[
i
]);
out
=
sdscatlen
(
out
,
tmp
,
sdslen
(
tmp
));
sdsfree
(
tmp
);
out
=
sdscatlen
(
out
,
" "
,
1
);
tmp
=
cliFormatReplyRaw
(
r
->
element
[
i
+
1
]);
out
=
sdscatlen
(
out
,
tmp
,
sdslen
(
tmp
));
sdsfree
(
tmp
);
}
break
;
default:
fprintf
(
stderr
,
"Unknown reply type: %d
\n
"
,
r
->
type
);
exit
(
1
);
...
...
@@ -1013,13 +1064,21 @@ static sds cliFormatReplyCSV(redisReply *r) {
case
REDIS_REPLY_INTEGER
:
out
=
sdscatprintf
(
out
,
"%lld"
,
r
->
integer
);
break
;
case
REDIS_REPLY_DOUBLE
:
out
=
sdscatprintf
(
out
,
"%s"
,
r
->
str
);
break
;
case
REDIS_REPLY_STRING
:
case
REDIS_REPLY_VERB
:
out
=
sdscatrepr
(
out
,
r
->
str
,
r
->
len
);
break
;
case
REDIS_REPLY_NIL
:
out
=
sdscat
(
out
,
"NIL"
);
out
=
sdscat
(
out
,
"NULL"
);
break
;
case
REDIS_REPLY_BOOL
:
out
=
sdscat
(
out
,
r
->
integer
?
"true"
:
"false"
);
break
;
case
REDIS_REPLY_ARRAY
:
case
REDIS_REPLY_MAP
:
/* CSV has no map type, just output flat list. */
for
(
i
=
0
;
i
<
r
->
elements
;
i
++
)
{
sds
tmp
=
cliFormatReplyCSV
(
r
->
element
[
i
]);
out
=
sdscatlen
(
out
,
tmp
,
sdslen
(
tmp
));
...
...
@@ -1213,7 +1272,8 @@ static int cliSendCommand(int argc, char **argv, long repeat) {
if
(
!
strcasecmp
(
command
,
"select"
)
&&
argc
==
2
&&
config
.
last_cmd_type
!=
REDIS_REPLY_ERROR
)
{
config
.
dbnum
=
atoi
(
argv
[
1
]);
cliRefreshPrompt
();
}
else
if
(
!
strcasecmp
(
command
,
"auth"
)
&&
argc
==
2
)
{
}
else
if
(
!
strcasecmp
(
command
,
"auth"
)
&&
(
argc
==
2
||
argc
==
3
))
{
cliSelect
();
}
}
...
...
@@ -1296,8 +1356,12 @@ static int parseOptions(int argc, char **argv) {
config
.
dbnum
=
atoi
(
argv
[
++
i
]);
}
else
if
(
!
strcmp
(
argv
[
i
],
"--no-auth-warning"
))
{
config
.
no_auth_warning
=
1
;
}
else
if
(
!
strcmp
(
argv
[
i
],
"-a"
)
&&
!
lastarg
)
{
}
else
if
((
!
strcmp
(
argv
[
i
],
"-a"
)
||
!
strcmp
(
argv
[
i
],
"--pass"
))
&&
!
lastarg
)
{
config
.
auth
=
argv
[
++
i
];
}
else
if
(
!
strcmp
(
argv
[
i
],
"--user"
)
&&
!
lastarg
)
{
config
.
user
=
argv
[
++
i
];
}
else
if
(
!
strcmp
(
argv
[
i
],
"-u"
)
&&
!
lastarg
)
{
parseRedisUri
(
argv
[
++
i
]);
}
else
if
(
!
strcmp
(
argv
[
i
],
"--raw"
))
{
...
...
@@ -1439,6 +1503,8 @@ static int parseOptions(int argc, char **argv) {
printf
(
"redis-cli %s
\n
"
,
version
);
sdsfree
(
version
);
exit
(
0
);
}
else
if
(
!
strcmp
(
argv
[
i
],
"-3"
))
{
config
.
resp3
=
1
;
}
else
if
(
CLUSTER_MANAGER_MODE
()
&&
argv
[
i
][
0
]
!=
'-'
)
{
if
(
config
.
cluster_manager_command
.
argc
==
0
)
{
int
j
=
i
+
1
;
...
...
@@ -1514,11 +1580,14 @@ static void usage(void) {
" You can also use the "
REDIS_CLI_AUTH_ENV
" environment
\n
"
" variable to pass this password more safely
\n
"
" (if both are used, this argument takes predecence).
\n
"
" -user <username> Used to send ACL style 'AUTH username pass'. Needs -a.
\n
"
" -pass <password> Alias of -a for consistency with the new --user option.
\n
"
" -u <uri> Server URI.
\n
"
" -r <repeat> Execute specified command N times.
\n
"
" -i <interval> When -r is used, waits <interval> seconds per command.
\n
"
" It is possible to specify sub-second times like -i 0.1.
\n
"
" -n <db> Database number.
\n
"
" -3 Start session in RESP3 protocol mode.
\n
"
" -x Read last argument from STDIN.
\n
"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default:
\\
n).
\n
"
" -c Enable cluster mode (follow -ASK and -MOVED redirections).
\n
"
...
...
@@ -1533,7 +1602,9 @@ static void usage(void) {
" --csv is specified, or if you redirect the output to a non
\n
"
" TTY, it samples the latency for 1 second (you can use
\n
"
" -i to change the interval), then produces a single output
\n
"
" and exits.
\n
"
" and exits.
\n
"
,
version
);
fprintf
(
stderr
,
" --latency-history Like --latency but tracking latency changes over time.
\n
"
" Default time interval is 15 sec. Change it using -i.
\n
"
" --latency-dist Shows latency as a spectrum, requires xterm 256 colors.
\n
"
...
...
@@ -1568,7 +1639,7 @@ static void usage(void) {
" --help Output this help and exit.
\n
"
" --version Output version and exit.
\n
"
"
\n
"
,
version
,
REDIS_CLI_DEFAULT_PIPE_TIMEOUT
);
REDIS_CLI_DEFAULT_PIPE_TIMEOUT
);
/* Using another fprintf call to avoid -Woverlength-strings compile warning */
fprintf
(
stderr
,
"Cluster Manager Commands:
\n
"
...
...
@@ -2350,7 +2421,12 @@ static int clusterManagerNodeConnect(clusterManagerNode *node) {
* errors. */
anetKeepAlive
(
NULL
,
node
->
context
->
fd
,
REDIS_CLI_KEEPALIVE_INTERVAL
);
if
(
config
.
auth
)
{
redisReply
*
reply
=
redisCommand
(
node
->
context
,
"AUTH %s"
,
config
.
auth
);
redisReply
*
reply
;
if
(
config
.
user
==
NULL
)
reply
=
redisCommand
(
node
->
context
,
"AUTH %s"
,
config
.
auth
);
else
reply
=
redisCommand
(
node
->
context
,
"AUTH %s %s"
,
config
.
user
,
config
.
auth
);
int
ok
=
clusterManagerCheckRedisReply
(
node
,
reply
,
NULL
);
if
(
reply
!=
NULL
)
freeReplyObject
(
reply
);
if
(
!
ok
)
return
0
;
...
...
@@ -6724,6 +6800,7 @@ static void pipeMode(void) {
/* Handle the readable state: we can read replies from the server. */
if
(
mask
&
AE_READABLE
)
{
ssize_t
nread
;
int
read_error
=
0
;
/* Read from socket and feed the hiredis reader. */
do
{
...
...
@@ -6731,7 +6808,8 @@ static void pipeMode(void) {
if
(
nread
==
-
1
&&
errno
!=
EAGAIN
&&
errno
!=
EINTR
)
{
fprintf
(
stderr
,
"Error reading from the server: %s
\n
"
,
strerror
(
errno
));
exit
(
1
);
read_error
=
1
;
break
;
}
if
(
nread
>
0
)
{
redisReaderFeed
(
reader
,
ibuf
,
nread
);
...
...
@@ -6764,6 +6842,11 @@ static void pipeMode(void) {
freeReplyObject
(
reply
);
}
}
while
(
reply
);
/* Abort on read errors. We abort here because it is important
* to consume replies even after a read error: this way we can
* show a potential problem to the user. */
if
(
read_error
)
exit
(
1
);
}
/* Handle the writable state: we can send protocol to the server. */
...
...
@@ -7671,6 +7754,7 @@ int main(int argc, char **argv) {
config
.
hotkeys
=
0
;
config
.
stdinarg
=
0
;
config
.
auth
=
NULL
;
config
.
user
=
NULL
;
config
.
eval
=
NULL
;
config
.
eval_ldb
=
0
;
config
.
eval_ldb_end
=
0
;
...
...
src/redismodule.h
View file @
61297585
...
...
@@ -129,6 +129,10 @@
#define REDISMODULE_NOT_USED(V) ((void) V)
/* Bit flags for aux_save_triggers and the aux_load and aux_save callbacks */
#define REDISMODULE_AUX_BEFORE_RDB (1<<0)
#define REDISMODULE_AUX_AFTER_RDB (1<<1)
/* This type represents a timer handle, and is returned when a timer is
* registered and used in order to invalidate a timer. It's just a 64 bit
* number, because this is how each timer is represented inside the radix tree
...
...
@@ -140,6 +144,9 @@ typedef uint64_t RedisModuleTimerID;
/* Do filter RedisModule_Call() commands initiated by module itself. */
#define REDISMODULE_CMDFILTER_NOSELF (1<<0)
/* Declare that the module can handle errors with RedisModule_SetModuleOptions. */
#define REDISMODULE_OPTIONS_HANDLE_IO_ERRORS (1<<0)
/* ------------------------- End of common defines ------------------------ */
#ifndef REDISMODULE_CORE
...
...
@@ -166,6 +173,8 @@ typedef void (*RedisModuleDisconnectFunc)(RedisModuleCtx *ctx, RedisModuleBlocke
typedef
int
(
*
RedisModuleNotificationFunc
)(
RedisModuleCtx
*
ctx
,
int
type
,
const
char
*
event
,
RedisModuleString
*
key
);
typedef
void
*
(
*
RedisModuleTypeLoadFunc
)(
RedisModuleIO
*
rdb
,
int
encver
);
typedef
void
(
*
RedisModuleTypeSaveFunc
)(
RedisModuleIO
*
rdb
,
void
*
value
);
typedef
int
(
*
RedisModuleTypeAuxLoadFunc
)(
RedisModuleIO
*
rdb
,
int
encver
,
int
when
);
typedef
void
(
*
RedisModuleTypeAuxSaveFunc
)(
RedisModuleIO
*
rdb
,
int
when
);
typedef
void
(
*
RedisModuleTypeRewriteFunc
)(
RedisModuleIO
*
aof
,
RedisModuleString
*
key
,
void
*
value
);
typedef
size_t
(
*
RedisModuleTypeMemUsageFunc
)(
const
void
*
value
);
typedef
void
(
*
RedisModuleTypeDigestFunc
)(
RedisModuleDigest
*
digest
,
void
*
value
);
...
...
@@ -175,7 +184,7 @@ typedef void (*RedisModuleTimerProc)(RedisModuleCtx *ctx, void *data);
typedef
void
(
*
RedisModuleCommandFilterFunc
)
(
RedisModuleCommandFilterCtx
*
filter
);
typedef
void
(
*
RedisModuleForkDoneHandler
)
(
int
exitcode
,
int
bysignal
,
void
*
user_data
);
#define REDISMODULE_TYPE_METHOD_VERSION
1
#define REDISMODULE_TYPE_METHOD_VERSION
2
typedef
struct
RedisModuleTypeMethods
{
uint64_t
version
;
RedisModuleTypeLoadFunc
rdb_load
;
...
...
@@ -184,6 +193,9 @@ typedef struct RedisModuleTypeMethods {
RedisModuleTypeMemUsageFunc
mem_usage
;
RedisModuleTypeDigestFunc
digest
;
RedisModuleTypeFreeFunc
free
;
RedisModuleTypeAuxLoadFunc
aux_load
;
RedisModuleTypeAuxSaveFunc
aux_save
;
int
aux_save_triggers
;
}
RedisModuleTypeMethods
;
#define REDISMODULE_GET_API(name) \
...
...
@@ -272,6 +284,8 @@ RedisModuleType *REDISMODULE_API_FUNC(RedisModule_CreateDataType)(RedisModuleCtx
int
REDISMODULE_API_FUNC
(
RedisModule_ModuleTypeSetValue
)(
RedisModuleKey
*
key
,
RedisModuleType
*
mt
,
void
*
value
);
RedisModuleType
*
REDISMODULE_API_FUNC
(
RedisModule_ModuleTypeGetType
)(
RedisModuleKey
*
key
);
void
*
REDISMODULE_API_FUNC
(
RedisModule_ModuleTypeGetValue
)(
RedisModuleKey
*
key
);
int
REDISMODULE_API_FUNC
(
RedisModule_IsIOError
)(
RedisModuleIO
*
io
);
void
REDISMODULE_API_FUNC
(
RedisModule_SetModuleOptions
)(
RedisModuleCtx
*
ctx
,
int
options
);
void
REDISMODULE_API_FUNC
(
RedisModule_SaveUnsigned
)(
RedisModuleIO
*
io
,
uint64_t
value
);
uint64_t
REDISMODULE_API_FUNC
(
RedisModule_LoadUnsigned
)(
RedisModuleIO
*
io
);
void
REDISMODULE_API_FUNC
(
RedisModule_SaveSigned
)(
RedisModuleIO
*
io
,
int64_t
value
);
...
...
@@ -287,6 +301,7 @@ void REDISMODULE_API_FUNC(RedisModule_SaveFloat)(RedisModuleIO *io, float value)
float
REDISMODULE_API_FUNC
(
RedisModule_LoadFloat
)(
RedisModuleIO
*
io
);
void
REDISMODULE_API_FUNC
(
RedisModule_Log
)(
RedisModuleCtx
*
ctx
,
const
char
*
level
,
const
char
*
fmt
,
...);
void
REDISMODULE_API_FUNC
(
RedisModule_LogIOError
)(
RedisModuleIO
*
io
,
const
char
*
levelstr
,
const
char
*
fmt
,
...);
void
REDISMODULE_API_FUNC
(
RedisModule__Assert
)(
const
char
*
estr
,
const
char
*
file
,
int
line
);
int
REDISMODULE_API_FUNC
(
RedisModule_StringAppendBuffer
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
*
str
,
const
char
*
buf
,
size_t
len
);
void
REDISMODULE_API_FUNC
(
RedisModule_RetainString
)(
RedisModuleCtx
*
ctx
,
RedisModuleString
*
str
);
int
REDISMODULE_API_FUNC
(
RedisModule_StringCompare
)(
RedisModuleString
*
a
,
RedisModuleString
*
b
);
...
...
@@ -448,6 +463,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
ModuleTypeSetValue
);
REDISMODULE_GET_API
(
ModuleTypeGetType
);
REDISMODULE_GET_API
(
ModuleTypeGetValue
);
REDISMODULE_GET_API
(
IsIOError
);
REDISMODULE_GET_API
(
SetModuleOptions
);
REDISMODULE_GET_API
(
SaveUnsigned
);
REDISMODULE_GET_API
(
LoadUnsigned
);
REDISMODULE_GET_API
(
SaveSigned
);
...
...
@@ -463,6 +480,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API
(
EmitAOF
);
REDISMODULE_GET_API
(
Log
);
REDISMODULE_GET_API
(
LogIOError
);
REDISMODULE_GET_API
(
_Assert
);
REDISMODULE_GET_API
(
StringAppendBuffer
);
REDISMODULE_GET_API
(
RetainString
);
REDISMODULE_GET_API
(
StringCompare
);
...
...
@@ -542,6 +560,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
return
REDISMODULE_OK
;
}
#define RedisModule_Assert(_e) ((_e)?(void)0 : (RedisModule__Assert(#_e,__FILE__,__LINE__),exit(1)))
#else
/* Things only defined for the modules core, not exported to modules
...
...
src/replication.c
View file @
61297585
This diff is collapsed.
Click to expand it.
Prev
1
2
3
4
5
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