Merge 'origin/unstable' into 7.0 for 7.0.3

This commit is contained in:
Oran Agra
2022-07-11 15:22:06 +03:00
84 changed files with 1098 additions and 292 deletions
+1 -1
View File
@@ -93,4 +93,4 @@ Community Impact Guidelines were inspired by Mozilla’s code of conduct
enforcement ladder.
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
https://www.contributor-covenant.org/translations.
View File
+2 -2
View File
@@ -223,11 +223,11 @@ public discussion groups, you agree to release your code under the terms
of the BSD license that you can find in the [COPYING][1] file included in the Redis
source distribution.
Please see the [CONTRIBUTING][2] file in this source distribution for more
Please see the [CONTRIBUTING.md][2] file in this source distribution for more
information. For security bugs and vulnerabilities, please see [SECURITY.md][3].
[1]: https://github.com/redis/redis/blob/unstable/COPYING
[2]: https://github.com/redis/redis/blob/unstable/CONTRIBUTING
[2]: https://github.com/redis/redis/blob/unstable/CONTRIBUTING.md
[3]: https://github.com/redis/redis/blob/unstable/SECURITY.md
Redis internals
+1 -1
View File
@@ -57,7 +57,7 @@ typedef enum {
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED, /* Failed to load CA Certificate or CA Path */
REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED, /* Failed to load client certificate */
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED, /* Failed to load private key */
REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED, /* Failed to open system certifcate store */
REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED, /* Failed to open system certificate store */
REDIS_SSL_CTX_OS_CERT_ADD_FAILED /* Failed to add CA certificates obtained from system to the SSL context */
} redisSSLContextError;
+1 -1
View File
@@ -184,7 +184,7 @@ const char *redisSSLContextGetError(redisSSLContextError error)
case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED:
return "Failed to load private key";
case REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED:
return "Failed to open system certifcate store";
return "Failed to open system certificate store";
case REDIS_SSL_CTX_OS_CERT_ADD_FAILED:
return "Failed to add CA certificates obtained from system to the SSL context";
default:
+1 -1
View File
@@ -1450,7 +1450,7 @@ appendfsync everysec
# BGSAVE or BGREWRITEAOF is in progress.
#
# This means that while another child is saving, the durability of Redis is
# the same as "appendfsync none". In practical terms, this means that it is
# the same as "appendfsync no". In practical terms, this means that it is
# possible to lose up to 30 seconds of log in the worst scenario (with the
# default Linux settings).
#
+3 -1
View File
@@ -286,7 +286,9 @@ sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
# REDIS COMMANDS RENAMING
# REDIS COMMANDS RENAMING (DEPRECATED)
#
# WARNING: avoid using this option if possible, instead use ACLs.
#
# Sometimes the Redis server has certain commands, that are needed for Sentinel
# to work correctly, renamed to unguessable strings. This is often the case
+16 -6
View File
@@ -2169,15 +2169,25 @@ sds ACLLoadFromFile(const char *filename) {
server.acl_filename, linenum);
}
int j;
for (j = 0; j < merged_argc; j++) {
int syntax_error = 0;
for (int j = 0; j < merged_argc; j++) {
acl_args[j] = sdstrim(acl_args[j],"\t\r\n");
if (ACLSetUser(u,acl_args[j],sdslen(acl_args[j])) != C_OK) {
const char *errmsg = ACLSetUserStringError();
errors = sdscatprintf(errors,
"%s:%d: %s. ",
server.acl_filename, linenum, errmsg);
continue;
if (errno == ENOENT) {
/* For missing commands, we print out more information since
* it shouldn't contain any sensitive information. */
errors = sdscatprintf(errors,
"%s:%d: Error in applying operation '%s': %s. ",
server.acl_filename, linenum, acl_args[j], errmsg);
} else if (syntax_error == 0) {
/* For all other errors, only print out the first error encountered
* since it might affect future operations. */
errors = sdscatprintf(errors,
"%s:%d: %s. ",
server.acl_filename, linenum, errmsg);
syntax_error = 1;
}
}
}
+22 -2
View File
@@ -572,6 +572,16 @@ int writeAofManifestFile(sds buf) {
tmp_am_name, am_name, strerror(errno));
ret = C_ERR;
goto cleanup;
}
/* Also sync the AOF directory as new AOF files may be added in the directory */
if (fsyncFileDir(am_filepath) == -1) {
serverLog(LL_WARNING, "Fail to fsync AOF directory %s: %s.",
am_filepath, strerror(errno));
ret = C_ERR;
goto cleanup;
}
cleanup:
@@ -1122,8 +1132,8 @@ void flushAppendOnlyFile(int force) {
if (can_log) {
serverLog(LL_WARNING,"Error writing to the AOF file: %s",
strerror(errno));
server.aof_last_write_errno = errno;
}
server.aof_last_write_errno = errno;
} else {
if (can_log) {
serverLog(LL_WARNING,"Short write while writing to "
@@ -2398,6 +2408,7 @@ int rewriteAppendOnlyFileBackground(void) {
if (dirCreateIfMissing(server.aof_dirname) == -1) {
serverLog(LL_WARNING, "Can't open or create append-only dir %s: %s",
server.aof_dirname, strerror(errno));
server.aof_lastbgrewrite_status = C_ERR;
return C_ERR;
}
@@ -2405,7 +2416,10 @@ int rewriteAppendOnlyFileBackground(void) {
* feedAppendOnlyFile() to issue a SELECT command. */
server.aof_selected_db = -1;
flushAppendOnlyFile(1);
if (openNewIncrAofForAppend() != C_OK) return C_ERR;
if (openNewIncrAofForAppend() != C_OK) {
server.aof_lastbgrewrite_status = C_ERR;
return C_ERR;
}
server.stat_aof_rewrites++;
if ((childpid = redisFork(CHILD_TYPE_AOF)) == 0) {
char tmpfile[256];
@@ -2563,6 +2577,8 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
strerror(errno));
aofManifestFree(temp_am);
sdsfree(new_base_filepath);
server.aof_lastbgrewrite_status = C_ERR;
server.stat_aofrw_consecutive_failures++;
goto cleanup;
}
latencyEndMonitor(latency);
@@ -2591,6 +2607,8 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
sdsfree(temp_incr_filepath);
sdsfree(new_incr_filepath);
sdsfree(temp_incr_aof_name);
server.aof_lastbgrewrite_status = C_ERR;
server.stat_aofrw_consecutive_failures++;
goto cleanup;
}
latencyEndMonitor(latency);
@@ -2614,6 +2632,8 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
bg_unlink(new_incr_filepath);
sdsfree(new_incr_filepath);
}
server.aof_lastbgrewrite_status = C_ERR;
server.stat_aofrw_consecutive_failures++;
goto cleanup;
}
sdsfree(new_base_filepath);
+36 -32
View File
@@ -308,8 +308,10 @@ int clusterLoadConfig(char *filename) {
if (atoi(argv[4])) n->ping_sent = mstime();
if (atoi(argv[5])) n->pong_received = mstime();
/* Set configEpoch for this node. */
n->configEpoch = strtoull(argv[6],NULL,10);
/* Set configEpoch for this node.
* If the node is a replica, set its config epoch to 0.
* If it's a primary, load the config epoch from the configuration file. */
n->configEpoch = (nodeIsSlave(n) && n->slaveof) ? 0 : strtoull(argv[6],NULL,10);
/* Populate hash slots served by this instance. */
for (j = 8; j < argc; j++) {
@@ -458,8 +460,8 @@ void clusterSaveConfigOrDie(int do_fsync) {
}
}
/* Lock the cluster config using flock(), and leaks the file descriptor used to
* acquire the lock so that the file will be locked forever.
/* Lock the cluster config using flock(), and retain the file descriptor used to
* acquire the lock so that the file will be locked as long as the process is up.
*
* This works because we always update nodes.conf with a new version
* in-place, reopening the file, and writing to it in place (later adjusting
@@ -498,8 +500,8 @@ int clusterLockConfig(char *filename) {
close(fd);
return C_ERR;
}
/* Lock acquired: leak the 'fd' by not closing it, so that we'll retain the
* lock to the file as long as the process exists.
/* Lock acquired: leak the 'fd' by not closing it until shutdown time, so that
* we'll retain the lock to the file as long as the process exists.
*
* After fork, the child process will get the fd opened by the parent process,
* we need save `fd` to `cluster_config_file_lock_fd`, so that in redisFork(),
@@ -964,6 +966,7 @@ clusterNode *createClusterNode(char *nodename, int flags) {
node->numslaves = 0;
node->slaves = NULL;
node->slaveof = NULL;
node->last_in_ping_gossip = 0;
node->ping_sent = node->pong_received = 0;
node->data_received = 0;
node->fail_time = 0;
@@ -1217,10 +1220,14 @@ clusterNode *clusterLookupNode(const char *name, int length) {
return dictGetVal(de);
}
/* Get all the nodes serving the same slots as myself. */
/* Get all the nodes serving the same slots as the given node. */
list *clusterGetNodesServingMySlots(clusterNode *node) {
list *nodes_for_slot = listCreate();
clusterNode *my_primary = nodeIsMaster(node) ? node : node->slaveof;
/* This function is only valid for fully connected nodes, so
* they should have a known primary. */
serverAssert(my_primary);
listAddNodeTail(nodes_for_slot, my_primary);
for (int i=0; i < my_primary->numslaves; i++) {
listAddNodeTail(nodes_for_slot, my_primary->slaves[i]);
@@ -1999,7 +2006,7 @@ int writeHostnamePingExt(clusterMsgPingExt **cursor) {
uint32_t extension_size = getHostnamePingExtSize();
/* Move the write cursor */
(*cursor)->type = CLUSTERMSG_EXT_TYPE_HOSTNAME;
(*cursor)->type = htons(CLUSTERMSG_EXT_TYPE_HOSTNAME);
(*cursor)->length = htonl(extension_size);
/* Make sure the string is NULL terminated by adding 1 */
*cursor = (clusterMsgPingExt *) (ext->hostname + EIGHT_BYTE_ALIGN(sdslen(myself->hostname) + 1));
@@ -2847,18 +2854,6 @@ void clusterBuildMessageHdr(clusterMsg *hdr, int type) {
* totlen field is up to the caller. */
}
/* Return non zero if the node is already present in the gossip section of the
* message pointed by 'hdr' and having 'count' gossip entries. Otherwise
* zero is returned. Helper for clusterSendPing(). */
int clusterNodeIsInGossipSection(clusterMsg *hdr, int count, clusterNode *n) {
int j;
for (j = 0; j < count; j++) {
if (memcmp(hdr->data.ping.gossip[j].nodename,n->name,
CLUSTER_NAMELEN) == 0) break;
}
return j != count;
}
/* Set the i-th entry of the gossip section in the message pointed by 'hdr'
* to the info of the specified node 'n'. */
void clusterSetGossipEntry(clusterMsg *hdr, int i, clusterNode *n) {
@@ -2878,6 +2873,8 @@ void clusterSetGossipEntry(clusterMsg *hdr, int i, clusterNode *n) {
/* Send a PING or PONG packet to the specified node, making sure to add enough
* gossip information. */
void clusterSendPing(clusterLink *link, int type) {
static unsigned long long cluster_pings_sent = 0;
cluster_pings_sent++;
unsigned char *buf;
clusterMsg *hdr;
int gossipcount = 0; /* Number of gossip sections added so far. */
@@ -2928,7 +2925,7 @@ void clusterSendPing(clusterLink *link, int type) {
* to put inside the packet. */
estlen = sizeof(clusterMsg) - sizeof(union clusterMsgData);
estlen += (sizeof(clusterMsgDataGossip)*(wanted + pfail_wanted));
estlen += sizeof(clusterMsgPingExt) + getHostnamePingExtSize();
estlen += getHostnamePingExtSize();
/* Note: clusterBuildMessageHdr() expects the buffer to be always at least
* sizeof(clusterMsg) or more. */
@@ -2967,10 +2964,11 @@ void clusterSendPing(clusterLink *link, int type) {
}
/* Do not add a node we already have. */
if (clusterNodeIsInGossipSection(hdr,gossipcount,this)) continue;
if (this->last_in_ping_gossip == cluster_pings_sent) continue;
/* Add it */
clusterSetGossipEntry(hdr,gossipcount,this);
this->last_in_ping_gossip = cluster_pings_sent;
freshnodes--;
gossipcount++;
}
@@ -2987,7 +2985,6 @@ void clusterSendPing(clusterLink *link, int type) {
if (node->flags & CLUSTER_NODE_NOADDR) continue;
if (!(node->flags & CLUSTER_NODE_PFAIL)) continue;
clusterSetGossipEntry(hdr,gossipcount,node);
freshnodes--;
gossipcount++;
/* We take the count of the slots we allocated, since the
* PFAIL stats may not match perfectly with the current number
@@ -5110,7 +5107,7 @@ void clusterReplyShards(client *c) {
* information and an empty slots array. */
while((de = dictNext(di)) != NULL) {
clusterNode *n = dictGetVal(de);
if (nodeIsSlave(n)) {
if (!nodeIsMaster(n)) {
/* You can force a replica to own slots, even though it'll get reverted,
* so freeing the slot pair here just in case. */
clusterFreeNodesSlotsInfo(n);
@@ -6104,7 +6101,7 @@ migrateCachedSocket* migrateGetSocket(client *c, robj *host, robj *port, long ti
/* Create the socket */
conn = server.tls_cluster ? connCreateTLS() : connCreateSocket();
if (connBlockingConnect(conn, c->argv[1]->ptr, atoi(c->argv[2]->ptr), timeout)
if (connBlockingConnect(conn, host->ptr, atoi(port->ptr), timeout)
!= C_OK) {
addReplyError(c,"-IOERR error or timeout connecting to the client");
connClose(conn);
@@ -6583,7 +6580,8 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
int multiple_keys = 0;
multiState *ms, _ms;
multiCmd mc;
int i, slot = 0, migrating_slot = 0, importing_slot = 0, missing_keys = 0;
int i, slot = 0, migrating_slot = 0, importing_slot = 0, missing_keys = 0,
existing_keys = 0;
/* Allow any key to be set if a module disabled cluster redirections. */
if (server.cluster_module_flags & CLUSTER_MODULE_FLAG_NO_REDIRECTION)
@@ -6695,10 +6693,10 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
* node until the migration completes with CLUSTER SETSLOT <slot>
* NODE <node-id>. */
int flags = LOOKUP_NOTOUCH | LOOKUP_NOSTATS | LOOKUP_NONOTIFY;
if ((migrating_slot || importing_slot) && !is_pubsubshard &&
lookupKeyReadWithFlags(&server.db[0], thiskey, flags) == NULL)
if ((migrating_slot || importing_slot) && !is_pubsubshard)
{
missing_keys++;
if (lookupKeyReadWithFlags(&server.db[0], thiskey, flags) == NULL) missing_keys++;
else existing_keys++;
}
}
getKeysFreeResult(&result);
@@ -6742,10 +6740,16 @@ clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, in
return myself;
/* If we don't have all the keys and we are migrating the slot, send
* an ASK redirection. */
* an ASK redirection or TRYAGAIN. */
if (migrating_slot && missing_keys) {
if (error_code) *error_code = CLUSTER_REDIR_ASK;
return server.cluster->migrating_slots_to[slot];
/* If we have keys but we don't have all keys, we return TRYAGAIN */
if (existing_keys) {
if (error_code) *error_code = CLUSTER_REDIR_UNSTABLE;
return NULL;
} else {
if (error_code) *error_code = CLUSTER_REDIR_ASK;
return server.cluster->migrating_slots_to[slot];
}
}
/* If we are receiving the slot, and the client correctly flagged the
+1
View File
@@ -127,6 +127,7 @@ typedef struct clusterNode {
may be NULL even if the node is a slave
if we don't have the master node in our
tables. */
unsigned long long last_in_ping_gossip; /* The number of the last carried in the ping gossip section */
mstime_t ping_sent; /* Unix time we sent latest ping */
mstime_t pong_received; /* Unix time we received the pong */
mstime_t data_received; /* Unix time we received any data */
+32 -28
View File
@@ -470,7 +470,10 @@ NULL
/********** CLUSTER MEET ********************/
/* CLUSTER MEET history */
#define CLUSTER_MEET_History NULL
commandHistory CLUSTER_MEET_History[] = {
{"4.0.0","Added the optional `cluster_bus_port` argument."},
{0}
};
/* CLUSTER MEET tips */
const char *CLUSTER_MEET_tips[] = {
@@ -482,6 +485,7 @@ NULL
struct redisCommandArg CLUSTER_MEET_Args[] = {
{"ip",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
{"cluster_bus_port",ARG_TYPE_INTEGER,-1,NULL,NULL,"4.0.0",CMD_ARG_OPTIONAL},
{0}
};
@@ -685,7 +689,7 @@ struct redisCommand CLUSTER_Subcommands[] = {
{"nodes","Get Cluster config for the node","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_NODES_History,CLUSTER_NODES_tips,clusterCommand,2,CMD_STALE,0},
{"replicas","List replica nodes of the specified master node","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_REPLICAS_History,CLUSTER_REPLICAS_tips,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_REPLICAS_Args},
{"replicate","Reconfigure a node as a replica of the specified master node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_REPLICATE_History,CLUSTER_REPLICATE_tips,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_REPLICATE_Args},
{"reset","Reset a Redis Cluster node","O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_RESET_History,CLUSTER_RESET_tips,clusterCommand,3,CMD_ADMIN|CMD_STALE|CMD_NOSCRIPT,0,.args=CLUSTER_RESET_Args},
{"reset","Reset a Redis Cluster node","O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_RESET_History,CLUSTER_RESET_tips,clusterCommand,-2,CMD_ADMIN|CMD_STALE|CMD_NOSCRIPT,0,.args=CLUSTER_RESET_Args},
{"saveconfig","Forces the node to save cluster state on disk","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SAVECONFIG_History,CLUSTER_SAVECONFIG_tips,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0},
{"set-config-epoch","Set the configuration epoch in a new node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SET_CONFIG_EPOCH_History,CLUSTER_SET_CONFIG_EPOCH_tips,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_SET_CONFIG_EPOCH_Args},
{"setslot","Bind a hash slot to a specific node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SETSLOT_History,CLUSTER_SETSLOT_tips,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_SETSLOT_Args},
@@ -1025,22 +1029,22 @@ struct redisCommandArg CLIENT_UNBLOCK_Args[] = {
/* CLIENT command table */
struct redisCommand CLIENT_Subcommands[] = {
{"caching","Instruct the server about tracking or not keys in the next request","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_CACHING_History,CLIENT_CACHING_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_CACHING_Args},
{"getname","Get the current connection name","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_GETNAME_History,CLIENT_GETNAME_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"getredir","Get tracking notifications redirection client ID if any","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_GETREDIR_History,CLIENT_GETREDIR_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_HELP_History,CLIENT_HELP_tips,clientCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"id","Returns the client ID for the current connection","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_ID_History,CLIENT_ID_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"info","Returns information about the current client connection.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_INFO_History,CLIENT_INFO_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"kill","Kill the connection of a client","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_KILL_History,CLIENT_KILL_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_KILL_Args},
{"list","Get the list of client connections","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_LIST_History,CLIENT_LIST_tips,clientCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_LIST_Args},
{"no-evict","Set client eviction mode for the current connection","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_NO_EVICT_History,CLIENT_NO_EVICT_tips,clientCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_NO_EVICT_Args},
{"pause","Stop processing commands from clients for some time","O(1)","2.9.50",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_PAUSE_History,CLIENT_PAUSE_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_PAUSE_Args},
{"reply","Instruct the server whether to reply to commands","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_REPLY_History,CLIENT_REPLY_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_REPLY_Args},
{"setname","Set the current connection name","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_SETNAME_History,CLIENT_SETNAME_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_SETNAME_Args},
{"tracking","Enable or disable server assisted client side caching support","O(1). Some options may introduce additional complexity.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_TRACKING_History,CLIENT_TRACKING_tips,clientCommand,-3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_TRACKING_Args},
{"trackinginfo","Return information about server assisted client side caching for the current connection","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_TRACKINGINFO_History,CLIENT_TRACKINGINFO_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"unblock","Unblock a client blocked in a blocking command from a different connection","O(log N) where N is the number of client connections","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_UNBLOCK_History,CLIENT_UNBLOCK_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_UNBLOCK_Args},
{"unpause","Resume processing of clients that were paused","O(N) Where N is the number of paused clients","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_UNPAUSE_History,CLIENT_UNPAUSE_tips,clientCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"caching","Instruct the server about tracking or not keys in the next request","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_CACHING_History,CLIENT_CACHING_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_CACHING_Args},
{"getname","Get the current connection name","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_GETNAME_History,CLIENT_GETNAME_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{"getredir","Get tracking notifications redirection client ID if any","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_GETREDIR_History,CLIENT_GETREDIR_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_HELP_History,CLIENT_HELP_tips,clientCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{"id","Returns the client ID for the current connection","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_ID_History,CLIENT_ID_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{"info","Returns information about the current client connection.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_INFO_History,CLIENT_INFO_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{"kill","Kill the connection of a client","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_KILL_History,CLIENT_KILL_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_KILL_Args},
{"list","Get the list of client connections","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_LIST_History,CLIENT_LIST_tips,clientCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_LIST_Args},
{"no-evict","Set client eviction mode for the current connection","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_NO_EVICT_History,CLIENT_NO_EVICT_tips,clientCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_NO_EVICT_Args},
{"pause","Stop processing commands from clients for some time","O(1)","2.9.50",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_PAUSE_History,CLIENT_PAUSE_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_PAUSE_Args},
{"reply","Instruct the server whether to reply to commands","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_REPLY_History,CLIENT_REPLY_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_REPLY_Args},
{"setname","Set the current connection name","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_SETNAME_History,CLIENT_SETNAME_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_SETNAME_Args},
{"tracking","Enable or disable server assisted client side caching support","O(1). Some options may introduce additional complexity.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_TRACKING_History,CLIENT_TRACKING_tips,clientCommand,-3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_TRACKING_Args},
{"trackinginfo","Return information about server assisted client side caching for the current connection","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_TRACKINGINFO_History,CLIENT_TRACKINGINFO_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{"unblock","Unblock a client blocked in a blocking command from a different connection","O(log N) where N is the number of client connections","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_UNBLOCK_History,CLIENT_UNBLOCK_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_UNBLOCK_Args},
{"unpause","Resume processing of clients that were paused","O(N) Where N is the number of paused clients","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_UNPAUSE_History,CLIENT_UNPAUSE_tips,clientCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{0}
};
@@ -4317,13 +4321,13 @@ struct redisCommandArg COMMAND_LIST_Args[] = {
/* COMMAND command table */
struct redisCommand COMMAND_Subcommands[] = {
{"count","Get total number of Redis commands","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_COUNT_History,COMMAND_COUNT_tips,commandCountCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"docs","Get array of specific Redis command documentation","O(N) where N is the number of commands to look up","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_DOCS_History,COMMAND_DOCS_tips,commandDocsCommand,-2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=COMMAND_DOCS_Args},
{"getkeys","Extract keys given a full Redis command","O(N) where N is the number of arguments to the command","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_GETKEYS_History,COMMAND_GETKEYS_tips,commandGetKeysCommand,-4,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"getkeysandflags","Extract keys and access flags given a full Redis command","O(N) where N is the number of arguments to the command","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_GETKEYSANDFLAGS_History,COMMAND_GETKEYSANDFLAGS_tips,commandGetKeysAndFlagsCommand,-4,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_HELP_History,COMMAND_HELP_tips,commandHelpCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
{"info","Get array of specific Redis command details, or all when no argument is given.","O(N) where N is the number of commands to look up","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_INFO_History,COMMAND_INFO_tips,commandInfoCommand,-2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=COMMAND_INFO_Args},
{"list","Get an array of Redis command names","O(N) where N is the total number of Redis commands","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_LIST_History,COMMAND_LIST_tips,commandListCommand,-2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=COMMAND_LIST_Args},
{"count","Get total number of Redis commands","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_COUNT_History,COMMAND_COUNT_tips,commandCountCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{"docs","Get array of specific Redis command documentation","O(N) where N is the number of commands to look up","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_DOCS_History,COMMAND_DOCS_tips,commandDocsCommand,-2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=COMMAND_DOCS_Args},
{"getkeys","Extract keys given a full Redis command","O(N) where N is the number of arguments to the command","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_GETKEYS_History,COMMAND_GETKEYS_tips,commandGetKeysCommand,-4,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{"getkeysandflags","Extract keys and access flags given a full Redis command","O(N) where N is the number of arguments to the command","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_GETKEYSANDFLAGS_History,COMMAND_GETKEYSANDFLAGS_tips,commandGetKeysAndFlagsCommand,-4,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_HELP_History,COMMAND_HELP_tips,commandHelpCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
{"info","Get array of specific Redis command details, or all when no argument is given.","O(N) where N is the number of commands to look up","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_INFO_History,COMMAND_INFO_tips,commandInfoCommand,-2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=COMMAND_INFO_Args},
{"list","Get an array of Redis command names","O(N) where N is the total number of Redis commands","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_LIST_History,COMMAND_LIST_tips,commandListCommand,-2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=COMMAND_LIST_Args},
{0}
};
@@ -5808,8 +5812,8 @@ struct redisCommandArg ZRANGE_offset_count_Subargs[] = {
/* ZRANGE argument table */
struct redisCommandArg ZRANGE_Args[] = {
{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
{"min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
{"max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
{"start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
{"stop",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
{"sortby",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,.subargs=ZRANGE_sortby_Subargs},
{"rev",ARG_TYPE_PURE_TOKEN,-1,"REV",NULL,"6.2.0",CMD_ARG_OPTIONAL},
{"offset_count",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,"6.2.0",CMD_ARG_OPTIONAL,.subargs=ZRANGE_offset_count_Subargs},
@@ -7175,7 +7179,7 @@ struct redisCommand redisCommandTable[] = {
/* connection */
{"auth","Authenticate to the server","O(N) where N is the number of passwords defined for the user","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,AUTH_History,AUTH_tips,authCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH|CMD_SENTINEL|CMD_ALLOW_BUSY,ACL_CATEGORY_CONNECTION,.args=AUTH_Args},
{"client","A container for client connection commands","Depends on subcommand.","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_History,CLIENT_tips,NULL,-2,CMD_SENTINEL,0,.subcommands=CLIENT_Subcommands},
{"echo","Echo the given string","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,ECHO_History,ECHO_tips,echoCommand,2,CMD_FAST,ACL_CATEGORY_CONNECTION,.args=ECHO_Args},
{"echo","Echo the given string","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,ECHO_History,ECHO_tips,echoCommand,2,CMD_LOADING|CMD_STALE|CMD_FAST,ACL_CATEGORY_CONNECTION,.args=ECHO_Args},
{"hello","Handshake with Redis","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,HELLO_History,HELLO_tips,helloCommand,-1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH|CMD_SENTINEL|CMD_ALLOW_BUSY,ACL_CATEGORY_CONNECTION,.args=HELLO_Args},
{"ping","Ping the server","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,PING_History,PING_tips,pingCommand,-1,CMD_FAST|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=PING_Args},
{"quit","Close the connection","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,QUIT_History,QUIT_tips,quitCommand,-1,CMD_ALLOW_BUSY|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH,ACL_CATEGORY_CONNECTION},
+2 -1
View File
@@ -10,7 +10,8 @@
"command_flags": [
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -10,7 +10,8 @@
"command_flags": [
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -10,7 +10,8 @@
"command_flags": [
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -9,7 +9,8 @@
"function": "clientCommand",
"command_flags": [
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -10,7 +10,8 @@
"command_flags": [
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -10,7 +10,8 @@
"command_flags": [
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -33,7 +33,8 @@
"ADMIN",
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -25,7 +25,8 @@
"ADMIN",
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -11,7 +11,8 @@
"ADMIN",
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -17,7 +17,8 @@
"ADMIN",
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -10,7 +10,8 @@
"command_flags": [
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -10,7 +10,8 @@
"command_flags": [
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -10,7 +10,8 @@
"command_flags": [
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -10,7 +10,8 @@
"command_flags": [
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -11,7 +11,8 @@
"ADMIN",
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -11,7 +11,8 @@
"ADMIN",
"NOSCRIPT",
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+12
View File
@@ -7,6 +7,12 @@
"arity": -4,
"container": "CLUSTER",
"function": "clusterCommand",
"history": [
[
"4.0.0",
"Added the optional `cluster_bus_port` argument."
]
],
"command_flags": [
"NO_ASYNC_LOADING",
"ADMIN",
@@ -23,6 +29,12 @@
{
"name": "port",
"type": "integer"
},
{
"name": "cluster_bus_port",
"type": "integer",
"optional": true,
"since": "4.0.0"
}
]
}
+1 -1
View File
@@ -4,7 +4,7 @@
"complexity": "O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.",
"group": "cluster",
"since": "3.0.0",
"arity": 3,
"arity": -2,
"container": "CLUSTER",
"function": "clusterCommand",
"command_flags": [
+2 -1
View File
@@ -9,7 +9,8 @@
"function": "commandCountCommand",
"command_flags": [
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -9,7 +9,8 @@
"function": "commandDocsCommand",
"command_flags": [
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -9,7 +9,8 @@
"function": "commandGetKeysCommand",
"command_flags": [
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -9,7 +9,8 @@
"function": "commandGetKeysAndFlagsCommand",
"command_flags": [
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -9,7 +9,8 @@
"function": "commandHelpCommand",
"command_flags": [
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -15,7 +15,8 @@
],
"command_flags": [
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2 -1
View File
@@ -9,7 +9,8 @@
"function": "commandListCommand",
"command_flags": [
"LOADING",
"STALE"
"STALE",
"SENTINEL"
],
"acl_categories": [
"CONNECTION"
+2
View File
@@ -7,6 +7,8 @@
"arity": 2,
"function": "echoCommand",
"command_flags": [
"LOADING",
"STALE",
"FAST"
],
"acl_categories": [
+2 -2
View File
@@ -45,11 +45,11 @@
"key_spec_index": 0
},
{
"name": "min",
"name": "start",
"type": "string"
},
{
"name": "max",
"name": "stop",
"type": "string"
},
{
+5
View File
@@ -1665,6 +1665,7 @@ int rewriteConfigOverwriteFile(char *configfile, sds content) {
const char *tmp_suffix = ".XXXXXX";
size_t offset = 0;
ssize_t written_bytes = 0;
int old_errno;
int tmp_path_len = snprintf(tmp_conffile, sizeof(tmp_conffile), "%s%s", configfile, tmp_suffix);
if (tmp_path_len <= 0 || (unsigned int)tmp_path_len >= sizeof(tmp_conffile)) {
@@ -1701,14 +1702,18 @@ int rewriteConfigOverwriteFile(char *configfile, sds content) {
serverLog(LL_WARNING, "Could not chmod config file (%s)", strerror(errno));
else if (rename(tmp_conffile, configfile) == -1)
serverLog(LL_WARNING, "Could not rename tmp config file (%s)", strerror(errno));
else if (fsyncFileDir(configfile) == -1)
serverLog(LL_WARNING, "Could not sync config file dir (%s)", strerror(errno));
else {
retval = 0;
serverLog(LL_DEBUG, "Rewritten config file (%s) successfully", configfile);
}
cleanup:
old_errno = errno;
close(fd);
if (retval) unlink(tmp_conffile);
errno = old_errno;
return retval;
}
+1 -1
View File
@@ -282,7 +282,7 @@ int dictRehashMilliseconds(dict *d, int ms) {
/* This function performs just a step of rehashing, and only if hashing has
* not been paused for our hash table. When we have iterators in the
* middle of a rehashing we can't mess with the two hash tables otherwise
* some element can be missed or duplicated.
* some elements can be missed or duplicated.
*
* This function is called by common lookup or update operations in the
* dictionary so that the hash table automatically migrates from H1 to H2
+5 -5
View File
@@ -266,14 +266,14 @@ void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evic
* has a low value.
*
* New keys don't start at zero, in order to have the ability to collect
* some accesses before being trashed away, so they start at COUNTER_INIT_VAL.
* The logarithmic increment performed on LOG_C takes care of COUNTER_INIT_VAL
* when incrementing the key, so that keys starting at COUNTER_INIT_VAL
* some accesses before being trashed away, so they start at LFU_INIT_VAL.
* The logarithmic increment performed on LOG_C takes care of LFU_INIT_VAL
* when incrementing the key, so that keys starting at LFU_INIT_VAL
* (or having a smaller value) have a very high chance of being incremented
* on access.
*
* During decrement, the value of the logarithmic counter is halved if
* its current value is greater than two times the COUNTER_INIT_VAL, otherwise
* its current value is greater than two times the LFU_INIT_VAL, otherwise
* it is just decremented by one.
* --------------------------------------------------------------------------*/
@@ -295,7 +295,7 @@ unsigned long LFUTimeElapsed(unsigned long ldt) {
}
/* Logarithmically increment a counter. The greater is the current counter value
* the less likely is that it gets really implemented. Saturate it at 255. */
* the less likely is that it gets really incremented. Saturate it at 255. */
uint8_t LFULogIncr(uint8_t counter) {
if (counter == 255) return 255;
double r = (double)rand()/RAND_MAX;
+1 -1
View File
@@ -674,7 +674,7 @@ void fcallroCommand(client *c) {
* * Engine name
* * Library description
* * Library code
* RDB_OPCODE_FUNCTION is saved before each library to present
* RDB_OPCODE_FUNCTION2 is saved before each library to present
* that the payload is a library.
* RDB version and crc64 is saved at the end of the payload.
* The RDB version is saved for backward compatibility.
+4 -4
View File
@@ -1,4 +1,4 @@
/* Automatically generated by ./utils/generate-command-help.rb, do not edit. */
/* Automatically generated by utils/generate-command-help.rb, do not edit. */
#ifndef __REDIS_HELP_H
#define __REDIS_HELP_H
@@ -138,7 +138,7 @@ struct commandHelp {
"key GET encoding offset [GET encoding offset ...]",
"Perform arbitrary bitfield integer operations on strings. Read-only variant of BITFIELD",
15,
"6.2.0" },
"6.0.0" },
{ "BITOP",
"operation destkey key [key ...]",
"Perform bitwise operations between strings",
@@ -355,7 +355,7 @@ struct commandHelp {
12,
"7.0.0" },
{ "CLUSTER MEET",
"ip port",
"ip port [cluster_bus_port]",
"Force a node cluster to handshake with another node",
12,
"3.0.0" },
@@ -1775,7 +1775,7 @@ struct commandHelp {
4,
"6.2.0" },
{ "ZRANGE",
"key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]",
"key start stop [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]",
"Return a range of members in a sorted set",
4,
"1.2.0" },
+1 -4
View File
@@ -49,7 +49,6 @@
#define LP_HDR_NUMELE_UNKNOWN UINT16_MAX
#define LP_MAX_INT_ENCODING_LEN 9
#define LP_MAX_BACKLEN_SIZE 5
#define LP_MAX_ENTRY_BACKLEN 34359738367ULL
#define LP_ENCODING_INT 0
#define LP_ENCODING_STRING 1
@@ -569,9 +568,7 @@ unsigned long lpLength(unsigned char *lp) {
* this lib.
*
* Similarly, there is no error returned since the listpack normally can be
* assumed to be valid, so that would be a very high API cost. However a function
* in order to check the integrity of the listpack at load time is provided,
* check lpIsValid(). */
* assumed to be valid, so that would be a very high API cost. */
static inline unsigned char *lpGetWithSize(unsigned char *p, int64_t *count, unsigned char *intbuf, uint64_t *entry_size) {
int64_t val;
uint64_t uval, negstart, negmax;
+77 -8
View File
@@ -2297,7 +2297,7 @@ RedisModuleString *RM_CreateStringPrintf(RedisModuleCtx *ctx, const char *fmt, .
}
/* Like RedisModule_CreatString(), but creates a string starting from a `long long`
/* Like RedisModule_CreateString(), but creates a string starting from a `long long`
* integer instead of taking a buffer and its length.
*
* The returned string must be released with RedisModule_FreeString() or by
@@ -2311,7 +2311,21 @@ RedisModuleString *RM_CreateStringFromLongLong(RedisModuleCtx *ctx, long long ll
return RM_CreateString(ctx,buf,len);
}
/* Like RedisModule_CreatString(), but creates a string starting from a double
/* Like RedisModule_CreateString(), but creates a string starting from a `unsigned long long`
* integer instead of taking a buffer and its length.
*
* The returned string must be released with RedisModule_FreeString() or by
* enabling automatic memory management.
*
* The passed context 'ctx' may be NULL if necessary, see the
* RedisModule_CreateString() documentation for more info. */
RedisModuleString *RM_CreateStringFromULongLong(RedisModuleCtx *ctx, unsigned long long ull) {
char buf[LONG_STR_SIZE];
size_t len = ull2string(buf,sizeof(buf),ull);
return RM_CreateString(ctx,buf,len);
}
/* Like RedisModule_CreateString(), but creates a string starting from a double
* instead of taking a buffer and its length.
*
* The returned string must be released with RedisModule_FreeString() or by
@@ -2322,7 +2336,7 @@ RedisModuleString *RM_CreateStringFromDouble(RedisModuleCtx *ctx, double d) {
return RM_CreateString(ctx,buf,len);
}
/* Like RedisModule_CreatString(), but creates a string starting from a long
/* Like RedisModule_CreateString(), but creates a string starting from a long
* double.
*
* The returned string must be released with RedisModule_FreeString() or by
@@ -2337,7 +2351,7 @@ RedisModuleString *RM_CreateStringFromLongDouble(RedisModuleCtx *ctx, long doubl
return RM_CreateString(ctx,buf,len);
}
/* Like RedisModule_CreatString(), but creates a string starting from another
/* Like RedisModule_CreateString(), but creates a string starting from another
* RedisModuleString.
*
* The returned string must be released with RedisModule_FreeString() or by
@@ -2519,6 +2533,14 @@ int RM_StringToLongLong(const RedisModuleString *str, long long *ll) {
REDISMODULE_ERR;
}
/* Convert the string into a `unsigned long long` integer, storing it at `*ull`.
* Returns REDISMODULE_OK on success. If the string can't be parsed
* as a valid, strict `unsigned long long` (no spaces before/after), REDISMODULE_ERR
* is returned. */
int RM_StringToULongLong(const RedisModuleString *str, unsigned long long *ull) {
return string2ull(str->ptr,ull) ? REDISMODULE_OK : REDISMODULE_ERR;
}
/* Convert the string into a double, storing it at `*d`.
* Returns REDISMODULE_OK on success or REDISMODULE_ERR if the string is
* not a valid string representation of a double value. */
@@ -3327,8 +3349,8 @@ int modulePopulateReplicationInfoStructure(void *ri, int structver) {
* is returned.
*
* When the client exist and the `ci` pointer is not NULL, but points to
* a structure of type RedisModuleClientInfo, previously initialized with
* the correct REDISMODULE_CLIENTINFO_INITIALIZER, the structure is populated
* a structure of type RedisModuleClientInfoV1, previously initialized with
* the correct REDISMODULE_CLIENTINFO_INITIALIZER_V1, the structure is populated
* with the following fields:
*
* uint64_t flags; // REDISMODULE_CLIENTINFO_FLAG_*
@@ -3373,6 +3395,40 @@ int RM_GetClientInfoById(void *ci, uint64_t id) {
return modulePopulateClientInfoStructure(ci,client,structver);
}
/* Returns the name of the client connection with the given ID.
*
* If the client ID does not exist or if the client has no name associated with
* it, NULL is returned. */
RedisModuleString *RM_GetClientNameById(RedisModuleCtx *ctx, uint64_t id) {
client *client = lookupClientByID(id);
if (client == NULL || client->name == NULL) return NULL;
robj *name = client->name;
incrRefCount(name);
autoMemoryAdd(ctx, REDISMODULE_AM_STRING, name);
return name;
}
/* Sets the name of the client with the given ID. This is equivalent to the client calling
* `CLIENT SETNAME name`.
*
* Returns REDISMODULE_OK on success. On failure, REDISMODULE_ERR is returned
* and errno is set as follows:
*
* - ENOENT if the client does not exist
* - EINVAL if the name contains invalid characters */
int RM_SetClientNameById(uint64_t id, RedisModuleString *name) {
client *client = lookupClientByID(id);
if (client == NULL) {
errno = ENOENT;
return REDISMODULE_ERR;
}
if (clientSetName(client, name) == C_ERR) {
errno = EINVAL;
return REDISMODULE_ERR;
}
return REDISMODULE_OK;
}
/* Publish a message to subscribers (see PUBLISH command). */
int RM_PublishMessage(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) {
UNUSED(ctx);
@@ -5783,7 +5839,16 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
if (flags & REDISMODULE_ARGV_RESPECT_DENY_OOM) {
if (cmd->flags & CMD_DENYOOM) {
if (server.pre_command_oom_state) {
int oom_state;
if (ctx->flags & REDISMODULE_CTX_THREAD_SAFE) {
/* On background thread we can not count on server.pre_command_oom_state.
* Because it is only set on the main thread, in such case we will check
* the actual memory usage. */
oom_state = (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_ERR);
} else {
oom_state = server.pre_command_oom_state;
}
if (oom_state) {
errno = ENOSPC;
if (error_as_call_replies) {
sds msg = sdsdup(shared.oomerr->ptr);
@@ -5823,7 +5888,7 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
}
int deny_write_type = writeCommandsDeniedByDiskError();
int obey_client = mustObeyClient(server.current_client);
int obey_client = (server.current_client && mustObeyClient(server.current_client));
if (deny_write_type != DISK_ERROR_TYPE_NONE && !obey_client) {
errno = ESPIPE;
@@ -12378,6 +12443,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(ListInsert);
REGISTER_API(ListDelete);
REGISTER_API(StringToLongLong);
REGISTER_API(StringToULongLong);
REGISTER_API(StringToDouble);
REGISTER_API(StringToLongDouble);
REGISTER_API(StringToStreamID);
@@ -12400,6 +12466,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(CreateStringFromCallReply);
REGISTER_API(CreateString);
REGISTER_API(CreateStringFromLongLong);
REGISTER_API(CreateStringFromULongLong);
REGISTER_API(CreateStringFromDouble);
REGISTER_API(CreateStringFromLongDouble);
REGISTER_API(CreateStringFromString);
@@ -12592,6 +12659,8 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(ServerInfoGetFieldUnsigned);
REGISTER_API(ServerInfoGetFieldDouble);
REGISTER_API(GetClientInfoById);
REGISTER_API(GetClientNameById);
REGISTER_API(SetClientNameById);
REGISTER_API(PublishMessage);
REGISTER_API(PublishMessageShard);
REGISTER_API(SubscribeToServerEvent);
+14 -3
View File
@@ -38,6 +38,7 @@ void initClientMultiState(client *c) {
c->mstate.cmd_flags = 0;
c->mstate.cmd_inv_flags = 0;
c->mstate.argv_len_sums = 0;
c->mstate.alloc_count = 0;
}
/* Release all the resources associated with MULTI/EXEC state */
@@ -65,9 +66,16 @@ void queueMultiCommand(client *c, uint64_t cmd_flags) {
* aborted. */
if (c->flags & (CLIENT_DIRTY_CAS|CLIENT_DIRTY_EXEC))
return;
c->mstate.commands = zrealloc(c->mstate.commands,
sizeof(multiCmd)*(c->mstate.count+1));
if (c->mstate.count == 0) {
/* If a client is using multi/exec, assuming it is used to execute at least
* two commands. Hence, creating by default size of 2. */
c->mstate.commands = zmalloc(sizeof(multiCmd)*2);
c->mstate.alloc_count = 2;
}
if (c->mstate.count == c->mstate.alloc_count) {
c->mstate.alloc_count = c->mstate.alloc_count < INT_MAX/2 ? c->mstate.alloc_count*2 : INT_MAX;
c->mstate.commands = zrealloc(c->mstate.commands, sizeof(multiCmd)*(c->mstate.alloc_count));
}
mc = c->mstate.commands+c->mstate.count;
mc->cmd = c->cmd;
mc->argc = c->argc;
@@ -230,6 +238,7 @@ void execCommand(client *c) {
/* Commands may alter argc/argv, restore mstate. */
c->mstate.commands[j].argc = c->argc;
c->mstate.commands[j].argv = c->argv;
c->mstate.commands[j].argv_len = c->argv_len;
c->mstate.commands[j].cmd = c->cmd;
}
@@ -465,5 +474,7 @@ size_t multiStateMemOverhead(client *c) {
size_t mem = c->mstate.argv_len_sums;
/* Add watched keys overhead, Note: this doesn't take into account the watched keys themselves, because they aren't managed per-client. */
mem += listLength(c->watched_keys) * (sizeof(listNode) + sizeof(watchedKey));
/* Reserved memory for queued multi commands. */
mem += c->mstate.alloc_count * sizeof(multiCmd);
return mem;
}
+34 -22
View File
@@ -2785,7 +2785,7 @@ sds catClientInfoString(sds s, client *client) {
}
sds ret = sdscatfmt(s,
"id=%U addr=%s laddr=%s %s name=%s age=%I idle=%I flags=%s db=%i sub=%i psub=%i multi=%i qbuf=%U qbuf-free=%U argv-mem=%U multi-mem=%U rbs=%U rbp=%U obl=%U oll=%U omem=%U tot-mem=%U events=%s cmd=%s user=%s redir=%I resp=%i",
"id=%U addr=%s laddr=%s %s name=%s age=%I idle=%I flags=%s db=%i sub=%i psub=%i ssub=%i multi=%i qbuf=%U qbuf-free=%U argv-mem=%U multi-mem=%U rbs=%U rbp=%U obl=%U oll=%U omem=%U tot-mem=%U events=%s cmd=%s user=%s redir=%I resp=%i",
(unsigned long long) client->id,
getClientPeerId(client),
getClientSockname(client),
@@ -2797,6 +2797,7 @@ sds catClientInfoString(sds s, client *client) {
client->db->id,
(int) dictSize(client->pubsub_channels),
(int) listLength(client->pubsub_patterns),
(int) dictSize(client->pubsubshard_channels),
(client->flags & CLIENT_MULTI) ? client->mstate.count : -1,
(unsigned long long) sdslen(client->querybuf),
(unsigned long long) sdsavail(client->querybuf),
@@ -2832,18 +2833,9 @@ sds getAllClientsInfoString(int type) {
return o;
}
/* This function implements CLIENT SETNAME, including replying to the
* user with an error if the charset is wrong (in that case C_ERR is
* returned). If the function succeeded C_OK is returned, and it's up
* to the caller to send a reply if needed.
*
* Setting an empty string as name has the effect of unsetting the
* currently set name: the client will remain unnamed.
*
* This function is also used to implement the HELLO SETNAME option. */
int clientSetNameOrReply(client *c, robj *name) {
int len = sdslen(name->ptr);
char *p = name->ptr;
/* Returns C_OK if the name has been set or C_ERR if the name is invalid. */
int clientSetName(client *c, robj *name) {
int len = (name != NULL) ? sdslen(name->ptr) : 0;
/* Setting the client name to an empty string actually removes
* the current name. */
@@ -2856,11 +2848,9 @@ int clientSetNameOrReply(client *c, robj *name) {
/* Otherwise check if the charset is ok. We need to do this otherwise
* CLIENT LIST format will break. You should always be able to
* split by space to get the different fields. */
char *p = name->ptr;
for (int j = 0; j < len; j++) {
if (p[j] < '!' || p[j] > '~') { /* ASCII is assumed. */
addReplyError(c,
"Client names cannot contain spaces, "
"newlines or special characters.");
return C_ERR;
}
}
@@ -2870,6 +2860,25 @@ int clientSetNameOrReply(client *c, robj *name) {
return C_OK;
}
/* This function implements CLIENT SETNAME, including replying to the
* user with an error if the charset is wrong (in that case C_ERR is
* returned). If the function succeeded C_OK is returned, and it's up
* to the caller to send a reply if needed.
*
* Setting an empty string as name has the effect of unsetting the
* currently set name: the client will remain unnamed.
*
* This function is also used to implement the HELLO SETNAME option. */
int clientSetNameOrReply(client *c, robj *name) {
int result = clientSetName(c, name);
if (result == C_ERR) {
addReplyError(c,
"Client names cannot contain spaces, "
"newlines or special characters.");
}
return result;
}
/* Reset the client state to resemble a newly connected client.
*/
void resetCommand(client *c) {
@@ -3660,9 +3669,7 @@ size_t getClientMemoryUsage(client *c, size_t *output_buffer_mem_usage) {
/* Add memory overhead of pubsub channels and patterns. Note: this is just the overhead of the robj pointers
* to the strings themselves because they aren't stored per client. */
mem += listLength(c->pubsub_patterns) * sizeof(listNode);
mem += dictSize(c->pubsub_channels) * sizeof(dictEntry) +
dictSlots(c->pubsub_channels) * sizeof(dictEntry*);
mem += pubsubMemOverhead(c);
/* Add memory overhead of the tracking prefixes, this is an underestimation so we don't need to traverse the entire rax */
if (c->client_tracking_prefixes)
@@ -3997,10 +4004,15 @@ void processEventsWhileBlocked(void) {
* ========================================================================== */
#define IO_THREADS_MAX_NUM 128
#define CACHE_LINE_SIZE 64
typedef struct __attribute__((aligned(CACHE_LINE_SIZE))) threads_pending {
redisAtomic unsigned long value;
} threads_pending;
pthread_t io_threads[IO_THREADS_MAX_NUM];
pthread_mutex_t io_threads_mutex[IO_THREADS_MAX_NUM];
redisAtomic unsigned long io_threads_pending[IO_THREADS_MAX_NUM];
threads_pending io_threads_pending[IO_THREADS_MAX_NUM];
int io_threads_op; /* IO_THREADS_OP_IDLE, IO_THREADS_OP_READ or IO_THREADS_OP_WRITE. */ // TODO: should access to this be atomic??!
/* This is the list of clients each thread will serve when threaded I/O is
@@ -4010,12 +4022,12 @@ list *io_threads_list[IO_THREADS_MAX_NUM];
static inline unsigned long getIOPendingCount(int i) {
unsigned long count = 0;
atomicGetWithSync(io_threads_pending[i], count);
atomicGetWithSync(io_threads_pending[i].value, count);
return count;
}
static inline void setIOPendingCount(int i, unsigned long count) {
atomicSetWithSync(io_threads_pending[i], count);
atomicSetWithSync(io_threads_pending[i].value, count);
}
void *IOThreadMain(void *myid) {
+12
View File
@@ -722,3 +722,15 @@ void sunsubscribeCommand(client *c) {
}
if (clientTotalPubSubSubscriptionCount(c) == 0) c->flags &= ~CLIENT_PUBSUB;
}
size_t pubsubMemOverhead(client *c) {
/* PubSub patterns */
size_t mem = listLength(c->pubsub_patterns) * sizeof(listNode);
/* Global PubSub channels */
mem += dictSize(c->pubsub_channels) * sizeof(dictEntry) +
dictSlots(c->pubsub_channels) * sizeof(dictEntry*);
/* Sharded PubSub channels */
mem += dictSize(c->pubsubshard_channels) * sizeof(dictEntry) +
dictSlots(c->pubsubshard_channels) * sizeof(dictEntry*);
return mem;
}
+8 -6
View File
@@ -1160,7 +1160,6 @@ ssize_t rdbSaveAuxFieldStrInt(rio *rdb, char *key, long long val) {
/* Save a few default AUX fields with information about the RDB generated. */
int rdbSaveInfoAuxFields(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
UNUSED(rdbflags);
int redis_bits = (sizeof(void*) == 8) ? 64 : 32;
int aof_base = (rdbflags & RDBFLAGS_AOF_PREAMBLE) != 0;
@@ -1397,6 +1396,7 @@ int rdbSave(int req, char *filename, rdbSaveInfo *rsi) {
FILE *fp = NULL;
rio rdb;
int error = 0;
char *err_op; /* For a detailed log */
snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
fp = fopen(tmpfile,"w");
@@ -1420,13 +1420,14 @@ int rdbSave(int req, char *filename, rdbSaveInfo *rsi) {
if (rdbSaveRio(req,&rdb,&error,RDBFLAGS_NONE,rsi) == C_ERR) {
errno = error;
err_op = "rdbSaveRio";
goto werr;
}
/* Make sure data will not remain on the OS's output buffers */
if (fflush(fp)) goto werr;
if (fsync(fileno(fp))) goto werr;
if (fclose(fp)) { fp = NULL; goto werr; }
if (fflush(fp)) { err_op = "fflush"; goto werr; }
if (fsync(fileno(fp))) { err_op = "fsync"; goto werr; }
if (fclose(fp)) { fp = NULL; err_op = "fclose"; goto werr; }
fp = NULL;
/* Use RENAME to make sure the DB file is changed atomically only
@@ -1445,6 +1446,7 @@ int rdbSave(int req, char *filename, rdbSaveInfo *rsi) {
stopSaving(0);
return C_ERR;
}
if (fsyncFileDir(filename) == -1) { err_op = "fsyncFileDir"; goto werr; }
serverLog(LL_NOTICE,"DB saved on disk");
server.dirty = 0;
@@ -1454,7 +1456,7 @@ int rdbSave(int req, char *filename, rdbSaveInfo *rsi) {
return C_OK;
werr:
serverLog(LL_WARNING,"Write error saving DB on disk: %s", strerror(errno));
serverLog(LL_WARNING,"Write error saving DB on disk(%s): %s", err_op, strerror(errno));
if (fp) fclose(fp);
unlink(tmpfile);
stopSaving(0);
@@ -3244,7 +3246,7 @@ eoferr:
* to do the actual loading. Moreover the ETA displayed in the INFO
* output is initialized and finalized.
*
* If you pass an 'rsi' structure initialized with RDB_SAVE_OPTION_INIT, the
* If you pass an 'rsi' structure initialized with RDB_SAVE_INFO_INIT, the
* loading code will fill the information fields in the structure. */
int rdbLoad(char *filename, rdbSaveInfo *rsi, int rdbflags) {
FILE *fp;
+4
View File
@@ -846,6 +846,10 @@ static client createClient(char *cmd, size_t len, client from, int thread_id) {
}
if (config.idlemode == 0)
aeCreateFileEvent(el,c->context->fd,AE_WRITABLE,writeHandler,c);
else
/* In idle mode, clients still need to register readHandler for catching errors */
aeCreateFileEvent(el,c->context->fd,AE_READABLE,readHandler,c);
listAddNodeTail(config.clients,c);
atomicIncr(config.liveclients, 1);
atomicGet(config.slots_last_update, c->slots_last_update);
+8 -1
View File
@@ -283,7 +283,10 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
robj *auxkey, *auxval;
rdbstate.doing = RDB_CHECK_DOING_READ_AUX;
if ((auxkey = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
if ((auxval = rdbLoadStringObject(&rdb)) == NULL) goto eoferr;
if ((auxval = rdbLoadStringObject(&rdb)) == NULL) {
decrRefCount(auxkey);
goto eoferr;
}
rdbCheckInfo("AUX FIELD %s = '%s'",
(char*)auxkey->ptr, (char*)auxval->ptr);
@@ -297,6 +300,10 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
if ((moduleid = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr;
if ((when_opcode = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr;
if ((when = rdbLoadLen(&rdb,NULL)) == RDB_LENERR) goto eoferr;
if (when_opcode != RDB_MODULE_OPCODE_UINT) {
rdbCheckError("bad when_opcode");
goto err;
}
char name[10];
moduleTypeNameByID(name,moduleid);
+66 -47
View File
@@ -81,6 +81,7 @@
#define REDIS_CLI_CLUSTER_YES_ENV "REDISCLI_CLUSTER_YES"
#define CLUSTER_MANAGER_SLOTS 16384
#define CLUSTER_MANAGER_PORT_INCR 10000 /* same as CLUSTER_PORT_INCR */
#define CLUSTER_MANAGER_MIGRATE_TIMEOUT 60000
#define CLUSTER_MANAGER_MIGRATE_PIPELINE 10
#define CLUSTER_MANAGER_REBALANCE_THRESHOLD 2
@@ -2867,6 +2868,7 @@ typedef struct clusterManagerNode {
sds name;
char *ip;
int port;
int bus_port; /* cluster-port */
uint64_t current_epoch;
time_t ping_sent;
time_t ping_recv;
@@ -2937,7 +2939,7 @@ typedef int (*clusterManagerOnReplyError)(redisReply *reply,
/* Cluster Manager helper functions */
static clusterManagerNode *clusterManagerNewNode(char *ip, int port);
static clusterManagerNode *clusterManagerNewNode(char *ip, int port, int bus_port);
static clusterManagerNode *clusterManagerNodeByName(const char *name);
static clusterManagerNode *clusterManagerNodeByAbbreviatedName(const char *n);
static void clusterManagerNodeResetSlots(clusterManagerNode *node);
@@ -2997,15 +2999,15 @@ typedef struct clusterManagerCommandDef {
clusterManagerCommandDef clusterManagerCommands[] = {
{"create", clusterManagerCommandCreate, -2, "host1:port1 ... hostN:portN",
"replicas <arg>"},
{"check", clusterManagerCommandCheck, -1, "host:port",
{"check", clusterManagerCommandCheck, -1, "<host:port> or <host> <port> - separated by either colon or space",
"search-multiple-owners"},
{"info", clusterManagerCommandInfo, -1, "host:port", NULL},
{"fix", clusterManagerCommandFix, -1, "host:port",
{"info", clusterManagerCommandInfo, -1, "<host:port> or <host> <port> - separated by either colon or space", NULL},
{"fix", clusterManagerCommandFix, -1, "<host:port> or <host> <port> - separated by either colon or space",
"search-multiple-owners,fix-with-unreachable-masters"},
{"reshard", clusterManagerCommandReshard, -1, "host:port",
{"reshard", clusterManagerCommandReshard, -1, "<host:port> or <host> <port> - separated by either colon or space",
"from <arg>,to <arg>,slots <arg>,yes,timeout <arg>,pipeline <arg>,"
"replace"},
{"rebalance", clusterManagerCommandRebalance, -1, "host:port",
{"rebalance", clusterManagerCommandRebalance, -1, "<host:port> or <host> <port> - separated by either colon or space",
"weight <node1=w1...nodeN=wN>,use-empty-masters,"
"timeout <arg>,simulate,pipeline <arg>,threshold <arg>,replace"},
{"add-node", clusterManagerCommandAddNode, 2,
@@ -3094,6 +3096,7 @@ static clusterManagerCommandProc *validateClusterManagerCommand(void) {
static int parseClusterNodeAddress(char *addr, char **ip_ptr, int *port_ptr,
int *bus_port_ptr)
{
/* ip:port[@bus_port] */
char *c = strrchr(addr, '@');
if (c != NULL) {
*c = '\0';
@@ -3203,12 +3206,15 @@ static void freeClusterManager(void) {
dictRelease(clusterManagerUncoveredSlots);
}
static clusterManagerNode *clusterManagerNewNode(char *ip, int port) {
static clusterManagerNode *clusterManagerNewNode(char *ip, int port, int bus_port) {
clusterManagerNode *node = zmalloc(sizeof(*node));
node->context = NULL;
node->name = NULL;
node->ip = ip;
node->port = port;
/* We don't need to know the bus_port, at this point this value may be wrong.
* If it is used, it will be corrected in clusterManagerLoadInfoFromNode. */
node->bus_port = bus_port ? bus_port : port + CLUSTER_MANAGER_PORT_INCR;
node->current_epoch = 0;
node->ping_sent = 0;
node->ping_recv = 0;
@@ -4611,9 +4617,20 @@ static int clusterManagerNodeLoadInfo(clusterManagerNode *node, int opts,
success = 0;
goto cleanup;
}
char *ip = NULL;
int port = 0, bus_port = 0;
if (addr == NULL || !parseClusterNodeAddress(addr, &ip, &port, &bus_port)) {
fprintf(stderr, "Error: invalid CLUSTER NODES reply\n");
success = 0;
goto cleanup;
}
int myself = (strstr(flags, "myself") != NULL);
clusterManagerNode *currentNode = NULL;
if (myself) {
/* bus-port could be wrong, correct it here, see clusterManagerNewNode. */
node->bus_port = bus_port;
node->flags |= CLUSTER_MANAGER_FLAG_MYSELF;
currentNode = node;
clusterManagerNodeResetSlots(node);
@@ -4681,22 +4698,7 @@ static int clusterManagerNodeLoadInfo(clusterManagerNode *node, int opts,
if (!(node->flags & CLUSTER_MANAGER_FLAG_MYSELF)) continue;
else break;
} else {
if (addr == NULL) {
fprintf(stderr, "Error: invalid CLUSTER NODES reply\n");
success = 0;
goto cleanup;
}
char *c = strrchr(addr, '@');
if (c != NULL) *c = '\0';
c = strrchr(addr, ':');
if (c == NULL) {
fprintf(stderr, "Error: invalid CLUSTER NODES reply\n");
success = 0;
goto cleanup;
}
*c = '\0';
int port = atoi(++c);
currentNode = clusterManagerNewNode(sdsnew(addr), port);
currentNode = clusterManagerNewNode(sdsnew(ip), port, bus_port);
currentNode->flags |= CLUSTER_MANAGER_FLAG_FRIEND;
if (node->friends == NULL) node->friends = listCreate();
listAddNodeTail(node->friends, currentNode);
@@ -6110,17 +6112,14 @@ static int clusterManagerCommandCreate(int argc, char **argv) {
cluster_manager.nodes = listCreate();
for (i = 0; i < argc; i++) {
char *addr = argv[i];
char *c = strrchr(addr, '@');
if (c != NULL) *c = '\0';
c = strrchr(addr, ':');
if (c == NULL) {
char *ip = NULL;
int port = 0;
if (!parseClusterNodeAddress(addr, &ip, &port, NULL)) {
fprintf(stderr, "Invalid address format: %s\n", addr);
return 0;
}
*c = '\0';
char *ip = addr;
int port = atoi(++c);
clusterManagerNode *node = clusterManagerNewNode(ip, port);
clusterManagerNode *node = clusterManagerNewNode(ip, port, 0);
if (!clusterManagerNodeConnect(node)) {
freeClusterManagerNode(node);
return 0;
@@ -6327,8 +6326,16 @@ assign_replicas:
continue;
}
redisReply *reply = NULL;
reply = CLUSTER_MANAGER_COMMAND(node, "cluster meet %s %d",
first_ip, first->port);
if (first->bus_port == 0 || (first->bus_port == first->port + CLUSTER_MANAGER_PORT_INCR)) {
/* CLUSTER MEET bus-port parameter was added in 4.0.
* So if (bus_port == 0) or (bus_port == port + CLUSTER_MANAGER_PORT_INCR),
* we just call CLUSTER MEET with 2 arguments, using the old form. */
reply = CLUSTER_MANAGER_COMMAND(node, "cluster meet %s %d",
first->ip, first->port);
} else {
reply = CLUSTER_MANAGER_COMMAND(node, "cluster meet %s %d %d",
first->ip, first->port, first->bus_port);
}
int is_err = 0;
if (reply != NULL) {
if ((is_err = reply->type == REDIS_REPLY_ERROR))
@@ -6362,6 +6369,8 @@ assign_replicas:
}
success = 0;
goto cleanup;
} else if (err != NULL) {
zfree(err);
}
}
// Reset Nodes
@@ -6405,7 +6414,7 @@ static int clusterManagerCommandAddNode(int argc, char **argv) {
clusterManagerLogInfo(">>> Adding node %s:%d to cluster %s:%d\n", ip, port,
ref_ip, ref_port);
// Check the existing cluster
clusterManagerNode *refnode = clusterManagerNewNode(ref_ip, ref_port);
clusterManagerNode *refnode = clusterManagerNewNode(ref_ip, ref_port, 0);
if (!clusterManagerLoadInfoFromNode(refnode)) return 0;
if (!clusterManagerCheckCluster(0)) return 0;
@@ -6429,7 +6438,7 @@ static int clusterManagerCommandAddNode(int argc, char **argv) {
}
// Add the new node
clusterManagerNode *new_node = clusterManagerNewNode(ip, port);
clusterManagerNode *new_node = clusterManagerNewNode(ip, port, 0);
int added = 0;
if (!clusterManagerNodeConnect(new_node)) {
clusterManagerLogErr("[ERR] Sorry, can't connect to node %s:%d\n",
@@ -6507,8 +6516,18 @@ static int clusterManagerCommandAddNode(int argc, char **argv) {
success = 0;
goto cleanup;
}
reply = CLUSTER_MANAGER_COMMAND(new_node, "CLUSTER MEET %s %d",
first_ip, first->port);
if (first->bus_port == 0 || (first->bus_port == first->port + CLUSTER_MANAGER_PORT_INCR)) {
/* CLUSTER MEET bus-port parameter was added in 4.0.
* So if (bus_port == 0) or (bus_port == port + CLUSTER_MANAGER_PORT_INCR),
* we just call CLUSTER MEET with 2 arguments, using the old form. */
reply = CLUSTER_MANAGER_COMMAND(new_node, "CLUSTER MEET %s %d",
first_ip, first->port);
} else {
reply = CLUSTER_MANAGER_COMMAND(new_node, "CLUSTER MEET %s %d %d",
first->ip, first->port, first->bus_port);
}
if (!(success = clusterManagerCheckRedisReply(new_node, reply, NULL)))
goto cleanup;
@@ -6545,7 +6564,7 @@ static int clusterManagerCommandDeleteNode(int argc, char **argv) {
char *node_id = argv[1];
clusterManagerLogInfo(">>> Removing node %s from cluster %s:%d\n",
node_id, ip, port);
clusterManagerNode *ref_node = clusterManagerNewNode(ip, port);
clusterManagerNode *ref_node = clusterManagerNewNode(ip, port, 0);
clusterManagerNode *node = NULL;
// Load cluster information
@@ -6607,7 +6626,7 @@ static int clusterManagerCommandInfo(int argc, char **argv) {
int port = 0;
char *ip = NULL;
if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
clusterManagerNode *node = clusterManagerNewNode(ip, port);
clusterManagerNode *node = clusterManagerNewNode(ip, port, 0);
if (!clusterManagerLoadInfoFromNode(node)) return 0;
clusterManagerShowClusterInfo();
return 1;
@@ -6620,7 +6639,7 @@ static int clusterManagerCommandCheck(int argc, char **argv) {
int port = 0;
char *ip = NULL;
if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
clusterManagerNode *node = clusterManagerNewNode(ip, port);
clusterManagerNode *node = clusterManagerNewNode(ip, port, 0);
if (!clusterManagerLoadInfoFromNode(node)) return 0;
clusterManagerShowClusterInfo();
return clusterManagerCheckCluster(0);
@@ -6638,7 +6657,7 @@ static int clusterManagerCommandReshard(int argc, char **argv) {
int port = 0;
char *ip = NULL;
if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
clusterManagerNode *node = clusterManagerNewNode(ip, port);
clusterManagerNode *node = clusterManagerNewNode(ip, port, 0);
if (!clusterManagerLoadInfoFromNode(node)) return 0;
clusterManagerCheckCluster(0);
if (cluster_manager.errors && listLength(cluster_manager.errors) > 0) {
@@ -6827,7 +6846,7 @@ static int clusterManagerCommandRebalance(int argc, char **argv) {
clusterManagerNode **weightedNodes = NULL;
list *involved = NULL;
if (!getClusterHostFromCmdArgs(argc, argv, &ip, &port)) goto invalid_args;
clusterManagerNode *node = clusterManagerNewNode(ip, port);
clusterManagerNode *node = clusterManagerNewNode(ip, port, 0);
if (!clusterManagerLoadInfoFromNode(node)) return 0;
int result = 1, i;
if (config.cluster_manager_command.weight != NULL) {
@@ -7028,7 +7047,7 @@ static int clusterManagerCommandSetTimeout(int argc, char **argv) {
return 0;
}
// Load cluster information
clusterManagerNode *node = clusterManagerNewNode(ip, port);
clusterManagerNode *node = clusterManagerNewNode(ip, port, 0);
if (!clusterManagerLoadInfoFromNode(node)) return 0;
int ok_count = 0, err_count = 0;
@@ -7098,7 +7117,7 @@ static int clusterManagerCommandImport(int argc, char **argv) {
clusterManagerLogInfo(">>> Importing data from %s:%d to cluster %s:%d\n",
src_ip, src_port, ip, port);
clusterManagerNode *refnode = clusterManagerNewNode(ip, port);
clusterManagerNode *refnode = clusterManagerNewNode(ip, port, 0);
if (!clusterManagerLoadInfoFromNode(refnode)) return 0;
if (!clusterManagerCheckCluster(0)) return 0;
char *reply_err = NULL;
@@ -7233,7 +7252,7 @@ static int clusterManagerCommandCall(int argc, char **argv) {
int port = 0, i;
char *ip = NULL;
if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;
clusterManagerNode *refnode = clusterManagerNewNode(ip, port);
clusterManagerNode *refnode = clusterManagerNewNode(ip, port, 0);
if (!clusterManagerLoadInfoFromNode(refnode)) return 0;
argc--;
argv++;
@@ -7278,7 +7297,7 @@ static int clusterManagerCommandBackup(int argc, char **argv) {
int success = 1, port = 0;
char *ip = NULL;
if (!getClusterHostFromCmdArgs(1, argv, &ip, &port)) goto invalid_args;
clusterManagerNode *refnode = clusterManagerNewNode(ip, port);
clusterManagerNode *refnode = clusterManagerNewNode(ip, port, 0);
if (!clusterManagerLoadInfoFromNode(refnode)) return 0;
int no_issues = clusterManagerCheckCluster(0);
int cluster_errors_count = (no_issues ? 0 :
@@ -7489,7 +7508,7 @@ struct distsamples {
* of the collected samples targeting an xterm 256 terminal.
*
* Takes an array of distsamples structures, ordered from smaller to bigger
* 'max' value. Last sample max must be 0, to mean that it olds all the
* 'max' value. Last sample max must be 0, to mean that it holds all the
* samples greater than the previous one, and is also the stop sentinel.
*
* "tot' is the total number of samples in the different buckets, so it
+1 -1
View File
@@ -43,7 +43,7 @@
#define assert(_e) (likely((_e))?(void)0 : (_serverAssert(#_e,__FILE__,__LINE__),redis_unreachable()))
#define panic(...) _serverPanic(__FILE__,__LINE__,__VA_ARGS__),redis_unreachable()
void _serverAssert(char *estr, char *file, int line);
void _serverAssert(const char *estr, const char *file, int line);
void _serverPanic(const char *file, int line, const char *msg, ...);
#endif
+10
View File
@@ -656,6 +656,8 @@ typedef struct RedisModuleClientInfo {
#define RedisModuleClientInfo RedisModuleClientInfoV1
#define REDISMODULE_CLIENTINFO_INITIALIZER_V1 { .version = 1 }
#define REDISMODULE_REPLICATIONINFO_VERSION 1
typedef struct RedisModuleReplicationInfo {
uint64_t version; /* Not used since this structure is never passed
@@ -915,6 +917,7 @@ REDISMODULE_API size_t (*RedisModule_CallReplyLength)(RedisModuleCallReply *repl
REDISMODULE_API RedisModuleCallReply * (*RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromULongLong)(RedisModuleCtx *ctx, unsigned long long ull) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str) REDISMODULE_ATTR;
@@ -948,6 +951,7 @@ REDISMODULE_API int (*RedisModule_ReplyWithDouble)(RedisModuleCtx *ctx, double d
REDISMODULE_API int (*RedisModule_ReplyWithBigNumber)(RedisModuleCtx *ctx, const char *bignum, size_t len) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithCallReply)(RedisModuleCtx *ctx, RedisModuleCallReply *reply) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringToLongLong)(const RedisModuleString *str, long long *ll) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringToULongLong)(const RedisModuleString *str, unsigned long long *ull) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringToDouble)(const RedisModuleString *str, double *d) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringToLongDouble)(const RedisModuleString *str, long double *d) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_StringToStreamID)(const RedisModuleString *str, RedisModuleStreamID *id) REDISMODULE_ATTR;
@@ -1000,6 +1004,8 @@ REDISMODULE_API void (*RedisModule_ChannelAtPosWithFlags)(RedisModuleCtx *ctx, i
REDISMODULE_API unsigned long long (*RedisModule_GetClientId)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_GetClientUserNameById)(RedisModuleCtx *ctx, uint64_t id) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetClientInfoById)(void *ci, uint64_t id) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_GetClientNameById)(RedisModuleCtx *ctx, uint64_t id) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SetClientNameById)(uint64_t id, RedisModuleString *name) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_PublishMessageShard)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetContextFlags)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
@@ -1258,6 +1264,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(ListInsert);
REDISMODULE_GET_API(ListDelete);
REDISMODULE_GET_API(StringToLongLong);
REDISMODULE_GET_API(StringToULongLong);
REDISMODULE_GET_API(StringToDouble);
REDISMODULE_GET_API(StringToLongDouble);
REDISMODULE_GET_API(StringToStreamID);
@@ -1280,6 +1287,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(CreateStringFromCallReply);
REDISMODULE_GET_API(CreateString);
REDISMODULE_GET_API(CreateStringFromLongLong);
REDISMODULE_GET_API(CreateStringFromULongLong);
REDISMODULE_GET_API(CreateStringFromDouble);
REDISMODULE_GET_API(CreateStringFromLongDouble);
REDISMODULE_GET_API(CreateStringFromString);
@@ -1426,6 +1434,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(ServerInfoGetFieldUnsigned);
REDISMODULE_GET_API(ServerInfoGetFieldDouble);
REDISMODULE_GET_API(GetClientInfoById);
REDISMODULE_GET_API(GetClientNameById);
REDISMODULE_GET_API(SetClientNameById);
REDISMODULE_GET_API(PublishMessage);
REDISMODULE_GET_API(PublishMessageShard);
REDISMODULE_GET_API(SubscribeToServerEvent);
+11 -1
View File
@@ -2151,6 +2151,16 @@ void readSyncBulkPayload(connection *conn) {
/* Close old rdb asynchronously. */
if (old_rdb_fd != -1) bioCreateCloseJob(old_rdb_fd);
/* Sync the directory to ensure rename is persisted */
if (fsyncFileDir(server.rdb_filename) == -1) {
serverLog(LL_WARNING,
"Failed trying to sync DB directory %s in "
"MASTER <-> REPLICA synchronization: %s",
server.rdb_filename, strerror(errno));
cancelReplicationHandshake(1);
return;
}
if (rdbLoad(server.rdb_filename,&rsi,RDBFLAGS_REPLICATION) != C_OK) {
serverLog(LL_WARNING,
"Failed trying to load the MASTER synchronization "
@@ -3310,7 +3320,7 @@ void replicationDiscardCachedMaster(void) {
* passed as argument as the socket for the new master.
*
* This function is called when successfully setup a partial resynchronization
* so the stream of data that we'll receive will start from were this
* so the stream of data that we'll receive will start from where this
* master left. */
void replicationResurrectCachedMaster(connection *conn) {
server.master = server.cached_master;
+5
View File
@@ -363,6 +363,11 @@ static int scriptVerifyWriteCommandAllow(scriptRunCtx *run_ctx, char **err) {
if (!(run_ctx->c->cmd->flags & CMD_WRITE))
return C_OK;
/* If the script already made a modification to the dataset, we can't
* fail it on unpredictable error state. */
if ((run_ctx->flags & SCRIPT_WRITE_DIRTY))
return C_OK;
/* Write commands are forbidden against read-only slaves, or if a
* command marked as non-deterministic was already called in the context
* of this script. */
+75 -22
View File
@@ -48,6 +48,7 @@
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/uio.h>
@@ -634,7 +635,7 @@ void resetChildState() {
NULL);
}
/* Return if child type is mutual exclusive with other fork children */
/* Return if child type is mutually exclusive with other fork children */
int isMutuallyExclusiveChildType(int type) {
return type == CHILD_TYPE_RDB || type == CHILD_TYPE_AOF || type == CHILD_TYPE_MODULE;
}
@@ -2793,8 +2794,23 @@ int populateArgsStructure(struct redisCommandArg *args) {
return count;
}
/* Recursively populate the command structure. */
void populateCommandStructure(struct redisCommand *c) {
/* Recursively populate the command structure.
*
* On success, the function return C_OK. Otherwise C_ERR is returned and we won't
* add this command in the commands dict. */
int populateCommandStructure(struct redisCommand *c) {
/* If the command marks with CMD_SENTINEL, it exists in sentinel. */
if (!(c->flags & CMD_SENTINEL) && server.sentinel_mode)
return C_ERR;
/* If the command marks with CMD_ONLY_SENTINEL, it only exists in sentinel. */
if (c->flags & CMD_ONLY_SENTINEL && !server.sentinel_mode)
return C_ERR;
/* Translate the command string flags description into an actual
* set of flags. */
setImplicitACLCategories(c);
/* Redis commands don't need more args than STATIC_KEY_SPECS_NUM (Number of keys
* specs can be greater than STATIC_KEY_SPECS_NUM only for module commands) */
c->key_specs = c->key_specs_static;
@@ -2828,14 +2844,15 @@ void populateCommandStructure(struct redisCommand *c) {
for (int j = 0; c->subcommands[j].declared_name; j++) {
struct redisCommand *sub = c->subcommands+j;
/* Translate the command string flags description into an actual
* set of flags. */
setImplicitACLCategories(sub);
sub->fullname = catSubCommandFullname(c->declared_name, sub->declared_name);
populateCommandStructure(sub);
if (populateCommandStructure(sub) == C_ERR)
continue;
commandAddSubcommand(c, sub, sub->declared_name);
}
}
return C_OK;
}
extern struct redisCommand redisCommandTable[];
@@ -2853,16 +2870,9 @@ void populateCommandTable(void) {
int retval1, retval2;
setImplicitACLCategories(c);
if (!(c->flags & CMD_SENTINEL) && server.sentinel_mode)
continue;
if (c->flags & CMD_ONLY_SENTINEL && !server.sentinel_mode)
continue;
c->fullname = sdsnew(c->declared_name);
populateCommandStructure(c);
if (populateCommandStructure(c) == C_ERR)
continue;
retval1 = dictAdd(server.commands, sdsdup(c->fullname), c);
/* Populate an additional dictionary that will be unaffected
@@ -3584,8 +3594,8 @@ int commandCheckArity(client *c, sds *err) {
* if C_ERR is returned the client was destroyed (i.e. after QUIT). */
int processCommand(client *c) {
if (!scriptIsTimedout()) {
/* Both EXEC and EVAL call call() directly so there should be
* no way in_exec or in_eval is 1.
/* Both EXEC and scripts call call() directly so there should be
* no way in_exec or scriptIsRunning() is 1.
* That is unless lua_timedout, in which case client may run
* some commands. */
serverAssert(!server.in_exec);
@@ -4207,6 +4217,12 @@ int finishShutdown(void) {
/* Close the listening sockets. Apparently this allows faster restarts. */
closeListeningSockets(1);
/* Unlock the cluster config file before shutdown */
if (server.cluster_enabled && server.cluster_config_file_lock_fd != -1) {
flock(server.cluster_config_file_lock_fd, LOCK_UN|LOCK_NB);
}
serverLog(LL_WARNING,"%s is now ready to exit, bye bye...",
server.sentinel_mode ? "Sentinel" : "Redis");
return C_OK;
@@ -5645,6 +5661,7 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
"keyspace_misses:%lld\r\n"
"pubsub_channels:%ld\r\n"
"pubsub_patterns:%lu\r\n"
"pubsubshard_channels:%lu\r\n"
"latest_fork_usec:%lld\r\n"
"total_forks:%lld\r\n"
"migrate_cached_sockets:%ld\r\n"
@@ -5694,6 +5711,7 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
server.stat_keyspace_misses,
dictSize(server.pubsub_channels),
dictSize(server.pubsub_patterns),
dictSize(server.pubsubshard_channels),
server.stat_fork_time,
server.stat_total_forks,
dictSize(server.migrate_cached_sockets),
@@ -6094,9 +6112,12 @@ void usage(void) {
fprintf(stderr," ./redis-server - (read config from stdin)\n");
fprintf(stderr," ./redis-server -v or --version\n");
fprintf(stderr," ./redis-server -h or --help\n");
fprintf(stderr," ./redis-server --test-memory <megabytes>\n\n");
fprintf(stderr," ./redis-server --test-memory <megabytes>\n");
fprintf(stderr," ./redis-server --check-system\n");
fprintf(stderr,"\n");
fprintf(stderr,"Examples:\n");
fprintf(stderr," ./redis-server (run the server with default conf)\n");
fprintf(stderr," echo 'maxmemory 128mb' | ./redis-server -\n");
fprintf(stderr," ./redis-server /etc/redis/6379.conf\n");
fprintf(stderr," ./redis-server --port 7777\n");
fprintf(stderr," ./redis-server --port 7777 --replicaof 127.0.0.1 8888\n");
@@ -6347,7 +6368,7 @@ int redisFork(int purpose) {
server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
/* The child_pid and child_type are only for mutual exclusive children.
/* The child_pid and child_type are only for mutually exclusive children.
* other child types should handle and store their pid's in dedicated variables.
*
* Today, we allows CHILD_TYPE_LDB to run in parallel with the other fork types:
@@ -6433,7 +6454,7 @@ void dismissClientMemory(client *c) {
/* In the child process, we don't need some buffers anymore, and these are
* likely to change in the parent when there's heavy write traffic.
* We dismis them right away, to avoid CoW.
* We dismiss them right away, to avoid CoW.
* see dismissMemeory(). */
void dismissMemoryInChild(void) {
/* madvise(MADV_DONTNEED) may not work if Transparent Huge Pages is enabled. */
@@ -6880,6 +6901,8 @@ int main(int argc, char **argv) {
server.exec_argv[1] = zstrdup(server.configfile);
j = 2; // Skip this arg when parsing options
}
sds *argv_tmp;
int argc_tmp;
int handled_last_config_arg = 1;
while(j < argc) {
/* Either first or last argument - Should we read config from stdin? */
@@ -6897,7 +6920,37 @@ int main(int argc, char **argv) {
/* argv[j]+2 for removing the preceding `--` */
options = sdscat(options,argv[j]+2);
options = sdscat(options," ");
handled_last_config_arg = 0;
argv_tmp = sdssplitargs(argv[j], &argc_tmp);
if (argc_tmp == 1) {
/* Means that we only have one option name, like --port or "--port " */
handled_last_config_arg = 0;
if ((j != argc-1) && argv[j+1][0] == '-' && argv[j+1][1] == '-' &&
!strcasecmp(argv[j], "--save"))
{
/* Special case: handle some things like `--save --config value`.
* In this case, if next argument starts with `--`, we will reset
* handled_last_config_arg flag and append an empty "" config value
* to the options, so it will become `--save "" --config value`.
* We are doing it to be compatible with pre 7.0 behavior (which we
* break it in #10660, 7.0.1), since there might be users who generate
* a command line from an array and when it's empty that's what they produce. */
options = sdscat(options, "\"\"");
handled_last_config_arg = 1;
}
else if ((j == argc-1) && !strcasecmp(argv[j], "--save")) {
/* Special case: when empty save is the last argument.
* In this case, we append an empty "" config value to the options,
* so it will become `--save ""` and will follow the same reset thing. */
options = sdscat(options, "\"\"");
}
} else {
/* Means that we are passing both config name and it's value in the same arg,
* like "--port 6380", so we need to reset handled_last_config_arg flag. */
handled_last_config_arg = 1;
}
sdsfreesplitres(argv_tmp, argc_tmp);
} else {
/* Option argument */
options = sdscatrepr(options,argv[j],strlen(argv[j]));
+9 -7
View File
@@ -959,6 +959,7 @@ typedef struct multiState {
is possible to know if all the commands have a
certain flag. */
size_t argv_len_sums; /* mem used by all commands arguments */
int alloc_count; /* total number of multiCmd struct memory reserved. */
} multiState;
/* This structure holds the blocking operation state for a client.
@@ -1857,7 +1858,7 @@ struct redisServer {
dict *pubsub_patterns; /* A dict of pubsub_patterns */
int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an
xor of NOTIFY_... flags. */
dict *pubsubshard_channels; /* Map channels to list of subscribed clients */
dict *pubsubshard_channels; /* Map shard channels to list of subscribed clients */
/* Cluster */
int cluster_enabled; /* Is cluster enabled? */
int cluster_port; /* Set the cluster port for a node. */
@@ -2186,13 +2187,14 @@ typedef int redisGetKeysProc(struct redisCommand *cmd, robj **argv, int argc, ge
* or may just execute read commands. A command can not be marked
* both CMD_WRITE and CMD_MAY_REPLICATE
*
* CMD_SENTINEL: This command is present in sentinel mode too.
* CMD_SENTINEL: This command is present in sentinel mode.
*
* CMD_SENTINEL_ONLY: This command is present only when in sentinel mode.
* CMD_ONLY_SENTINEL: This command is present only when in sentinel mode.
* And should be removed from redis.
*
* CMD_NO_MANDATORY_KEYS: This key arguments for this command are optional.
*
* CMD_NO_MULTI: The command is nt allowed inside a transaction
* CMD_NO_MULTI: The command is not allowed inside a transaction
*
* The following additional flags are only used in order to put commands
* in a specific ACL category. Commands can have multiple ACL categories.
@@ -2242,7 +2244,6 @@ struct redisCommand {
struct redisCommandArg *args;
/* Runtime populated data */
/* What keys should be loaded in background when calling this command? */
long long microseconds, calls, rejected_calls, failed_calls;
int id; /* Command ID. This is a progressive ID starting from 0 that
is assigned at runtime, and is used in order to check
@@ -2498,6 +2499,7 @@ char *getClientPeerId(client *client);
char *getClientSockName(client *client);
sds catClientInfoString(sds s, client *client);
sds getAllClientsInfoString(int type);
int clientSetName(client *c, robj *name);
void rewriteClientCommandVector(client *c, int argc, ...);
void rewriteClientCommandArgument(client *c, int i, robj *newval);
void replaceClientCommandVector(client *c, int argc, robj **argv);
@@ -2607,7 +2609,6 @@ void decrRefCount(robj *o);
void decrRefCountVoid(void *o);
void incrRefCount(robj *o);
robj *makeObjectShared(robj *o);
robj *resetRefCount(robj *obj);
void freeStringObject(robj *o);
void freeListObject(robj *o);
void freeSetObject(robj *o);
@@ -2752,7 +2753,7 @@ void sendChildInfo(childInfoType info_type, size_t keys, char *pname);
void receiveChildInfo(void);
/* Fork helpers */
int redisFork(int type);
int redisFork(int purpose);
int hasActiveChildProcess();
void resetChildState();
int isMutuallyExclusiveChildType(int type);
@@ -2998,6 +2999,7 @@ int pubsubPublishMessageAndPropagateToCluster(robj *channel, robj *message, int
void addReplyPubsubMessage(client *c, robj *channel, robj *msg, robj *message_bulk);
int serverPubsubSubscriptionCount();
int serverPubsubShardSubscriptionCount();
size_t pubsubMemOverhead(client *c);
/* Keyspace events notification */
void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid);
+1 -1
View File
@@ -150,7 +150,7 @@ int checkOvercommit(sds *error_msg) {
}
fclose(fp);
if (atoi(buf)) {
if (strtol(buf, NULL, 10) == 0) {
*error_msg = sdsnew(
"overcommit_memory is set to 0! Background save may fail under low memory condition. "
"To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the "
+4 -2
View File
@@ -2952,8 +2952,10 @@ static void zrangeResultFinalizeClient(zrange_result_handler *handler,
/* Result handler methods for storing the ZRANGESTORE to a zset. */
static void zrangeResultBeginStore(zrange_result_handler *handler, long length)
{
UNUSED(length);
handler->dstobj = createZsetListpackObject();
if (length > (long)server.zset_max_listpack_entries)
handler->dstobj = createZsetObject();
else
handler->dstobj = createZsetListpackObject();
}
static void zrangeResultEmitCBufferForStore(zrange_result_handler *handler,
+5 -3
View File
@@ -722,6 +722,8 @@ static void connTLSClose(connection *conn_) {
tls_connection *conn = (tls_connection *) conn_;
if (conn->ssl) {
if (conn->c.state == CONN_STATE_CONNECTED)
SSL_shutdown(conn->ssl);
SSL_free(conn->ssl);
conn->ssl = NULL;
}
@@ -834,12 +836,12 @@ static int connTLSWritev(connection *conn_, const struct iovec *iov, int iovcnt)
* which is not worth doing so much memory copying to reduce system calls,
* therefore, invoke connTLSWrite() multiple times to avoid memory copies. */
if (iov_bytes_len > NET_MAX_WRITES_PER_EVENT) {
size_t tot_sent = 0;
ssize_t tot_sent = 0;
for (int i = 0; i < iovcnt; i++) {
size_t sent = connTLSWrite(conn_, iov[i].iov_base, iov[i].iov_len);
ssize_t sent = connTLSWrite(conn_, iov[i].iov_base, iov[i].iov_len);
if (sent <= 0) return tot_sent > 0 ? tot_sent : sent;
tot_sent += sent;
if (sent != iov[i].iov_len) break;
if ((size_t) sent != iov[i].iov_len) break;
}
return tot_sent;
}
+49
View File
@@ -43,6 +43,7 @@
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <libgen.h>
#include "util.h"
#include "sha256.h"
@@ -923,6 +924,54 @@ sds makePath(char *path, char *filename) {
return sdscatfmt(sdsempty(), "%s/%s", path, filename);
}
/* Given the filename, sync the corresponding directory.
*
* Usually a portable and safe pattern to overwrite existing files would be like:
* 1. create a new temp file (on the same file system!)
* 2. write data to the temp file
* 3. fsync() the temp file
* 4. rename the temp file to the appropriate name
* 5. fsync() the containing directory */
int fsyncFileDir(const char *filename) {
#ifdef _AIX
/* AIX is unable to fsync a directory */
return 0;
#endif
char temp_filename[PATH_MAX + 1];
char *dname;
int dir_fd;
if (strlen(filename) > PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
/* In the glibc implementation dirname may modify their argument. */
memcpy(temp_filename, filename, strlen(filename) + 1);
dname = dirname(temp_filename);
dir_fd = open(dname, O_RDONLY);
if (dir_fd == -1) {
/* Some OSs don't allow us to open directories at all, just
* ignore the error in that case */
if (errno == EISDIR) {
return 0;
}
return -1;
}
/* Some OSs don't allow us to fsync directories at all, so we can ignore
* those errors. */
if (redis_fsync(dir_fd) == -1 && !(errno == EBADF || errno == EINVAL)) {
int save_errno = errno;
close(dir_fd);
errno = save_errno;
return -1;
}
close(dir_fd);
return 0;
}
#ifdef REDIS_TEST
#include <assert.h>
+1
View File
@@ -85,6 +85,7 @@ int dirExists(char *dname);
int dirRemove(char *dname);
int fileExist(char *filename);
sds makePath(char *path, char *filename);
int fsyncFileDir(const char *filename);
#ifdef REDIS_TEST
int utilTest(int argc, char **argv, int flags);
+13
View File
@@ -80,3 +80,16 @@ test "Script no-cluster flag" {
assert_match {*Can not run script on cluster, 'no-cluster' flag is set*} $e
}
test "CLUSTER RESET SOFT test" {
set last_epoch_node0 [get_info_field [R 0 cluster info] cluster_current_epoch]
R 0 FLUSHALL
R 0 CLUSTER RESET
assert {[get_info_field [R 0 cluster info] cluster_current_epoch] eq $last_epoch_node0}
set last_epoch_node1 [get_info_field [R 1 cluster info] cluster_current_epoch]
R 1 FLUSHALL
R 1 CLUSTER RESET SOFT
assert {[get_info_field [R 1 cluster info] cluster_current_epoch] eq $last_epoch_node1}
}
+18 -1
View File
@@ -182,4 +182,21 @@ test "Test the replica reports a loading state while it's loading" {
# Final sanity, the replica agrees it is online.
assert_equal "online" [dict get [get_node_info_from_shard $replica_cluster_id $replica_id "node"] health]
}
}
test "Regression test for a crash when calling SHARDS during handshake" {
# Reset forget a node, so we can use it to establish handshaking connections
set id [R 19 CLUSTER MYID]
R 19 CLUSTER RESET HARD
for {set i 0} {$i < 19} {incr i} {
R $i CLUSTER FORGET $id
}
R 19 cluster meet 127.0.0.1 [get_instance_attrib redis 0 port]
# This should line would previously crash, since all the outbound
# connections were in handshake state.
R 19 CLUSTER SHARDS
}
test "Cluster is up" {
assert_cluster_state ok
}
@@ -0,0 +1,50 @@
# Tests for the response of slot migrations.
source "../tests/includes/init-tests.tcl"
source "../tests/includes/utils.tcl"
test "Create a 2 nodes cluster" {
create_cluster 2 0
config_set_all_nodes cluster-allow-replica-migration no
}
test "Cluster is up" {
assert_cluster_state ok
}
set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
catch {unset nodefrom}
catch {unset nodeto}
$cluster refresh_nodes_map
test "Set many keys in the cluster" {
for {set i 0} {$i < 5000} {incr i} {
$cluster set $i $i
assert { [$cluster get $i] eq $i }
}
}
test "Test cluster responses during migration of slot x" {
set slot 10
array set nodefrom [$cluster masternode_for_slot $slot]
array set nodeto [$cluster masternode_notfor_slot $slot]
$nodeto(link) cluster setslot $slot importing $nodefrom(id)
$nodefrom(link) cluster setslot $slot migrating $nodeto(id)
# Get a key from that slot
set key [$nodefrom(link) cluster GETKEYSINSLOT $slot "1"]
# MOVED REPLY
assert_error "*MOVED*" {$nodeto(link) set $key "newVal"}
# ASK REPLY
assert_error "*ASK*" {$nodefrom(link) set "abc{$key}" "newVal"}
# UNSTABLE REPLY
assert_error "*TRYAGAIN*" {$nodefrom(link) mset "a{$key}" "newVal" $key "newVal2"}
}
config_set_all_nodes cluster-allow-replica-migration yes
+9
View File
@@ -116,6 +116,15 @@ start_server {tags {"benchmark network external:skip"}} {
# ensure the keyspace has the desired size
assert_match {50} [scan [regexp -inline {keys\=([\d]*)} [r info keyspace]] keys=%d]
}
test {benchmark: clients idle mode should return error when reached maxclients limit} {
set cmd [redisbenchmark $master_host $master_port "-c 10 -I"]
set original_maxclients [lindex [r config get maxclients] 1]
r config set maxclients 5
catch { exec {*}$cmd } error
assert_match "*Error*" $error
r config set maxclients $original_maxclients
}
# tls specific tests
if {$::tls} {
+18 -2
View File
@@ -7,6 +7,7 @@
#include <assert.h>
#include <stdio.h>
#include <pthread.h>
#include <strings.h>
#define UNUSED(V) ((void) V)
@@ -119,8 +120,20 @@ void *bg_call_worker(void *arg) {
}
// Call the command
const char* cmd = RedisModule_StringPtrLen(bg->argv[1], NULL);
RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "v", bg->argv + 2, bg->argc - 2);
const char *module_cmd = RedisModule_StringPtrLen(bg->argv[0], NULL);
int cmd_pos = 1;
RedisModuleString *format_redis_str = RedisModule_CreateString(NULL, "v", 1);
if (!strcasecmp(module_cmd, "do_bg_rm_call_format")) {
cmd_pos = 2;
size_t format_len;
const char *format = RedisModule_StringPtrLen(bg->argv[1], &format_len);
RedisModule_StringAppendBuffer(NULL, format_redis_str, format, format_len);
RedisModule_StringAppendBuffer(NULL, format_redis_str, "E", 1);
}
const char *format = RedisModule_StringPtrLen(format_redis_str, NULL);
const char *cmd = RedisModule_StringPtrLen(bg->argv[cmd_pos], NULL);
RedisModuleCallReply *rep = RedisModule_Call(ctx, cmd, format, bg->argv + cmd_pos + 1, bg->argc - cmd_pos - 1);
RedisModule_FreeString(NULL, format_redis_str);
// Release GIL
RedisModule_ThreadSafeContextUnlock(ctx);
@@ -306,6 +319,9 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
if (RedisModule_CreateCommand(ctx, "do_bg_rm_call", do_bg_rm_call, "", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "do_bg_rm_call_format", do_bg_rm_call, "", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "do_fake_bg_true", do_fake_bg_true, "", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
+100 -2
View File
@@ -4,6 +4,7 @@
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#define UNUSED(x) (void)(x)
@@ -239,9 +240,17 @@ int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
(void) argv;
(void) argc;
RedisModuleClientInfo ci = { .version = REDISMODULE_CLIENTINFO_VERSION };
RedisModuleClientInfoV1 ci = REDISMODULE_CLIENTINFO_INITIALIZER_V1;
uint64_t client_id = RedisModule_GetClientId(ctx);
if (RedisModule_GetClientInfoById(&ci, RedisModule_GetClientId(ctx)) == REDISMODULE_ERR) {
/* Check expected result from the V1 initializer. */
assert(ci.version == 1);
/* Trying to populate a future version of the struct should fail. */
ci.version = REDISMODULE_CLIENTINFO_VERSION + 1;
assert(RedisModule_GetClientInfoById(&ci, client_id) == REDISMODULE_ERR);
ci.version = 1;
if (RedisModule_GetClientInfoById(&ci, client_id) == REDISMODULE_ERR) {
RedisModule_ReplyWithError(ctx, "failed to get client info");
return REDISMODULE_OK;
}
@@ -270,6 +279,27 @@ int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_OK;
}
int test_getname(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
(void)argv;
if (argc != 1) return RedisModule_WrongArity(ctx);
unsigned long long id = RedisModule_GetClientId(ctx);
RedisModuleString *name = RedisModule_GetClientNameById(ctx, id);
if (name == NULL)
return RedisModule_ReplyWithError(ctx, "-ERR No name");
RedisModule_ReplyWithString(ctx, name);
RedisModule_FreeString(ctx, name);
return REDISMODULE_OK;
}
int test_setname(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 2) return RedisModule_WrongArity(ctx);
unsigned long long id = RedisModule_GetClientId(ctx);
if (RedisModule_SetClientNameById(id, argv[1]) == REDISMODULE_OK)
return RedisModule_ReplyWithSimpleString(ctx, "OK");
else
return RedisModule_ReplyWithError(ctx, strerror(errno));
}
int test_log_tsctx(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
RedisModuleCtx *tsctx = RedisModule_GetDetachedThreadSafeContext(ctx);
@@ -354,6 +384,68 @@ int test_rm_call_flags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
return REDISMODULE_OK;
}
int test_ull_conv(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv);
UNUSED(argc);
unsigned long long ull = 18446744073709551615ULL;
const char *ullstr = "18446744073709551615";
RedisModuleString *s1 = RedisModule_CreateStringFromULongLong(ctx, ull);
RedisModuleString *s2 =
RedisModule_CreateString(ctx, ullstr, strlen(ullstr));
if (RedisModule_StringCompare(s1, s2) != 0) {
char err[4096];
snprintf(err, 4096,
"Failed to convert unsigned long long to string ('%s' != '%s')",
RedisModule_StringPtrLen(s1, NULL),
RedisModule_StringPtrLen(s2, NULL));
RedisModule_ReplyWithError(ctx, err);
goto final;
}
unsigned long long ull2 = 0;
if (RedisModule_StringToULongLong(s2, &ull2) == REDISMODULE_ERR) {
RedisModule_ReplyWithError(ctx,
"Failed to convert string to unsigned long long");
goto final;
}
if (ull2 != ull) {
char err[4096];
snprintf(err, 4096,
"Failed to convert string to unsigned long long (%llu != %llu)",
ull2,
ull);
RedisModule_ReplyWithError(ctx, err);
goto final;
}
/* Make sure we can't convert a string more than ULLONG_MAX or less than 0 */
ullstr = "18446744073709551616";
RedisModuleString *s3 = RedisModule_CreateString(ctx, ullstr, strlen(ullstr));
unsigned long long ull3;
if (RedisModule_StringToULongLong(s3, &ull3) == REDISMODULE_OK) {
RedisModule_ReplyWithError(ctx, "Invalid string successfully converted to unsigned long long");
RedisModule_FreeString(ctx, s3);
goto final;
}
RedisModule_FreeString(ctx, s3);
ullstr = "-1";
RedisModuleString *s4 = RedisModule_CreateString(ctx, ullstr, strlen(ullstr));
unsigned long long ull4;
if (RedisModule_StringToULongLong(s4, &ull4) == REDISMODULE_OK) {
RedisModule_ReplyWithError(ctx, "Invalid string successfully converted to unsigned long long");
RedisModule_FreeString(ctx, s4);
goto final;
}
RedisModule_FreeString(ctx, s4);
RedisModule_ReplyWithSimpleString(ctx, "ok");
final:
RedisModule_FreeString(ctx, s1);
RedisModule_FreeString(ctx, s2);
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
@@ -366,6 +458,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.ld_conversion", test_ld_conv, "",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.ull_conversion", test_ull_conv, "",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.flushall", test_flushall,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.dbsize", test_dbsize,"",0,0,0) == REDISMODULE_ERR)
@@ -384,6 +478,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.getname", test_getname,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.setname", test_setname,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.redisversion", test_redisversion,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.getclientcert", test_getclientcert,"",0,0,0) == REDISMODULE_ERR)
+7
View File
@@ -12,6 +12,13 @@ if {$::simulate_error} {
}
}
test "Sentinel commands sanity check" {
foreach_sentinel_id id {
assert_equal {72} [llength [S $id command list]]
assert_equal {15} [S $id command count]
}
}
test "Basic failover works if the master is down" {
set old_port [RPort $master_id]
set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]
+7 -5
View File
@@ -126,7 +126,7 @@ test "Sentinels (re)connection following master ACL change" {
wait_for_condition 100 50 {
[string match "*disconnected*" [dict get [S $sent2re SENTINEL MASTER mymaster] flags]] != 0
} else {
fail "Expected: Sentinel to be disconnected from master due to wrong password"
fail "Expected: Restarted sentinel to be disconnected from master due to obsolete password"
}
# Verify sentinel with updated password managed to connect (wait for sentinelTimer to reconnect)
@@ -137,8 +137,10 @@ test "Sentinels (re)connection following master ACL change" {
}
# Verify sentinel untouched gets failed to connect master
if {![string match "*disconnected*" [dict get [S $sent2un SENTINEL MASTER mymaster] flags]]} {
fail "Expected: Sentinel to be disconnected from master due to wrong password"
wait_for_condition 100 50 {
[string match "*disconnected*" [dict get [S $sent2un SENTINEL MASTER mymaster] flags]] != 0
} else {
fail "Expected: Sentinel to be disconnected from master due to obsolete password"
}
# Now update all sentinels with new password
@@ -167,14 +169,14 @@ test "Set parameters in normal case" {
set origin_down_after_milliseconds [dict get $info down-after-milliseconds]
set update_quorum [expr $origin_quorum+1]
set update_down_after_milliseconds [expr $origin_down_after_milliseconds+1000]
assert_equal [S 0 SENTINEL SET mymaster quorum $update_quorum] "OK"
assert_equal [S 0 SENTINEL SET mymaster down-after-milliseconds $update_down_after_milliseconds] "OK"
set update_info [S 0 SENTINEL master mymaster]
assert {[dict get $update_info quorum] != $origin_quorum}
assert {[dict get $update_info down-after-milliseconds] != $origin_down_after_milliseconds}
#restore to origin config parameters
assert_equal [S 0 SENTINEL SET mymaster quorum $origin_quorum] "OK"
assert_equal [S 0 SENTINEL SET mymaster down-after-milliseconds $origin_down_after_milliseconds] "OK"
+34 -7
View File
@@ -67,6 +67,33 @@ proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}} {re
interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id
}
# On recent versions of tcl-tls/OpenSSL, reading from a dropped connection
# results with an error we need to catch and mimic the old behavior.
proc ::redis::redis_safe_read {fd len} {
if {$len == -1} {
set err [catch {set val [read $fd]} msg]
} else {
set err [catch {set val [read $fd $len]} msg]
}
if {!$err} {
return $val
}
if {[string match "*connection abort*" $msg]} {
return {}
}
error $msg
}
proc ::redis::redis_safe_gets {fd} {
if {[catch {set val [gets $fd]} msg]} {
if {[string match "*connection abort*" $msg]} {
return {}
}
error $msg
}
return $val
}
# This is a wrapper to the actual dispatching procedure that handles
# reconnection if needed.
proc ::redis::__dispatch__ {id method args} {
@@ -148,8 +175,8 @@ proc ::redis::__method__read {id fd} {
::redis::redis_read_reply $id $fd
}
proc ::redis::__method__rawread {id fd len} {
return [read $fd $len]
proc ::redis::__method__rawread {id fd {len -1}} {
return [redis_safe_read $fd $len]
}
proc ::redis::__method__write {id fd buf} {
@@ -207,8 +234,8 @@ proc ::redis::redis_writenl {fd buf} {
}
proc ::redis::redis_readnl {fd len} {
set buf [read $fd $len]
read $fd 2 ; # discard CR LF
set buf [redis_safe_read $fd $len]
redis_safe_read $fd 2 ; # discard CR LF
return $buf
}
@@ -254,11 +281,11 @@ proc ::redis::redis_read_map {id fd} {
}
proc ::redis::redis_read_line fd {
string trim [gets $fd]
string trim [redis_safe_gets $fd]
}
proc ::redis::redis_read_null fd {
gets $fd
redis_safe_gets $fd
return {}
}
@@ -281,7 +308,7 @@ proc ::redis::redis_read_reply {id fd} {
}
while {1} {
set type [read $fd 1]
set type [redis_safe_read $fd 1]
switch -exact -- $type {
_ {return [redis_read_null $fd]}
: -
+2 -2
View File
@@ -72,8 +72,8 @@ proc sanitizer_errors_from_file {filename} {
}
proc getInfoProperty {infostr property} {
if {[regexp "\r\n$property:(.*?)\r\n" $infostr _ value]} {
set _ $value
if {[regexp -lineanchor "^$property:(.*?)\r\n" $infostr _ value]} {
return $value
}
}
+14 -3
View File
@@ -156,13 +156,13 @@ start_server {} {
test "client evicted due to pubsub subscriptions" {
r flushdb
# Since pubsub subscriptions cause a small overheatd this test uses a minimal maxmemory-clients config
# Since pubsub subscriptions cause a small overhead this test uses a minimal maxmemory-clients config
set temp_maxmemory_clients 200000
r config set maxmemory-clients $temp_maxmemory_clients
# Test eviction due to pubsub patterns
set rr [redis_client]
# Add patterns until list maxes out maxmemroy clients and causes client eviction
# Add patterns until list maxes out maxmemory clients and causes client eviction
catch {
for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
$rr psubscribe $j
@@ -173,7 +173,7 @@ start_server {} {
# Test eviction due to pubsub channels
set rr [redis_client]
# Add patterns until list maxes out maxmemroy clients and causes client eviction
# Subscribe to global channels until list maxes out maxmemory clients and causes client eviction
catch {
for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
$rr subscribe $j
@@ -181,6 +181,17 @@ start_server {} {
} e
assert_match {I/O error reading reply} $e
$rr close
# Test eviction due to sharded pubsub channels
set rr [redis_client]
# Subscribe to sharded pubsub channels until list maxes out maxmemory clients and causes client eviction
catch {
for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
$rr ssubscribe $j
}
} e
assert_match {I/O error reading reply} $e
$rr close
# Restore config for next tests
r config set maxmemory-clients $maxmemory_clients
+86 -8
View File
@@ -3,9 +3,7 @@
source tests/support/cli.tcl
proc cluster_info {r field} {
if {[regexp "^$field:(.*?)\r\n" [$r cluster info] _ value]} {
set _ $value
}
set _ [getInfoProperty [$r cluster info] $field]
}
# Provide easy access to CLUSTER INFO properties. Same semantic as "proc s".
@@ -110,7 +108,7 @@ start_multiple_servers 3 [list overrides $base_conf] {
}
$node3_rd close
test "Run blocking command again on cluster node1" {
$node1 del key9184688
# key9184688 is mapped to slot 10923 which has been moved to node1
@@ -123,9 +121,9 @@ start_multiple_servers 3 [list overrides $base_conf] {
fail "Client not blocked"
}
}
test "Kill a cluster node and wait for fail state" {
# kill node3 in cluster
# kill node3 in cluster
exec kill -SIGSTOP $node3_pid
wait_for_condition 1000 50 {
@@ -135,7 +133,7 @@ start_multiple_servers 3 [list overrides $base_conf] {
fail "Cluster doesn't fail"
}
}
test "Verify command got unblocked after cluster failure" {
assert_error {*CLUSTERDOWN*} {$node1_rd read}
@@ -208,7 +206,7 @@ start_multiple_servers 5 [list overrides $base_conf] {
127.0.0.1:[srv -4 port] \
127.0.0.1:[srv 0 port]
} e
assert_match {*node already contains functions*} $e
assert_match {*node already contains functions*} $e
}
} ;# stop servers
@@ -315,6 +313,86 @@ test {Migrate the last slot away from a node using redis-cli} {
}
}
# Test redis-cli --cluster create, add-node with cluster-port.
# Create five nodes, three with custom cluster_port and two with default values.
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cluster-port [find_available_port $::baseport $::portcount]]] {
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1]] {
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cluster-port [find_available_port $::baseport $::portcount]]] {
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1]] {
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cluster-port [find_available_port $::baseport $::portcount]]] {
# The first three are used to test --cluster create.
# The last two are used to test --cluster add-node
set node1_rd [redis_client 0]
set node2_rd [redis_client -1]
set node3_rd [redis_client -2]
set node4_rd [redis_client -3]
set node5_rd [redis_client -4]
test {redis-cli --cluster create with cluster-port} {
exec src/redis-cli --cluster-yes --cluster create \
127.0.0.1:[srv 0 port] \
127.0.0.1:[srv -1 port] \
127.0.0.1:[srv -2 port]
wait_for_condition 1000 50 {
[csi 0 cluster_state] eq {ok} &&
[csi -1 cluster_state] eq {ok} &&
[csi -2 cluster_state] eq {ok}
} else {
fail "Cluster doesn't stabilize"
}
# Make sure each node can meet other nodes
assert_equal 3 [csi 0 cluster_known_nodes]
assert_equal 3 [csi -1 cluster_known_nodes]
assert_equal 3 [csi -2 cluster_known_nodes]
}
test {redis-cli --cluster add-node with cluster-port} {
# Adding node to the cluster (without cluster-port)
exec src/redis-cli --cluster-yes --cluster add-node \
127.0.0.1:[srv -3 port] \
127.0.0.1:[srv 0 port]
wait_for_condition 1000 50 {
[csi 0 cluster_state] eq {ok} &&
[csi -1 cluster_state] eq {ok} &&
[csi -2 cluster_state] eq {ok} &&
[csi -3 cluster_state] eq {ok}
} else {
fail "Cluster doesn't stabilize"
}
# Adding node to the cluster (with cluster-port)
exec src/redis-cli --cluster-yes --cluster add-node \
127.0.0.1:[srv -4 port] \
127.0.0.1:[srv 0 port]
wait_for_condition 1000 50 {
[csi 0 cluster_state] eq {ok} &&
[csi -1 cluster_state] eq {ok} &&
[csi -2 cluster_state] eq {ok} &&
[csi -3 cluster_state] eq {ok} &&
[csi -4 cluster_state] eq {ok}
} else {
fail "Cluster doesn't stabilize"
}
# Make sure each node can meet other nodes
assert_equal 5 [csi 0 cluster_known_nodes]
assert_equal 5 [csi -1 cluster_known_nodes]
assert_equal 5 [csi -2 cluster_known_nodes]
assert_equal 5 [csi -3 cluster_known_nodes]
assert_equal 5 [csi -4 cluster_known_nodes]
}
# stop 5 servers
}
}
}
}
}
} ;# tags
set ::singledb $old_singledb
+45 -5
View File
@@ -7,7 +7,7 @@ start_server {tags {"introspection"}} {
test {CLIENT LIST} {
r client list
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|list user=* redir=-1 resp=2*}
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 ssub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|list user=* redir=-1 resp=2*}
test {CLIENT LIST with IDs} {
set myid [r client id]
@@ -17,7 +17,7 @@ start_server {tags {"introspection"}} {
test {CLIENT INFO} {
r client info
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|info user=* redir=-1 resp=2*}
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 ssub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|info user=* redir=-1 resp=2*}
test {CLIENT KILL with illegal arguments} {
assert_error "ERR wrong number of arguments for 'client|kill' command" {r client kill}
@@ -503,6 +503,21 @@ start_server {tags {"introspection"}} {
assert_match {*'replicaof "--127.0.0.1"'*wrong number of arguments*} $err
} {} {external:skip}
test {redis-server command line arguments - allow passing option name and option value in the same arg} {
start_server {config "default.conf" args {"--maxmemory 700mb" "--maxmemory-policy volatile-lru"}} {
assert_match [r config get maxmemory] {maxmemory 734003200}
assert_match [r config get maxmemory-policy] {maxmemory-policy volatile-lru}
}
} {} {external:skip}
test {redis-server command line arguments - wrong usage that we support anyway} {
start_server {config "default.conf" args {loglevel verbose "--maxmemory '700mb'" "--maxmemory-policy 'volatile-lru'"}} {
assert_match [r config get loglevel] {loglevel verbose}
assert_match [r config get maxmemory] {maxmemory 734003200}
assert_match [r config get maxmemory-policy] {maxmemory-policy volatile-lru}
}
} {} {external:skip}
test {redis-server command line arguments - allow option value to use the `--` prefix} {
start_server {config "default.conf" args {--proc-title-template --my--title--template --loglevel verbose}} {
assert_match [r config get proc-title-template] {proc-title-template --my--title--template}
@@ -510,15 +525,40 @@ start_server {tags {"introspection"}} {
}
} {} {external:skip}
test {redis-server command line arguments - option name and option value in the same arg and `--` prefix} {
start_server {config "default.conf" args {"--proc-title-template --my--title--template" "--loglevel verbose"}} {
assert_match [r config get proc-title-template] {proc-title-template --my--title--template}
assert_match [r config get loglevel] {loglevel verbose}
}
} {} {external:skip}
test {redis-server command line arguments - save with empty input} {
# Take `--loglevel` as the save option value.
catch {exec src/redis-server --save --loglevel verbose} err
assert_match {*'save "--loglevel" "verbose"'*Invalid save parameters*} $err
start_server {config "default.conf" args {--save --loglevel verbose}} {
assert_match [r config get save] {save {}}
assert_match [r config get loglevel] {loglevel verbose}
}
start_server {config "default.conf" args {--loglevel verbose --save}} {
assert_match [r config get save] {save {}}
assert_match [r config get loglevel] {loglevel verbose}
}
start_server {config "default.conf" args {--save {} --loglevel verbose}} {
assert_match [r config get save] {save {}}
assert_match [r config get loglevel] {loglevel verbose}
}
start_server {config "default.conf" args {--loglevel verbose --save {}}} {
assert_match [r config get save] {save {}}
assert_match [r config get loglevel] {loglevel verbose}
}
start_server {config "default.conf" args {--proc-title-template --save --save {} --loglevel verbose}} {
assert_match [r config get proc-title-template] {proc-title-template --save}
assert_match [r config get save] {save {}}
assert_match [r config get loglevel] {loglevel verbose}
}
} {} {external:skip}
test {redis-server command line arguments - take one bulk string with spaces for MULTI_ARG configs parsing} {
+13
View File
@@ -76,6 +76,19 @@ start_server {tags {"modules"}} {
r do_bg_rm_call hgetall hash
} {foo bar}
test {RM_Call from blocked client with script mode} {
r do_bg_rm_call_format S hset k foo bar
} {1}
test {RM_Call from blocked client with oom mode} {
r config set maxmemory 1
# will set server.pre_command_oom_state to 1
assert_error {OOM command not allowed*} {r hset hash foo bar}
r config set maxmemory 0
# now its should be OK to call OOM commands
r do_bg_rm_call_format M hset k1 foo bar
} {1} {needs:config-maxmemory}
test {RESP version carries through to blocked client} {
for {set client_proto 2} {$client_proto <= 3} {incr client_proto} {
r hello $client_proto
+17
View File
@@ -29,6 +29,11 @@ start_server {tags {"modules"}} {
set ld [r test.ld_conversion]
assert {[string match $ld "0.00000000000000001"]}
}
test {test unsigned long long conversions} {
set ret [r test.ull_conversion]
assert {[string match $ret "ok"]}
}
test {test module db commands} {
r set x foo
@@ -103,6 +108,18 @@ start_server {tags {"modules"}} {
assert { [dict get $info flags] == "${ssl_flag}::tracking::" }
}
test {test module get/set client name by id api} {
catch { r test.getname } e
assert_equal "-ERR No name" $e
r client setname nobody
catch { r test.setname "name with spaces" } e
assert_match "*Invalid argument*" $e
assert_equal nobody [r client getname]
assert_equal nobody [r test.getname]
r test.setname somebody
assert_equal somebody [r client getname]
}
test {test module getclientcert api} {
set cert [r test.getclientcert]
+2 -3
View File
@@ -140,7 +140,7 @@ start_server {tags {"obuf-limits external:skip"}} {
# Read nothing
set fd [$rd channel]
assert_equal {} [read $fd]
assert_equal {} [$rd rawread]
}
# Note: This test assumes that what's written with one write, will be read by redis in one read.
@@ -180,8 +180,7 @@ start_server {tags {"obuf-limits external:skip"}} {
assert_equal "PONG" [r ping]
set clients [r client list]
assert_no_match "*name=multicommands*" $clients
set fd [$rd2 channel]
assert_equal {} [read $fd]
assert_equal {} [$rd2 rawread]
}
test {Execute transactions completely even if client output buffer limit is enforced} {
+50 -4
View File
@@ -1570,6 +1570,52 @@ start_server {tags {"scripting"}} {
r config set min-replicas-to-write 0
}
test "not enough good replicas state change during long script" {
r set x "pre-script value"
r config set min-replicas-to-write 1
r config set lua-time-limit 10
start_server {tags {"external:skip"}} {
# add a replica and wait for the master to recognize it's online
r slaveof [srv -1 host] [srv -1 port]
wait_replica_online [srv -1 client]
# run a slow script that does one write, then waits for INFO to indicate
# that the replica dropped, and then runs another write
set rd [redis_deferring_client -1]
$rd eval {
redis.call('set','x',"script value")
while true do
local info = redis.call('info','replication')
if (string.match(info, "connected_slaves:0")) then
redis.call('set','x',info)
break
end
end
return 1
} 1 x
# wait for the script to time out and yield
wait_for_condition 100 100 {
[catch {r -1 ping} e] == 1
} else {
fail "Can't wait for script to start running"
}
catch {r -1 ping} e
assert_match {BUSY*} $e
# cause the replica to disconnect (triggering the busy script to exit)
r slaveof no one
# make sure the script was able to write after the replica dropped
assert_equal [$rd read] 1
assert_match {*connected_slaves:0*} [r -1 get x]
$rd close
}
r config set min-replicas-to-write 0
r config set lua-time-limit 5000
} {OK} {external:skip needs:repl}
test "allow-stale shebang flag" {
r config set replica-serve-stale-data no
r replicaof 127.0.0.1 1
@@ -1599,19 +1645,19 @@ start_server {tags {"scripting"}} {
} 1 x
}
assert_match {*redis_version*} [
assert_match {foobar} [
r eval {#!lua flags=allow-stale,no-writes
return redis.call('info','server')
return redis.call('echo','foobar')
} 0
]
# Test again with EVALSHA
set sha [
r script load {#!lua flags=allow-stale,no-writes
return redis.call('info','server')
return redis.call('echo','foobar')
}
]
assert_match {*redis_version*} [r evalsha $sha 0]
assert_match {foobar} [r evalsha $sha 0]
r replicaof no one
r config set replica-serve-stale-data yes
+13 -1
View File
@@ -2208,7 +2208,7 @@ start_server {tags {"zset"}} {
assert_match "*syntax*" $err
}
test {ZRANGESTORE with zset-max-listpack-entries 0 dst key should use skiplist encoding} {
test {ZRANGESTORE with zset-max-listpack-entries 0 #10767 case} {
set original_max [lindex [r config get zset-max-listpack-entries] 1]
r config set zset-max-listpack-entries 0
r del z1{t} z2{t}
@@ -2217,6 +2217,18 @@ start_server {tags {"zset"}} {
r config set zset-max-listpack-entries $original_max
}
test {ZRANGESTORE with zset-max-listpack-entries 1 dst key should use skiplist encoding} {
set original_max [lindex [r config get zset-max-listpack-entries] 1]
r config set zset-max-listpack-entries 1
r del z1{t} z2{t} z3{t}
r zadd z1{t} 1 a 2 b
assert_equal 1 [r zrangestore z2{t} z1{t} 0 0]
assert_encoding listpack z2{t}
assert_equal 2 [r zrangestore z3{t} z1{t} 0 1]
assert_encoding skiplist z3{t}
r config set zset-max-listpack-entries $original_max
}
test {ZRANGE invalid syntax} {
catch {r zrange z1{t} 0 -1 limit 1 2} err
assert_match "*syntax*" $err
+5 -4
View File
@@ -11,12 +11,13 @@ def markdown(s)
s.chop! while s[-1] == "\n" || s[-1] == " "
lines = s.split("\n")
newlines = []
# Fix some markdown, except in code blocks indented by 4 spaces.
# Fix some markdown
lines.each{|l|
# Rewrite RM_Xyz() to RedisModule_Xyz().
l = l.gsub(/(?<![A-Z_])RM_(?=[A-Z])/, 'RedisModule_')
# Fix more markdown, except in code blocks indented by 4 spaces, which we
# don't want to mess with.
if not l.start_with?(' ')
# Rewrite RM_Xyz() to `RedisModule_Xyz()`. The () suffix is
# optional. Even RM_Xyz*() with * as wildcard is handled.
l = l.gsub(/(?<!`)RM_([A-z]+(?:\*?\(\))?)/, '`RedisModule_\1`')
# Add backquotes around RedisModule functions and type where missing.
l = l.gsub(/(?<!`)RedisModule[A-z]+(?:\*?\(\))?/){|x| "`#{x}`"}
# Add backquotes around c functions like malloc() where missing.
+5 -5
View File
@@ -19,7 +19,7 @@ struct entry {
};
#define to_16bit_minutes(x) ((x/60) & 65535)
#define COUNTER_INIT_VAL 5
#define LFU_INIT_VAL 5
/* Compute the difference in minutes between two 16 bit minutes times
* obtained with to_16bit_minutes(). Since they can wrap around if
@@ -36,7 +36,7 @@ uint16_t minutes_diff(uint16_t now, uint16_t prev) {
uint8_t log_incr(uint8_t counter) {
if (counter == 255) return counter;
double r = (double)rand()/RAND_MAX;
double baseval = counter-COUNTER_INIT_VAL;
double baseval = counter-LFU_INIT_VAL;
if (baseval < 0) baseval = 0;
double limit = 1.0/(baseval*10+1);
if (r < limit) counter++;
@@ -56,7 +56,7 @@ uint8_t scan_entry(struct entry *e) {
>= decr_every)
{
if (e->counter) {
if (e->counter > COUNTER_INIT_VAL*2) {
if (e->counter > LFU_INIT_VAL*2) {
e->counter /= 2;
} else {
e->counter--;
@@ -89,7 +89,7 @@ int main(void) {
/* Initialize. */
for (j = 0; j < keyspace_size; j++) {
entries[j].counter = COUNTER_INIT_VAL;
entries[j].counter = LFU_INIT_VAL;
entries[j].decrtime = to_16bit_minutes(start);
entries[j].hits = 0;
entries[j].ctime = time(NULL);
@@ -131,7 +131,7 @@ int main(void) {
* 10 and 19, a random one every 10 seconds. */
if (new_entry_time <= now) {
idx = 10+(rand()%10);
entries[idx].counter = COUNTER_INIT_VAL;
entries[idx].counter = LFU_INIT_VAL;
entries[idx].decrtime = to_16bit_minutes(time(NULL));
entries[idx].hits = 0;
entries[idx].ctime = time(NULL);