mirror of
https://github.com/fluencelabs/redis
synced 2025-04-25 10:32:14 +00:00
Merge branch 'unstable' of github.com:/antirez/redis into unstable
This commit is contained in:
commit
50d4326e3b
18
README.md
18
README.md
@ -35,6 +35,11 @@ It is as simple as:
|
|||||||
|
|
||||||
% make
|
% make
|
||||||
|
|
||||||
|
To build with TLS support, you'll need OpenSSL development libraries (e.g.
|
||||||
|
libssl-dev on Debian/Ubuntu) and run:
|
||||||
|
|
||||||
|
% make BUILD_TLS=yes
|
||||||
|
|
||||||
You can run a 32 bit Redis binary using:
|
You can run a 32 bit Redis binary using:
|
||||||
|
|
||||||
% make 32bit
|
% make 32bit
|
||||||
@ -43,6 +48,13 @@ After building Redis, it is a good idea to test it using:
|
|||||||
|
|
||||||
% make test
|
% make test
|
||||||
|
|
||||||
|
If TLS is built, running the tests with TLS enabled (you will need `tcl-tls`
|
||||||
|
installed):
|
||||||
|
|
||||||
|
% ./utils/gen-test-certs.sh
|
||||||
|
% ./runtest --tls
|
||||||
|
|
||||||
|
|
||||||
Fixing build problems with dependencies or cached build options
|
Fixing build problems with dependencies or cached build options
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@ -125,6 +137,12 @@ as options using the command line. Examples:
|
|||||||
All the options in redis.conf are also supported as options using the command
|
All the options in redis.conf are also supported as options using the command
|
||||||
line, with exactly the same name.
|
line, with exactly the same name.
|
||||||
|
|
||||||
|
Running Redis with TLS:
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Please consult the [TLS.md](TLS.md) file for more information on
|
||||||
|
how to use Redis with TLS.
|
||||||
|
|
||||||
Playing with Redis
|
Playing with Redis
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
45
TLS.md
45
TLS.md
@ -1,8 +1,5 @@
|
|||||||
TLS Support -- Work In Progress
|
TLS Support
|
||||||
===============================
|
===========
|
||||||
|
|
||||||
This is a brief note to capture current thoughts/ideas and track pending action
|
|
||||||
items.
|
|
||||||
|
|
||||||
Getting Started
|
Getting Started
|
||||||
---------------
|
---------------
|
||||||
@ -69,37 +66,23 @@ probably not be so hard. For cluster keys migration it might be more difficult,
|
|||||||
but there are probably other good reasons to improve that part anyway.
|
but there are probably other good reasons to improve that part anyway.
|
||||||
|
|
||||||
To-Do List
|
To-Do List
|
||||||
==========
|
----------
|
||||||
|
|
||||||
Additional TLS Features
|
- [ ] Add session caching support. Check if/how it's handled by clients to
|
||||||
-----------------------
|
assess how useful/important it is.
|
||||||
|
- [ ] redis-benchmark support. The current implementation is a mix of using
|
||||||
|
hiredis for parsing and basic networking (establishing connections), but
|
||||||
|
directly manipulating sockets for most actions. This will need to be cleaned
|
||||||
|
up for proper TLS support. The best approach is probably to migrate to hiredis
|
||||||
|
async mode.
|
||||||
|
- [ ] redis-cli `--slave` and `--rdb` support.
|
||||||
|
|
||||||
1. Add metrics to INFO?
|
Multi-port
|
||||||
2. Add session caching support. Check if/how it's handled by clients to assess
|
----------
|
||||||
how useful/important it is.
|
|
||||||
|
|
||||||
redis-benchmark
|
|
||||||
---------------
|
|
||||||
|
|
||||||
The current implementation is a mix of using hiredis for parsing and basic
|
|
||||||
networking (establishing connections), but directly manipulating sockets for
|
|
||||||
most actions.
|
|
||||||
|
|
||||||
This will need to be cleaned up for proper TLS support. The best approach is
|
|
||||||
probably to migrate to hiredis async mode.
|
|
||||||
|
|
||||||
redis-cli
|
|
||||||
---------
|
|
||||||
|
|
||||||
1. Add support for TLS in --slave and --rdb modes.
|
|
||||||
|
|
||||||
Others
|
|
||||||
------
|
|
||||||
|
|
||||||
Consider the implications of allowing TLS to be configured on a separate port,
|
Consider the implications of allowing TLS to be configured on a separate port,
|
||||||
making Redis listening on multiple ports.
|
making Redis listening on multiple ports:
|
||||||
|
|
||||||
This impacts many things, like
|
|
||||||
1. Startup banner port notification
|
1. Startup banner port notification
|
||||||
2. Proctitle
|
2. Proctitle
|
||||||
3. How slaves announce themselves
|
3. How slaves announce themselves
|
||||||
|
27
redis.conf
27
redis.conf
@ -155,23 +155,22 @@ tcp-keepalive 300
|
|||||||
# tls-ca-cert-file ca.crt
|
# tls-ca-cert-file ca.crt
|
||||||
# tls-ca-cert-dir /etc/ssl/certs
|
# tls-ca-cert-dir /etc/ssl/certs
|
||||||
|
|
||||||
# If TLS/SSL clients are required to authenticate using a client side
|
# By default, clients (including replica servers) on a TLS port are required
|
||||||
# certificate, use this directive.
|
# to authenticate using valid client side certificates.
|
||||||
#
|
#
|
||||||
# Note: this applies to all incoming clients, including replicas.
|
# It is possible to disable authentication using this directive.
|
||||||
#
|
#
|
||||||
# tls-auth-clients yes
|
# tls-auth-clients no
|
||||||
|
|
||||||
# If TLS/SSL should be used when connecting as a replica to a master, enable
|
# By default, a Redis replica does not attempt to establish a TLS connection
|
||||||
# this configuration directive:
|
# with its master.
|
||||||
|
#
|
||||||
|
# Use the following directive to enable TLS on replication links.
|
||||||
#
|
#
|
||||||
# tls-replication yes
|
# tls-replication yes
|
||||||
|
|
||||||
# If TLS/SSL should be used for the Redis Cluster bus, enable this configuration
|
# By default, the Redis Cluster bus uses a plain TCP connection. To enable
|
||||||
# directive.
|
# TLS for the bus protocol, use the following directive:
|
||||||
#
|
|
||||||
# NOTE: If TLS/SSL is enabled for Cluster Bus, mutual authentication is always
|
|
||||||
# enforced.
|
|
||||||
#
|
#
|
||||||
# tls-cluster yes
|
# tls-cluster yes
|
||||||
|
|
||||||
@ -1362,7 +1361,11 @@ latency-monitor-threshold 0
|
|||||||
# z Sorted set commands
|
# z Sorted set commands
|
||||||
# x Expired events (events generated every time a key expires)
|
# x Expired events (events generated every time a key expires)
|
||||||
# e Evicted events (events generated when a key is evicted for maxmemory)
|
# e Evicted events (events generated when a key is evicted for maxmemory)
|
||||||
# A Alias for g$lshzxe, so that the "AKE" string means all the events.
|
# t Stream commands
|
||||||
|
# m Key-miss events (Note: It is not included in the 'A' class)
|
||||||
|
# A Alias for g$lshzxet, so that the "AKE" string means all the events
|
||||||
|
# (Except key-miss events which are excluded from 'A' due to their
|
||||||
|
# unique nature).
|
||||||
#
|
#
|
||||||
# The "notify-keyspace-events" takes as argument a string that is composed
|
# The "notify-keyspace-events" takes as argument a string that is composed
|
||||||
# of zero or multiple characters. The empty string means that notifications
|
# of zero or multiple characters. The empty string means that notifications
|
||||||
|
@ -1592,6 +1592,7 @@ void addACLLogEntry(client *c, int reason, int keypos, sds username) {
|
|||||||
/* ACL -- show and modify the configuration of ACL users.
|
/* ACL -- show and modify the configuration of ACL users.
|
||||||
* ACL HELP
|
* ACL HELP
|
||||||
* ACL LOAD
|
* ACL LOAD
|
||||||
|
* ACL SAVE
|
||||||
* ACL LIST
|
* ACL LIST
|
||||||
* ACL USERS
|
* ACL USERS
|
||||||
* ACL CAT [<category>]
|
* ACL CAT [<category>]
|
||||||
@ -1855,6 +1856,7 @@ void aclCommand(client *c) {
|
|||||||
} else if (!strcasecmp(sub,"help")) {
|
} else if (!strcasecmp(sub,"help")) {
|
||||||
const char *help[] = {
|
const char *help[] = {
|
||||||
"LOAD -- Reload users from the ACL file.",
|
"LOAD -- Reload users from the ACL file.",
|
||||||
|
"SAVE -- Save the current config to the ACL file."
|
||||||
"LIST -- Show user details in config file format.",
|
"LIST -- Show user details in config file format.",
|
||||||
"USERS -- List all the registered usernames.",
|
"USERS -- List all the registered usernames.",
|
||||||
"SETUSER <username> [attribs ...] -- Create or modify a user.",
|
"SETUSER <username> [attribs ...] -- Create or modify a user.",
|
||||||
|
@ -242,6 +242,7 @@ void stopAppendOnly(void) {
|
|||||||
server.aof_fd = -1;
|
server.aof_fd = -1;
|
||||||
server.aof_selected_db = -1;
|
server.aof_selected_db = -1;
|
||||||
server.aof_state = AOF_OFF;
|
server.aof_state = AOF_OFF;
|
||||||
|
server.aof_rewrite_scheduled = 0;
|
||||||
killAppendOnlyChild();
|
killAppendOnlyChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
137
src/config.c
137
src/config.c
@ -190,8 +190,9 @@ typedef struct typeInterface {
|
|||||||
void (*init)(typeData data);
|
void (*init)(typeData data);
|
||||||
/* Called on server start, should return 1 on success, 0 on error and should set err */
|
/* Called on server start, should return 1 on success, 0 on error and should set err */
|
||||||
int (*load)(typeData data, sds *argc, int argv, char **err);
|
int (*load)(typeData data, sds *argc, int argv, char **err);
|
||||||
/* Called on CONFIG SET, returns 1 on success, 0 on error */
|
/* Called on server startup and CONFIG SET, returns 1 on success, 0 on error
|
||||||
int (*set)(typeData data, sds value, char **err);
|
* and can set a verbose err string, update is true when called from CONFIG SET */
|
||||||
|
int (*set)(typeData data, sds value, int update, char **err);
|
||||||
/* Called on CONFIG GET, required to add output to the client */
|
/* Called on CONFIG GET, required to add output to the client */
|
||||||
void (*get)(client *c, typeData data);
|
void (*get)(client *c, typeData data);
|
||||||
/* Called on CONFIG REWRITE, required to rewrite the config state */
|
/* Called on CONFIG REWRITE, required to rewrite the config state */
|
||||||
@ -323,7 +324,11 @@ void loadServerConfigFromString(char *config) {
|
|||||||
if ((!strcasecmp(argv[0],config->name) ||
|
if ((!strcasecmp(argv[0],config->name) ||
|
||||||
(config->alias && !strcasecmp(argv[0],config->alias))))
|
(config->alias && !strcasecmp(argv[0],config->alias))))
|
||||||
{
|
{
|
||||||
if (!config->interface.load(config->data, argv, argc, &err)) {
|
if (argc != 2) {
|
||||||
|
err = "wrong number of arguments";
|
||||||
|
goto loaderr;
|
||||||
|
}
|
||||||
|
if (!config->interface.set(config->data, argv[1], 0, &err)) {
|
||||||
goto loaderr;
|
goto loaderr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,6 +349,10 @@ void loadServerConfigFromString(char *config) {
|
|||||||
if (addresses > CONFIG_BINDADDR_MAX) {
|
if (addresses > CONFIG_BINDADDR_MAX) {
|
||||||
err = "Too many bind addresses specified"; goto loaderr;
|
err = "Too many bind addresses specified"; goto loaderr;
|
||||||
}
|
}
|
||||||
|
/* Free old bind addresses */
|
||||||
|
for (j = 0; j < server.bindaddr_count; j++) {
|
||||||
|
zfree(server.bindaddr[j]);
|
||||||
|
}
|
||||||
for (j = 0; j < addresses; j++)
|
for (j = 0; j < addresses; j++)
|
||||||
server.bindaddr[j] = zstrdup(argv[j+1]);
|
server.bindaddr[j] = zstrdup(argv[j+1]);
|
||||||
server.bindaddr_count = addresses;
|
server.bindaddr_count = addresses;
|
||||||
@ -599,7 +608,7 @@ void configSetCommand(client *c) {
|
|||||||
if(config->modifiable && (!strcasecmp(c->argv[2]->ptr,config->name) ||
|
if(config->modifiable && (!strcasecmp(c->argv[2]->ptr,config->name) ||
|
||||||
(config->alias && !strcasecmp(c->argv[2]->ptr,config->alias))))
|
(config->alias && !strcasecmp(c->argv[2]->ptr,config->alias))))
|
||||||
{
|
{
|
||||||
if (!config->interface.set(config->data,o->ptr, &errstr)) {
|
if (!config->interface.set(config->data,o->ptr,1,&errstr)) {
|
||||||
goto badfmt;
|
goto badfmt;
|
||||||
}
|
}
|
||||||
addReply(c,shared.ok);
|
addReply(c,shared.ok);
|
||||||
@ -1536,9 +1545,8 @@ static char loadbuf[LOADBUF_SIZE];
|
|||||||
.alias = (config_alias), \
|
.alias = (config_alias), \
|
||||||
.modifiable = (is_modifiable),
|
.modifiable = (is_modifiable),
|
||||||
|
|
||||||
#define embedConfigInterface(initfn, loadfn, setfn, getfn, rewritefn) .interface = { \
|
#define embedConfigInterface(initfn, setfn, getfn, rewritefn) .interface = { \
|
||||||
.init = (initfn), \
|
.init = (initfn), \
|
||||||
.load = (loadfn), \
|
|
||||||
.set = (setfn), \
|
.set = (setfn), \
|
||||||
.get = (getfn), \
|
.get = (getfn), \
|
||||||
.rewrite = (rewritefn) \
|
.rewrite = (rewritefn) \
|
||||||
@ -1561,30 +1569,17 @@ static void boolConfigInit(typeData data) {
|
|||||||
*data.yesno.config = data.yesno.default_value;
|
*data.yesno.config = data.yesno.default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int boolConfigLoad(typeData data, sds *argv, int argc, char **err) {
|
static int boolConfigSet(typeData data, sds value, int update, char **err) {
|
||||||
int yn;
|
int yn = yesnotoi(value);
|
||||||
if (argc != 2) {
|
if (yn == -1) {
|
||||||
*err = "wrong number of arguments";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if ((yn = yesnotoi(argv[1])) == -1) {
|
|
||||||
*err = "argument must be 'yes' or 'no'";
|
*err = "argument must be 'yes' or 'no'";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (data.yesno.is_valid_fn && !data.yesno.is_valid_fn(yn, err))
|
|
||||||
return 0;
|
|
||||||
*data.yesno.config = yn;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int boolConfigSet(typeData data, sds value, char **err) {
|
|
||||||
int yn = yesnotoi(value);
|
|
||||||
if (yn == -1) return 0;
|
|
||||||
if (data.yesno.is_valid_fn && !data.yesno.is_valid_fn(yn, err))
|
if (data.yesno.is_valid_fn && !data.yesno.is_valid_fn(yn, err))
|
||||||
return 0;
|
return 0;
|
||||||
int prev = *(data.yesno.config);
|
int prev = *(data.yesno.config);
|
||||||
*(data.yesno.config) = yn;
|
*(data.yesno.config) = yn;
|
||||||
if (data.yesno.update_fn && !data.yesno.update_fn(yn, prev, err)) {
|
if (update && data.yesno.update_fn && !data.yesno.update_fn(yn, prev, err)) {
|
||||||
*(data.yesno.config) = prev;
|
*(data.yesno.config) = prev;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1601,7 +1596,7 @@ static void boolConfigRewrite(typeData data, const char *name, struct rewriteCon
|
|||||||
|
|
||||||
#define createBoolConfig(name, alias, modifiable, config_addr, default, is_valid, update) { \
|
#define createBoolConfig(name, alias, modifiable, config_addr, default, is_valid, update) { \
|
||||||
embedCommonConfig(name, alias, modifiable) \
|
embedCommonConfig(name, alias, modifiable) \
|
||||||
embedConfigInterface(boolConfigInit, boolConfigLoad, boolConfigSet, boolConfigGet, boolConfigRewrite) \
|
embedConfigInterface(boolConfigInit, boolConfigSet, boolConfigGet, boolConfigRewrite) \
|
||||||
.data.yesno = { \
|
.data.yesno = { \
|
||||||
.config = &(config_addr), \
|
.config = &(config_addr), \
|
||||||
.default_value = (default), \
|
.default_value = (default), \
|
||||||
@ -1619,23 +1614,7 @@ static void stringConfigInit(typeData data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int stringConfigLoad(typeData data, sds *argv, int argc, char **err) {
|
static int stringConfigSet(typeData data, sds value, int update, char **err) {
|
||||||
if (argc != 2) {
|
|
||||||
*err = "wrong number of arguments";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (data.string.is_valid_fn && !data.string.is_valid_fn(argv[1], err))
|
|
||||||
return 0;
|
|
||||||
zfree(*data.string.config);
|
|
||||||
if (data.string.convert_empty_to_null) {
|
|
||||||
*data.string.config = argv[1][0] ? zstrdup(argv[1]) : NULL;
|
|
||||||
} else {
|
|
||||||
*data.string.config = zstrdup(argv[1]);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int stringConfigSet(typeData data, sds value, char **err) {
|
|
||||||
if (data.string.is_valid_fn && !data.string.is_valid_fn(value, err))
|
if (data.string.is_valid_fn && !data.string.is_valid_fn(value, err))
|
||||||
return 0;
|
return 0;
|
||||||
char *prev = *data.string.config;
|
char *prev = *data.string.config;
|
||||||
@ -1644,7 +1623,7 @@ static int stringConfigSet(typeData data, sds value, char **err) {
|
|||||||
} else {
|
} else {
|
||||||
*data.string.config = zstrdup(value);
|
*data.string.config = zstrdup(value);
|
||||||
}
|
}
|
||||||
if (data.string.update_fn && !data.string.update_fn(*data.string.config, prev, err)) {
|
if (update && data.string.update_fn && !data.string.update_fn(*data.string.config, prev, err)) {
|
||||||
zfree(*data.string.config);
|
zfree(*data.string.config);
|
||||||
*data.string.config = prev;
|
*data.string.config = prev;
|
||||||
return 0;
|
return 0;
|
||||||
@ -1666,7 +1645,7 @@ static void stringConfigRewrite(typeData data, const char *name, struct rewriteC
|
|||||||
|
|
||||||
#define createStringConfig(name, alias, modifiable, empty_to_null, config_addr, default, is_valid, update) { \
|
#define createStringConfig(name, alias, modifiable, empty_to_null, config_addr, default, is_valid, update) { \
|
||||||
embedCommonConfig(name, alias, modifiable) \
|
embedCommonConfig(name, alias, modifiable) \
|
||||||
embedConfigInterface(stringConfigInit, stringConfigLoad, stringConfigSet, stringConfigGet, stringConfigRewrite) \
|
embedConfigInterface(stringConfigInit, stringConfigSet, stringConfigGet, stringConfigRewrite) \
|
||||||
.data.string = { \
|
.data.string = { \
|
||||||
.config = &(config_addr), \
|
.config = &(config_addr), \
|
||||||
.default_value = (default), \
|
.default_value = (default), \
|
||||||
@ -1677,17 +1656,12 @@ static void stringConfigRewrite(typeData data, const char *name, struct rewriteC
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Enum configs */
|
/* Enum configs */
|
||||||
static void configEnumInit(typeData data) {
|
static void enumConfigInit(typeData data) {
|
||||||
*data.enumd.config = data.enumd.default_value;
|
*data.enumd.config = data.enumd.default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int configEnumLoad(typeData data, sds *argv, int argc, char **err) {
|
static int enumConfigSet(typeData data, sds value, int update, char **err) {
|
||||||
if (argc != 2) {
|
int enumval = configEnumGetValue(data.enumd.enum_value, value);
|
||||||
*err = "wrong number of arguments";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int enumval = configEnumGetValue(data.enumd.enum_value, argv[1]);
|
|
||||||
if (enumval == INT_MIN) {
|
if (enumval == INT_MIN) {
|
||||||
sds enumerr = sdsnew("argument must be one of the following: ");
|
sds enumerr = sdsnew("argument must be one of the following: ");
|
||||||
configEnum *enumNode = data.enumd.enum_value;
|
configEnum *enumNode = data.enumd.enum_value;
|
||||||
@ -1707,37 +1681,28 @@ static int configEnumLoad(typeData data, sds *argv, int argc, char **err) {
|
|||||||
*err = loadbuf;
|
*err = loadbuf;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (data.enumd.is_valid_fn && !data.enumd.is_valid_fn(enumval, err))
|
|
||||||
return 0;
|
|
||||||
*(data.enumd.config) = enumval;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int configEnumSet(typeData data, sds value, char **err) {
|
|
||||||
int enumval = configEnumGetValue(data.enumd.enum_value, value);
|
|
||||||
if (enumval == INT_MIN) return 0;
|
|
||||||
if (data.enumd.is_valid_fn && !data.enumd.is_valid_fn(enumval, err))
|
if (data.enumd.is_valid_fn && !data.enumd.is_valid_fn(enumval, err))
|
||||||
return 0;
|
return 0;
|
||||||
int prev = *(data.enumd.config);
|
int prev = *(data.enumd.config);
|
||||||
*(data.enumd.config) = enumval;
|
*(data.enumd.config) = enumval;
|
||||||
if (data.enumd.update_fn && !data.enumd.update_fn(enumval, prev, err)) {
|
if (update && data.enumd.update_fn && !data.enumd.update_fn(enumval, prev, err)) {
|
||||||
*(data.enumd.config) = prev;
|
*(data.enumd.config) = prev;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void configEnumGet(client *c, typeData data) {
|
static void enumConfigGet(client *c, typeData data) {
|
||||||
addReplyBulkCString(c, configEnumGetNameOrUnknown(data.enumd.enum_value,*data.enumd.config));
|
addReplyBulkCString(c, configEnumGetNameOrUnknown(data.enumd.enum_value,*data.enumd.config));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void configEnumRewrite(typeData data, const char *name, struct rewriteConfigState *state) {
|
static void enumConfigRewrite(typeData data, const char *name, struct rewriteConfigState *state) {
|
||||||
rewriteConfigEnumOption(state, name,*(data.enumd.config), data.enumd.enum_value, data.enumd.default_value);
|
rewriteConfigEnumOption(state, name,*(data.enumd.config), data.enumd.enum_value, data.enumd.default_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define createEnumConfig(name, alias, modifiable, enum, config_addr, default, is_valid, update) { \
|
#define createEnumConfig(name, alias, modifiable, enum, config_addr, default, is_valid, update) { \
|
||||||
embedCommonConfig(name, alias, modifiable) \
|
embedCommonConfig(name, alias, modifiable) \
|
||||||
embedConfigInterface(configEnumInit, configEnumLoad, configEnumSet, configEnumGet, configEnumRewrite) \
|
embedConfigInterface(enumConfigInit, enumConfigSet, enumConfigGet, enumConfigRewrite) \
|
||||||
.data.enumd = { \
|
.data.enumd = { \
|
||||||
.config = &(config_addr), \
|
.config = &(config_addr), \
|
||||||
.default_value = (default), \
|
.default_value = (default), \
|
||||||
@ -1832,49 +1797,22 @@ static int numericBoundaryCheck(typeData data, long long ll, char **err) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int numericConfigLoad(typeData data, sds *argv, int argc, char **err) {
|
static int numericConfigSet(typeData data, sds value, int update, char **err) {
|
||||||
long long ll;
|
long long ll, prev = 0;
|
||||||
|
|
||||||
if (argc != 2) {
|
|
||||||
*err = "wrong number of arguments";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.numeric.is_memory) {
|
if (data.numeric.is_memory) {
|
||||||
int memerr;
|
int memerr;
|
||||||
ll = memtoll(argv[1], &memerr);
|
ll = memtoll(value, &memerr);
|
||||||
if (memerr || ll < 0) {
|
if (memerr || ll < 0) {
|
||||||
*err = "argument must be a memory value";
|
*err = "argument must be a memory value";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!string2ll(argv[1], sdslen(argv[1]),&ll)) {
|
if (!string2ll(value, sdslen(value),&ll)) {
|
||||||
*err = "argument couldn't be parsed into an integer" ;
|
*err = "argument couldn't be parsed into an integer" ;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!numericBoundaryCheck(data, ll, err))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (data.numeric.is_valid_fn && !data.numeric.is_valid_fn(ll, err))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
SET_NUMERIC_TYPE(ll)
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int numericConfigSet(typeData data, sds value, char **err) {
|
|
||||||
long long ll, prev = 0;
|
|
||||||
if (data.numeric.is_memory) {
|
|
||||||
int memerr;
|
|
||||||
ll = memtoll(value, &memerr);
|
|
||||||
if (memerr || ll < 0) return 0;
|
|
||||||
} else {
|
|
||||||
if (!string2ll(value, sdslen(value),&ll)) return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!numericBoundaryCheck(data, ll, err))
|
if (!numericBoundaryCheck(data, ll, err))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -1884,7 +1822,7 @@ static int numericConfigSet(typeData data, sds value, char **err) {
|
|||||||
GET_NUMERIC_TYPE(prev)
|
GET_NUMERIC_TYPE(prev)
|
||||||
SET_NUMERIC_TYPE(ll)
|
SET_NUMERIC_TYPE(ll)
|
||||||
|
|
||||||
if (data.numeric.update_fn && !data.numeric.update_fn(ll, prev, err)) {
|
if (update && data.numeric.update_fn && !data.numeric.update_fn(ll, prev, err)) {
|
||||||
SET_NUMERIC_TYPE(prev)
|
SET_NUMERIC_TYPE(prev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1918,7 +1856,7 @@ static void numericConfigRewrite(typeData data, const char *name, struct rewrite
|
|||||||
|
|
||||||
#define embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) { \
|
#define embedCommonNumericalConfig(name, alias, modifiable, lower, upper, config_addr, default, memory, is_valid, update) { \
|
||||||
embedCommonConfig(name, alias, modifiable) \
|
embedCommonConfig(name, alias, modifiable) \
|
||||||
embedConfigInterface(numericConfigInit, numericConfigLoad, numericConfigSet, numericConfigGet, numericConfigRewrite) \
|
embedConfigInterface(numericConfigInit, numericConfigSet, numericConfigGet, numericConfigRewrite) \
|
||||||
.data.numeric = { \
|
.data.numeric = { \
|
||||||
.lower_bound = (lower), \
|
.lower_bound = (lower), \
|
||||||
.upper_bound = (upper), \
|
.upper_bound = (upper), \
|
||||||
@ -2061,8 +1999,9 @@ static int updateMaxmemory(long long val, long long prev, char **err) {
|
|||||||
UNUSED(prev);
|
UNUSED(prev);
|
||||||
UNUSED(err);
|
UNUSED(err);
|
||||||
if (val) {
|
if (val) {
|
||||||
if ((unsigned long long)val < zmalloc_used_memory()) {
|
size_t used = zmalloc_used_memory()-freeMemoryGetNotCountedMemory();
|
||||||
serverLog(LL_WARNING,"WARNING: the new maxmemory value set via CONFIG SET is smaller than the current memory usage. This will result in key eviction and/or the inability to accept new write commands depending on the maxmemory-policy.");
|
if ((unsigned long long)val < used) {
|
||||||
|
serverLog(LL_WARNING,"WARNING: the new maxmemory value set via CONFIG SET (%llu) is smaller than the current memory usage (%zu). This will result in key eviction and/or the inability to accept new write commands depending on the maxmemory-policy.", server.maxmemory, used);
|
||||||
}
|
}
|
||||||
freeMemoryIfNeededAndSafe();
|
freeMemoryIfNeededAndSafe();
|
||||||
}
|
}
|
||||||
|
16
src/db.c
16
src/db.c
@ -1522,6 +1522,22 @@ int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numk
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Helper function to extract keys from memory command.
|
||||||
|
* MEMORY USAGE <key> */
|
||||||
|
int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||||
|
int *keys;
|
||||||
|
UNUSED(cmd);
|
||||||
|
|
||||||
|
if (argc >= 3 && !strcasecmp(argv[1]->ptr,"usage")) {
|
||||||
|
keys = zmalloc(sizeof(int) * 1);
|
||||||
|
keys[0] = 2;
|
||||||
|
*numkeys = 1;
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
*numkeys = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* XREAD [BLOCK <milliseconds>] [COUNT <count>] [GROUP <groupname> <ttl>]
|
/* XREAD [BLOCK <milliseconds>] [COUNT <count>] [GROUP <groupname> <ttl>]
|
||||||
* STREAMS key_1 key_2 ... key_N ID_1 ID_2 ... ID_N */
|
* STREAMS key_1 key_2 ... key_N ID_1 ID_2 ... ID_N */
|
||||||
int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||||
|
@ -355,6 +355,7 @@ void debugCommand(client *c) {
|
|||||||
"CRASH-AND-RECOVER <milliseconds> -- Hard crash and restart after <milliseconds> delay.",
|
"CRASH-AND-RECOVER <milliseconds> -- Hard crash and restart after <milliseconds> delay.",
|
||||||
"DIGEST -- Output a hex signature representing the current DB content.",
|
"DIGEST -- Output a hex signature representing the current DB content.",
|
||||||
"DIGEST-VALUE <key-1> ... <key-N>-- Output a hex signature of the values of all the specified keys.",
|
"DIGEST-VALUE <key-1> ... <key-N>-- Output a hex signature of the values of all the specified keys.",
|
||||||
|
"DEBUG PROTOCOL [string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false]",
|
||||||
"ERROR <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors.",
|
"ERROR <string> -- Return a Redis protocol error with <string> as message. Useful for clients unit tests to simulate Redis errors.",
|
||||||
"LOG <message> -- write message to the server log.",
|
"LOG <message> -- write message to the server log.",
|
||||||
"HTSTATS <dbid> -- Return hash table statistics of the specified Redis database.",
|
"HTSTATS <dbid> -- Return hash table statistics of the specified Redis database.",
|
||||||
@ -586,7 +587,7 @@ NULL
|
|||||||
}
|
}
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"protocol") && c->argc == 3) {
|
} else if (!strcasecmp(c->argv[1]->ptr,"protocol") && c->argc == 3) {
|
||||||
/* DEBUG PROTOCOL [string|integer|double|bignum|null|array|set|map|
|
/* DEBUG PROTOCOL [string|integer|double|bignum|null|array|set|map|
|
||||||
* attrib|push|verbatim|true|false|state|err|bloberr] */
|
* attrib|push|verbatim|true|false] */
|
||||||
char *name = c->argv[2]->ptr;
|
char *name = c->argv[2]->ptr;
|
||||||
if (!strcasecmp(name,"string")) {
|
if (!strcasecmp(name,"string")) {
|
||||||
addReplyBulkCString(c,"Hello World");
|
addReplyBulkCString(c,"Hello World");
|
||||||
@ -634,7 +635,7 @@ NULL
|
|||||||
} else if (!strcasecmp(name,"verbatim")) {
|
} else if (!strcasecmp(name,"verbatim")) {
|
||||||
addReplyVerbatim(c,"This is a verbatim\nstring",25,"txt");
|
addReplyVerbatim(c,"This is a verbatim\nstring",25,"txt");
|
||||||
} else {
|
} else {
|
||||||
addReplyError(c,"Wrong protocol type name. Please use one of the following: string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false|state|err|bloberr");
|
addReplyError(c,"Wrong protocol type name. Please use one of the following: string|integer|double|bignum|null|array|set|map|attrib|push|verbatim|true|false");
|
||||||
}
|
}
|
||||||
} else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) {
|
} else if (!strcasecmp(c->argv[1]->ptr,"sleep") && c->argc == 3) {
|
||||||
double dtime = strtod(c->argv[2]->ptr,NULL);
|
double dtime = strtod(c->argv[2]->ptr,NULL);
|
||||||
|
21
src/module.c
21
src/module.c
@ -714,9 +714,9 @@ void RM_KeyAtPos(RedisModuleCtx *ctx, int pos) {
|
|||||||
* flags into the command flags used by the Redis core.
|
* flags into the command flags used by the Redis core.
|
||||||
*
|
*
|
||||||
* It returns the set of flags, or -1 if unknown flags are found. */
|
* It returns the set of flags, or -1 if unknown flags are found. */
|
||||||
int commandFlagsFromString(char *s) {
|
int64_t commandFlagsFromString(char *s) {
|
||||||
int count, j;
|
int count, j;
|
||||||
int flags = 0;
|
int64_t flags = 0;
|
||||||
sds *tokens = sdssplitlen(s,strlen(s)," ",1,&count);
|
sds *tokens = sdssplitlen(s,strlen(s)," ",1,&count);
|
||||||
for (j = 0; j < count; j++) {
|
for (j = 0; j < count; j++) {
|
||||||
char *t = tokens[j];
|
char *t = tokens[j];
|
||||||
@ -798,7 +798,7 @@ int commandFlagsFromString(char *s) {
|
|||||||
* to authenticate a client.
|
* to authenticate a client.
|
||||||
*/
|
*/
|
||||||
int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) {
|
int RM_CreateCommand(RedisModuleCtx *ctx, const char *name, RedisModuleCmdFunc cmdfunc, const char *strflags, int firstkey, int lastkey, int keystep) {
|
||||||
int flags = strflags ? commandFlagsFromString((char*)strflags) : 0;
|
int64_t flags = strflags ? commandFlagsFromString((char*)strflags) : 0;
|
||||||
if (flags == -1) return REDISMODULE_ERR;
|
if (flags == -1) return REDISMODULE_ERR;
|
||||||
if ((flags & CMD_MODULE_NO_CLUSTER) && server.cluster_enabled)
|
if ((flags & CMD_MODULE_NO_CLUSTER) && server.cluster_enabled)
|
||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
@ -890,7 +890,8 @@ void RM_SetModuleOptions(RedisModuleCtx *ctx, int options) {
|
|||||||
ctx->module->options = options;
|
ctx->module->options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Signals that the key is modified from user's perspective (i.e. invalidate WATCH). */
|
/* Signals that the key is modified from user's perspective (i.e. invalidate WATCH
|
||||||
|
* and client side caching). */
|
||||||
int RM_SignalModifiedKey(RedisModuleCtx *ctx, RedisModuleString *keyname) {
|
int RM_SignalModifiedKey(RedisModuleCtx *ctx, RedisModuleString *keyname) {
|
||||||
signalModifiedKey(ctx->client->db,keyname);
|
signalModifiedKey(ctx->client->db,keyname);
|
||||||
return REDISMODULE_OK;
|
return REDISMODULE_OK;
|
||||||
@ -3649,14 +3650,15 @@ void moduleRDBLoadError(RedisModuleIO *io) {
|
|||||||
io->error = 1;
|
io->error = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
serverLog(LL_WARNING,
|
serverPanic(
|
||||||
"Error loading data from RDB (short read or EOF). "
|
"Error loading data from RDB (short read or EOF). "
|
||||||
"Read performed by module '%s' about type '%s' "
|
"Read performed by module '%s' about type '%s' "
|
||||||
"after reading '%llu' bytes of a value.",
|
"after reading '%llu' bytes of a value "
|
||||||
|
"for key named: '%s'.",
|
||||||
io->type->module->name,
|
io->type->module->name,
|
||||||
io->type->name,
|
io->type->name,
|
||||||
(unsigned long long)io->bytes);
|
(unsigned long long)io->bytes,
|
||||||
exit(1);
|
io->key? (char*)io->key->ptr: "(null)");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 0 if there's at least one registered data type that did not declare
|
/* Returns 0 if there's at least one registered data type that did not declare
|
||||||
@ -4805,7 +4807,8 @@ void moduleReleaseGIL(void) {
|
|||||||
* - REDISMODULE_NOTIFY_EXPIRED: Expiration events
|
* - REDISMODULE_NOTIFY_EXPIRED: Expiration events
|
||||||
* - REDISMODULE_NOTIFY_EVICTED: Eviction events
|
* - REDISMODULE_NOTIFY_EVICTED: Eviction events
|
||||||
* - REDISMODULE_NOTIFY_STREAM: Stream events
|
* - REDISMODULE_NOTIFY_STREAM: Stream events
|
||||||
* - REDISMODULE_NOTIFY_ALL: All events
|
* - REDISMODULE_NOTIFY_KEYMISS: Key-miss events
|
||||||
|
* - REDISMODULE_NOTIFY_ALL: All events (Excluding REDISMODULE_NOTIFY_KEYMISS)
|
||||||
*
|
*
|
||||||
* We do not distinguish between key events and keyspace events, and it is up
|
* We do not distinguish between key events and keyspace events, and it is up
|
||||||
* to the module to filter the actions taken based on the key.
|
* to the module to filter the actions taken based on the key.
|
||||||
|
@ -369,9 +369,10 @@ void addReplyErrorLength(client *c, const char *s, size_t len) {
|
|||||||
* Where the master must propagate the first change even if the second
|
* Where the master must propagate the first change even if the second
|
||||||
* will produce an error. However it is useful to log such events since
|
* will produce an error. However it is useful to log such events since
|
||||||
* they are rare and may hint at errors in a script or a bug in Redis. */
|
* they are rare and may hint at errors in a script or a bug in Redis. */
|
||||||
if (c->flags & (CLIENT_MASTER|CLIENT_SLAVE) && !(c->flags & CLIENT_MONITOR)) {
|
int ctype = getClientType(c);
|
||||||
char* to = c->flags & CLIENT_MASTER? "master": "replica";
|
if (ctype == CLIENT_TYPE_MASTER || ctype == CLIENT_TYPE_SLAVE) {
|
||||||
char* from = c->flags & CLIENT_MASTER? "replica": "master";
|
char* to = ctype == CLIENT_TYPE_MASTER? "master": "replica";
|
||||||
|
char* from = ctype == CLIENT_TYPE_MASTER? "replica": "master";
|
||||||
char *cmdname = c->lastcmd ? c->lastcmd->name : "<unknown>";
|
char *cmdname = c->lastcmd ? c->lastcmd->name : "<unknown>";
|
||||||
serverLog(LL_WARNING,"== CRITICAL == This %s is sending an error "
|
serverLog(LL_WARNING,"== CRITICAL == This %s is sending an error "
|
||||||
"to its %s: '%s' after processing the command "
|
"to its %s: '%s' after processing the command "
|
||||||
@ -1074,7 +1075,7 @@ void freeClient(client *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Log link disconnection with slave */
|
/* Log link disconnection with slave */
|
||||||
if ((c->flags & CLIENT_SLAVE) && !(c->flags & CLIENT_MONITOR)) {
|
if (getClientType(c) == CLIENT_TYPE_SLAVE) {
|
||||||
serverLog(LL_WARNING,"Connection with replica %s lost.",
|
serverLog(LL_WARNING,"Connection with replica %s lost.",
|
||||||
replicationGetSlaveName(c));
|
replicationGetSlaveName(c));
|
||||||
}
|
}
|
||||||
@ -1121,7 +1122,7 @@ void freeClient(client *c) {
|
|||||||
/* We need to remember the time when we started to have zero
|
/* We need to remember the time when we started to have zero
|
||||||
* attached slaves, as after some time we'll free the replication
|
* attached slaves, as after some time we'll free the replication
|
||||||
* backlog. */
|
* backlog. */
|
||||||
if (c->flags & CLIENT_SLAVE && listLength(server.slaves) == 0)
|
if (getClientType(c) == CLIENT_TYPE_SLAVE && listLength(server.slaves) == 0)
|
||||||
server.repl_no_slaves_since = server.unixtime;
|
server.repl_no_slaves_since = server.unixtime;
|
||||||
refreshGoodSlavesCount();
|
refreshGoodSlavesCount();
|
||||||
/* Fire the replica change modules event. */
|
/* Fire the replica change modules event. */
|
||||||
@ -1162,9 +1163,14 @@ void freeClientAsync(client *c) {
|
|||||||
* may access the list while Redis uses I/O threads. All the other accesses
|
* may access the list while Redis uses I/O threads. All the other accesses
|
||||||
* are in the context of the main thread while the other threads are
|
* are in the context of the main thread while the other threads are
|
||||||
* idle. */
|
* idle. */
|
||||||
static pthread_mutex_t async_free_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
if (c->flags & CLIENT_CLOSE_ASAP || c->flags & CLIENT_LUA) return;
|
if (c->flags & CLIENT_CLOSE_ASAP || c->flags & CLIENT_LUA) return;
|
||||||
c->flags |= CLIENT_CLOSE_ASAP;
|
c->flags |= CLIENT_CLOSE_ASAP;
|
||||||
|
if (server.io_threads_num == 1) {
|
||||||
|
/* no need to bother with locking if there's just one thread (the main thread) */
|
||||||
|
listAddNodeTail(server.clients_to_close,c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
static pthread_mutex_t async_free_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
pthread_mutex_lock(&async_free_queue_mutex);
|
pthread_mutex_lock(&async_free_queue_mutex);
|
||||||
listAddNodeTail(server.clients_to_close,c);
|
listAddNodeTail(server.clients_to_close,c);
|
||||||
pthread_mutex_unlock(&async_free_queue_mutex);
|
pthread_mutex_unlock(&async_free_queue_mutex);
|
||||||
@ -1252,8 +1258,8 @@ int writeToClient(client *c, int handler_installed) {
|
|||||||
* just deliver as much data as it is possible to deliver.
|
* just deliver as much data as it is possible to deliver.
|
||||||
*
|
*
|
||||||
* Moreover, we also send as much as possible if the client is
|
* Moreover, we also send as much as possible if the client is
|
||||||
* a slave (otherwise, on high-speed traffic, the replication
|
* a slave or a monitor (otherwise, on high-speed traffic, the
|
||||||
* buffer will grow indefinitely) */
|
* replication/output buffer will grow indefinitely) */
|
||||||
if (totwritten > NET_MAX_WRITES_PER_EVENT &&
|
if (totwritten > NET_MAX_WRITES_PER_EVENT &&
|
||||||
(server.maxmemory == 0 ||
|
(server.maxmemory == 0 ||
|
||||||
zmalloc_used_memory() < server.maxmemory) &&
|
zmalloc_used_memory() < server.maxmemory) &&
|
||||||
@ -1439,7 +1445,7 @@ int processInlineBuffer(client *c) {
|
|||||||
/* Newline from slaves can be used to refresh the last ACK time.
|
/* Newline from slaves can be used to refresh the last ACK time.
|
||||||
* This is useful for a slave to ping back while loading a big
|
* This is useful for a slave to ping back while loading a big
|
||||||
* RDB file. */
|
* RDB file. */
|
||||||
if (querylen == 0 && c->flags & CLIENT_SLAVE)
|
if (querylen == 0 && getClientType(c) == CLIENT_TYPE_SLAVE)
|
||||||
c->repl_ack_time = server.unixtime;
|
c->repl_ack_time = server.unixtime;
|
||||||
|
|
||||||
/* Move querybuffer position to the next query in the buffer. */
|
/* Move querybuffer position to the next query in the buffer. */
|
||||||
@ -2433,12 +2439,14 @@ unsigned long getClientOutputBufferMemoryUsage(client *c) {
|
|||||||
*
|
*
|
||||||
* The function will return one of the following:
|
* The function will return one of the following:
|
||||||
* CLIENT_TYPE_NORMAL -> Normal client
|
* CLIENT_TYPE_NORMAL -> Normal client
|
||||||
* CLIENT_TYPE_SLAVE -> Slave or client executing MONITOR command
|
* CLIENT_TYPE_SLAVE -> Slave
|
||||||
* CLIENT_TYPE_PUBSUB -> Client subscribed to Pub/Sub channels
|
* CLIENT_TYPE_PUBSUB -> Client subscribed to Pub/Sub channels
|
||||||
* CLIENT_TYPE_MASTER -> The client representing our replication master.
|
* CLIENT_TYPE_MASTER -> The client representing our replication master.
|
||||||
*/
|
*/
|
||||||
int getClientType(client *c) {
|
int getClientType(client *c) {
|
||||||
if (c->flags & CLIENT_MASTER) return CLIENT_TYPE_MASTER;
|
if (c->flags & CLIENT_MASTER) return CLIENT_TYPE_MASTER;
|
||||||
|
/* Even though MONITOR clients are marked as replicas, we
|
||||||
|
* want the expose them as normal clients. */
|
||||||
if ((c->flags & CLIENT_SLAVE) && !(c->flags & CLIENT_MONITOR))
|
if ((c->flags & CLIENT_SLAVE) && !(c->flags & CLIENT_MONITOR))
|
||||||
return CLIENT_TYPE_SLAVE;
|
return CLIENT_TYPE_SLAVE;
|
||||||
if (c->flags & CLIENT_PUBSUB) return CLIENT_TYPE_PUBSUB;
|
if (c->flags & CLIENT_PUBSUB) return CLIENT_TYPE_PUBSUB;
|
||||||
|
@ -82,10 +82,10 @@ sds keyspaceEventsFlagsToString(int flags) {
|
|||||||
if (flags & NOTIFY_EXPIRED) res = sdscatlen(res,"x",1);
|
if (flags & NOTIFY_EXPIRED) res = sdscatlen(res,"x",1);
|
||||||
if (flags & NOTIFY_EVICTED) res = sdscatlen(res,"e",1);
|
if (flags & NOTIFY_EVICTED) res = sdscatlen(res,"e",1);
|
||||||
if (flags & NOTIFY_STREAM) res = sdscatlen(res,"t",1);
|
if (flags & NOTIFY_STREAM) res = sdscatlen(res,"t",1);
|
||||||
if (flags & NOTIFY_KEY_MISS) res = sdscatlen(res,"m",1);
|
|
||||||
}
|
}
|
||||||
if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1);
|
if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1);
|
||||||
if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1);
|
if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1);
|
||||||
|
if (flags & NOTIFY_KEY_MISS) res = sdscatlen(res,"m",1);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
src/object.c
35
src/object.c
@ -974,38 +974,29 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
|
|||||||
mh->repl_backlog = mem;
|
mh->repl_backlog = mem;
|
||||||
mem_total += mem;
|
mem_total += mem;
|
||||||
|
|
||||||
mem = 0;
|
|
||||||
if (listLength(server.slaves)) {
|
|
||||||
listIter li;
|
|
||||||
listNode *ln;
|
|
||||||
|
|
||||||
listRewind(server.slaves,&li);
|
|
||||||
while((ln = listNext(&li))) {
|
|
||||||
client *c = listNodeValue(ln);
|
|
||||||
mem += getClientOutputBufferMemoryUsage(c);
|
|
||||||
mem += sdsAllocSize(c->querybuf);
|
|
||||||
mem += sizeof(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mh->clients_slaves = mem;
|
|
||||||
mem_total+=mem;
|
|
||||||
|
|
||||||
mem = 0;
|
mem = 0;
|
||||||
if (listLength(server.clients)) {
|
if (listLength(server.clients)) {
|
||||||
listIter li;
|
listIter li;
|
||||||
listNode *ln;
|
listNode *ln;
|
||||||
|
size_t mem_normal = 0, mem_slaves = 0;
|
||||||
|
|
||||||
listRewind(server.clients,&li);
|
listRewind(server.clients,&li);
|
||||||
while((ln = listNext(&li))) {
|
while((ln = listNext(&li))) {
|
||||||
|
size_t mem_curr = 0;
|
||||||
client *c = listNodeValue(ln);
|
client *c = listNodeValue(ln);
|
||||||
if (c->flags & CLIENT_SLAVE && !(c->flags & CLIENT_MONITOR))
|
int type = getClientType(c);
|
||||||
continue;
|
mem_curr += getClientOutputBufferMemoryUsage(c);
|
||||||
mem += getClientOutputBufferMemoryUsage(c);
|
mem_curr += sdsAllocSize(c->querybuf);
|
||||||
mem += sdsAllocSize(c->querybuf);
|
mem_curr += sizeof(client);
|
||||||
mem += sizeof(client);
|
if (type == CLIENT_TYPE_SLAVE)
|
||||||
|
mem_slaves += mem_curr;
|
||||||
|
else
|
||||||
|
mem_normal += mem_curr;
|
||||||
}
|
}
|
||||||
|
mh->clients_slaves = mem_slaves;
|
||||||
|
mh->clients_normal = mem_normal;
|
||||||
|
mem = mem_slaves + mem_normal;
|
||||||
}
|
}
|
||||||
mh->clients_normal = mem;
|
|
||||||
mem_total+=mem;
|
mem_total+=mem;
|
||||||
|
|
||||||
mem = 0;
|
mem = 0;
|
||||||
|
@ -1871,7 +1871,10 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
|
|||||||
}
|
}
|
||||||
} else if (rdbtype == RDB_TYPE_MODULE || rdbtype == RDB_TYPE_MODULE_2) {
|
} else if (rdbtype == RDB_TYPE_MODULE || rdbtype == RDB_TYPE_MODULE_2) {
|
||||||
uint64_t moduleid = rdbLoadLen(rdb,NULL);
|
uint64_t moduleid = rdbLoadLen(rdb,NULL);
|
||||||
if (rioGetReadError(rdb)) return NULL;
|
if (rioGetReadError(rdb)) {
|
||||||
|
rdbReportReadError("Short read module id");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
moduleType *mt = moduleTypeLookupModuleByID(moduleid);
|
moduleType *mt = moduleTypeLookupModuleByID(moduleid);
|
||||||
char name[10];
|
char name[10];
|
||||||
|
|
||||||
|
@ -1595,15 +1595,15 @@ static int parseOptions(int argc, char **argv) {
|
|||||||
#ifdef USE_OPENSSL
|
#ifdef USE_OPENSSL
|
||||||
} else if (!strcmp(argv[i],"--tls")) {
|
} else if (!strcmp(argv[i],"--tls")) {
|
||||||
config.tls = 1;
|
config.tls = 1;
|
||||||
} else if (!strcmp(argv[i],"--sni")) {
|
} else if (!strcmp(argv[i],"--sni") && !lastarg) {
|
||||||
config.sni = argv[++i];
|
config.sni = argv[++i];
|
||||||
} else if (!strcmp(argv[i],"--cacertdir")) {
|
} else if (!strcmp(argv[i],"--cacertdir") && !lastarg) {
|
||||||
config.cacertdir = argv[++i];
|
config.cacertdir = argv[++i];
|
||||||
} else if (!strcmp(argv[i],"--cacert")) {
|
} else if (!strcmp(argv[i],"--cacert") && !lastarg) {
|
||||||
config.cacert = argv[++i];
|
config.cacert = argv[++i];
|
||||||
} else if (!strcmp(argv[i],"--cert")) {
|
} else if (!strcmp(argv[i],"--cert") && !lastarg) {
|
||||||
config.cert = argv[++i];
|
config.cert = argv[++i];
|
||||||
} else if (!strcmp(argv[i],"--key")) {
|
} else if (!strcmp(argv[i],"--key") && !lastarg) {
|
||||||
config.key = argv[++i];
|
config.key = argv[++i];
|
||||||
#endif
|
#endif
|
||||||
} else if (!strcmp(argv[i],"-v") || !strcmp(argv[i], "--version")) {
|
} else if (!strcmp(argv[i],"-v") || !strcmp(argv[i], "--version")) {
|
||||||
@ -1701,12 +1701,13 @@ static void usage(void) {
|
|||||||
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
|
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
|
||||||
#ifdef USE_OPENSSL
|
#ifdef USE_OPENSSL
|
||||||
" --tls Establish a secure TLS connection.\n"
|
" --tls Establish a secure TLS connection.\n"
|
||||||
" --cacert CA Certificate file to verify with.\n"
|
" --sni <host> Server name indication for TLS.\n"
|
||||||
" --cacertdir Directory where trusted CA certificates are stored.\n"
|
" --cacert <file> CA Certificate file to verify with.\n"
|
||||||
|
" --cacertdir <dir> Directory where trusted CA certificates are stored.\n"
|
||||||
" If neither cacert nor cacertdir are specified, the default\n"
|
" If neither cacert nor cacertdir are specified, the default\n"
|
||||||
" system-wide trusted root certs configuration will apply.\n"
|
" system-wide trusted root certs configuration will apply.\n"
|
||||||
" --cert Client certificate to authenticate with.\n"
|
" --cert <file> Client certificate to authenticate with.\n"
|
||||||
" --key Private key file to authenticate with.\n"
|
" --key <file> Private key file to authenticate with.\n"
|
||||||
#endif
|
#endif
|
||||||
" --raw Use raw formatting for replies (default when STDOUT is\n"
|
" --raw Use raw formatting for replies (default when STDOUT is\n"
|
||||||
" not a tty).\n"
|
" not a tty).\n"
|
||||||
|
@ -127,8 +127,8 @@
|
|||||||
#define REDISMODULE_NOTIFY_EXPIRED (1<<8) /* x */
|
#define REDISMODULE_NOTIFY_EXPIRED (1<<8) /* x */
|
||||||
#define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */
|
#define REDISMODULE_NOTIFY_EVICTED (1<<9) /* e */
|
||||||
#define REDISMODULE_NOTIFY_STREAM (1<<10) /* t */
|
#define REDISMODULE_NOTIFY_STREAM (1<<10) /* t */
|
||||||
#define REDISMODULE_NOTIFY_KEY_MISS (1<<11) /* m */
|
#define REDISMODULE_NOTIFY_KEY_MISS (1<<11) /* m (Note: This one is excluded from REDISMODULE_NOTIFY_ALL on purpose) */
|
||||||
#define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM | REDISMODULE_NOTIFY_KEY_MISS) /* A */
|
#define REDISMODULE_NOTIFY_ALL (REDISMODULE_NOTIFY_GENERIC | REDISMODULE_NOTIFY_STRING | REDISMODULE_NOTIFY_LIST | REDISMODULE_NOTIFY_SET | REDISMODULE_NOTIFY_HASH | REDISMODULE_NOTIFY_ZSET | REDISMODULE_NOTIFY_EXPIRED | REDISMODULE_NOTIFY_EVICTED | REDISMODULE_NOTIFY_STREAM) /* A */
|
||||||
|
|
||||||
|
|
||||||
/* A special pointer that we can use between the core and the module to signal
|
/* A special pointer that we can use between the core and the module to signal
|
||||||
|
@ -1354,7 +1354,7 @@ void disklessLoadRestoreBackups(redisDb *backup, int restore, int empty_db_flags
|
|||||||
void readSyncBulkPayload(connection *conn) {
|
void readSyncBulkPayload(connection *conn) {
|
||||||
char buf[4096];
|
char buf[4096];
|
||||||
ssize_t nread, readlen, nwritten;
|
ssize_t nread, readlen, nwritten;
|
||||||
int use_diskless_load;
|
int use_diskless_load = useDisklessLoad();
|
||||||
redisDb *diskless_load_backup = NULL;
|
redisDb *diskless_load_backup = NULL;
|
||||||
int empty_db_flags = server.repl_slave_lazy_flush ? EMPTYDB_ASYNC :
|
int empty_db_flags = server.repl_slave_lazy_flush ? EMPTYDB_ASYNC :
|
||||||
EMPTYDB_NO_FLAGS;
|
EMPTYDB_NO_FLAGS;
|
||||||
@ -1411,19 +1411,18 @@ void readSyncBulkPayload(connection *conn) {
|
|||||||
server.repl_transfer_size = 0;
|
server.repl_transfer_size = 0;
|
||||||
serverLog(LL_NOTICE,
|
serverLog(LL_NOTICE,
|
||||||
"MASTER <-> REPLICA sync: receiving streamed RDB from master with EOF %s",
|
"MASTER <-> REPLICA sync: receiving streamed RDB from master with EOF %s",
|
||||||
useDisklessLoad()? "to parser":"to disk");
|
use_diskless_load? "to parser":"to disk");
|
||||||
} else {
|
} else {
|
||||||
usemark = 0;
|
usemark = 0;
|
||||||
server.repl_transfer_size = strtol(buf+1,NULL,10);
|
server.repl_transfer_size = strtol(buf+1,NULL,10);
|
||||||
serverLog(LL_NOTICE,
|
serverLog(LL_NOTICE,
|
||||||
"MASTER <-> REPLICA sync: receiving %lld bytes from master %s",
|
"MASTER <-> REPLICA sync: receiving %lld bytes from master %s",
|
||||||
(long long) server.repl_transfer_size,
|
(long long) server.repl_transfer_size,
|
||||||
useDisklessLoad()? "to parser":"to disk");
|
use_diskless_load? "to parser":"to disk");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
use_diskless_load = useDisklessLoad();
|
|
||||||
if (!use_diskless_load) {
|
if (!use_diskless_load) {
|
||||||
/* Read the data from the socket, store it to a file and search
|
/* Read the data from the socket, store it to a file and search
|
||||||
* for the EOF. */
|
* for the EOF. */
|
||||||
@ -2399,6 +2398,10 @@ void replicationUnsetMaster(void) {
|
|||||||
moduleFireServerEvent(REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,
|
moduleFireServerEvent(REDISMODULE_EVENT_REPLICATION_ROLE_CHANGED,
|
||||||
REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER,
|
REDISMODULE_EVENT_REPLROLECHANGED_NOW_MASTER,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
/* Restart the AOF subsystem in case we shut it down during a sync when
|
||||||
|
* we were still a slave. */
|
||||||
|
if (server.aof_enabled && server.aof_state == AOF_OFF) restartAOFAfterSYNC();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function is called when the slave lose the connection with the
|
/* This function is called when the slave lose the connection with the
|
||||||
@ -2436,9 +2439,6 @@ void replicaofCommand(client *c) {
|
|||||||
serverLog(LL_NOTICE,"MASTER MODE enabled (user request from '%s')",
|
serverLog(LL_NOTICE,"MASTER MODE enabled (user request from '%s')",
|
||||||
client);
|
client);
|
||||||
sdsfree(client);
|
sdsfree(client);
|
||||||
/* Restart the AOF subsystem in case we shut it down during a sync when
|
|
||||||
* we were still a slave. */
|
|
||||||
if (server.aof_enabled && server.aof_state == AOF_OFF) restartAOFAfterSYNC();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
long port;
|
long port;
|
||||||
|
20
src/server.c
20
src/server.c
@ -579,7 +579,7 @@ struct redisCommand redisCommandTable[] = {
|
|||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"select",selectCommand,2,
|
{"select",selectCommand,2,
|
||||||
"ok-loading fast @keyspace",
|
"ok-loading fast ok-stale @keyspace",
|
||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"swapdb",swapdbCommand,3,
|
{"swapdb",swapdbCommand,3,
|
||||||
@ -660,7 +660,7 @@ struct redisCommand redisCommandTable[] = {
|
|||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"lastsave",lastsaveCommand,1,
|
{"lastsave",lastsaveCommand,1,
|
||||||
"read-only random fast @admin @dangerous",
|
"read-only random fast ok-loading ok-stale @admin @dangerous",
|
||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"type",typeCommand,2,
|
{"type",typeCommand,2,
|
||||||
@ -708,7 +708,7 @@ struct redisCommand redisCommandTable[] = {
|
|||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"monitor",monitorCommand,1,
|
{"monitor",monitorCommand,1,
|
||||||
"admin no-script",
|
"admin no-script ok-loading ok-stale",
|
||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"ttl",ttlCommand,2,
|
{"ttl",ttlCommand,2,
|
||||||
@ -740,7 +740,7 @@ struct redisCommand redisCommandTable[] = {
|
|||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"debug",debugCommand,-2,
|
{"debug",debugCommand,-2,
|
||||||
"admin no-script",
|
"admin no-script ok-loading ok-stale",
|
||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"config",configCommand,-2,
|
{"config",configCommand,-2,
|
||||||
@ -817,14 +817,14 @@ struct redisCommand redisCommandTable[] = {
|
|||||||
|
|
||||||
{"memory",memoryCommand,-2,
|
{"memory",memoryCommand,-2,
|
||||||
"random read-only",
|
"random read-only",
|
||||||
0,NULL,0,0,0,0,0,0},
|
0,memoryGetKeys,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"client",clientCommand,-2,
|
{"client",clientCommand,-2,
|
||||||
"admin no-script random @connection",
|
"admin no-script random ok-loading ok-stale @connection",
|
||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"hello",helloCommand,-2,
|
{"hello",helloCommand,-2,
|
||||||
"no-auth no-script fast no-monitor no-slowlog @connection",
|
"no-auth no-script fast no-monitor ok-loading ok-stale no-slowlog @connection",
|
||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
/* EVAL can modify the dataset, however it is not flagged as a write
|
/* EVAL can modify the dataset, however it is not flagged as a write
|
||||||
@ -838,7 +838,7 @@ struct redisCommand redisCommandTable[] = {
|
|||||||
0,evalGetKeys,0,0,0,0,0,0},
|
0,evalGetKeys,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"slowlog",slowlogCommand,-2,
|
{"slowlog",slowlogCommand,-2,
|
||||||
"admin random",
|
"admin random ok-loading ok-stale",
|
||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"script",scriptCommand,-2,
|
{"script",scriptCommand,-2,
|
||||||
@ -846,7 +846,7 @@ struct redisCommand redisCommandTable[] = {
|
|||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"time",timeCommand,1,
|
{"time",timeCommand,1,
|
||||||
"read-only random fast",
|
"read-only random fast ok-loading ok-stale",
|
||||||
0,NULL,0,0,0,0,0,0},
|
0,NULL,0,0,0,0,0,0},
|
||||||
|
|
||||||
{"bitop",bitopCommand,-4,
|
{"bitop",bitopCommand,-4,
|
||||||
@ -1498,7 +1498,7 @@ int clientsCronHandleTimeout(client *c, mstime_t now_ms) {
|
|||||||
time_t now = now_ms/1000;
|
time_t now = now_ms/1000;
|
||||||
|
|
||||||
if (server.maxidletime &&
|
if (server.maxidletime &&
|
||||||
!(c->flags & CLIENT_SLAVE) && /* no timeout for slaves */
|
!(c->flags & CLIENT_SLAVE) && /* no timeout for slaves and monitors */
|
||||||
!(c->flags & CLIENT_MASTER) && /* no timeout for masters */
|
!(c->flags & CLIENT_MASTER) && /* no timeout for masters */
|
||||||
!(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 */
|
||||||
|
@ -413,8 +413,8 @@ typedef long long ustime_t; /* microsecond time type. */
|
|||||||
#define NOTIFY_EXPIRED (1<<8) /* x */
|
#define NOTIFY_EXPIRED (1<<8) /* x */
|
||||||
#define NOTIFY_EVICTED (1<<9) /* e */
|
#define NOTIFY_EVICTED (1<<9) /* e */
|
||||||
#define NOTIFY_STREAM (1<<10) /* t */
|
#define NOTIFY_STREAM (1<<10) /* t */
|
||||||
#define NOTIFY_KEY_MISS (1<<11) /* m */
|
#define NOTIFY_KEY_MISS (1<<11) /* m (Note: This one is excluded from NOTIFY_ALL on purpose) */
|
||||||
#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM | NOTIFY_KEY_MISS) /* A flag */
|
#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM) /* A flag */
|
||||||
|
|
||||||
/* Get the first bind addr or NULL */
|
/* Get the first bind addr or NULL */
|
||||||
#define NET_FIRST_BIND_ADDR (server.bindaddr_count ? server.bindaddr[0] : NULL)
|
#define NET_FIRST_BIND_ADDR (server.bindaddr_count ? server.bindaddr[0] : NULL)
|
||||||
@ -2077,6 +2077,7 @@ int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
|||||||
int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||||
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||||
int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||||
|
int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||||
|
|
||||||
/* Cluster */
|
/* Cluster */
|
||||||
void clusterInit(void);
|
void clusterInit(void);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user