From 593cde16bcac64fb52a36500377574e981c580f6 Mon Sep 17 00:00:00 2001 From: sundb Date: Wed, 13 Jan 2021 17:38:02 +0800 Subject: Fix units in log message of copy-on-write (#8320) --- src/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.c b/src/server.c index 0551eb3e4..90b9045ea 100644 --- a/src/server.c +++ b/src/server.c @@ -5533,7 +5533,7 @@ void sendChildCOWInfo(int ptype, int on_exit, char *pname) { if (private_dirty) { serverLog(on_exit ? LL_NOTICE : LL_VERBOSE, "%s: %zu MB of memory used by copy-on-write", - pname, private_dirty); + pname, private_dirty/(1024*1024)); } sendChildInfo(ptype, on_exit, private_dirty); -- cgit v1.2.1 From 8a81ed1b5ab204fd33651bf448cd5e1230631fb7 Mon Sep 17 00:00:00 2001 From: sundb Date: Wed, 13 Jan 2021 17:58:12 +0800 Subject: Fix use of lookupKeyRead and lookupKeyWrite in zrangeGenericCommand, zunionInterDiffGenericCommand (#8316) * Change zunionInterDiffGenericCommand to use lookupKeyRead if dstkey is null * Change zrangeGenericCommand to use lookupKey Write if dstkey isn't null ZRANGESTORE and UNION, ZINTER, ZDIFF are all new commands (6.2 RC1 and RC2). In redis 6.0 the ZRANGE was using lookupKeyRead, and ZUNIONSTORE / ZINTERSTORE were using lookupKeyWrite. So there bugs are introduced in 6.2 and will be resolved before it is released. the implications of this bug are also not big: The sole difference between LookupKeyRead and LookupKeyWrite is for command executed on a replica, which are not received from its master client. (for the master, and for the master client on the replica, these two functions behave the same)! --- src/t_zset.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/t_zset.c b/src/t_zset.c index 3d63c41c6..eb4f274a3 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -2543,7 +2543,9 @@ void zunionInterDiffGenericCommand(client *c, robj *dstkey, int numkeysIndex, in /* read keys to be used for input */ src = zcalloc(sizeof(zsetopsrc) * setnum); for (i = 0, j = numkeysIndex+1; i < setnum; i++, j++) { - robj *obj = lookupKeyWrite(c->db,c->argv[j]); + robj *obj = dstkey ? + lookupKeyWrite(c->db,c->argv[j]) : + lookupKeyRead(c->db,c->argv[j]); if (obj != NULL) { if (obj->type != OBJ_ZSET && obj->type != OBJ_SET) { zfree(src); @@ -3605,11 +3607,16 @@ void zrangeGenericCommand(zrange_result_handler *handler, int argc_start, int st } /* Step 3: Lookup the key and get the range. */ - if (((zobj = lookupKeyReadOrReply(c, key, shared.emptyarray)) == NULL) - || checkType(c, zobj, OBJ_ZSET)) { + zobj = handler->dstkey ? + lookupKeyWrite(c->db,key) : + lookupKeyRead(c->db,key); + if (zobj == NULL) { + addReply(c,shared.emptyarray); goto cleanup; } + if (checkType(c,zobj,OBJ_ZSET)) goto cleanup; + /* Step 4: Pass this to the command-specific handler. */ switch (rangetype) { case ZRANGE_AUTO: -- cgit v1.2.1 From f5577fdbd8898112427e5807edf102f9fcd9da33 Mon Sep 17 00:00:00 2001 From: "houzj.fnst" <63178771+sherlockcpp@users.noreply.github.com> Date: Thu, 14 Jan 2021 04:00:55 +0800 Subject: remove some unnecessary checks (#7431) Remove several checks that always evaluate to true. --- src/db.c | 2 +- src/expire.c | 2 +- src/networking.c | 2 +- src/redis-cli.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/db.c b/src/db.c index 5d63566a7..13126988f 100644 --- a/src/db.c +++ b/src/db.c @@ -951,7 +951,7 @@ void scanGenericCommand(client *c, robj *o, unsigned long cursor) { int filter = 0; /* Filter element if it does not match the pattern. */ - if (!filter && use_pattern) { + if (use_pattern) { if (sdsEncodedObject(kobj)) { if (!stringmatchlen(pat, patlen, kobj->ptr, sdslen(kobj->ptr), 0)) filter = 1; diff --git a/src/expire.c b/src/expire.c index 275a735a7..d2945524c 100644 --- a/src/expire.c +++ b/src/expire.c @@ -224,7 +224,7 @@ void activeExpireCycle(int type) { /* When there are less than 1% filled slots, sampling the key * space is expensive, so stop here waiting for better times... * The dictionary will be resized asap. */ - if (num && slots > DICT_HT_INITIAL_SIZE && + if (slots > DICT_HT_INITIAL_SIZE && (num*100/slots < 1)) break; /* The main collection cycle. Sample random keys among keys diff --git a/src/networking.c b/src/networking.c index e624dd8f9..f1c606236 100644 --- a/src/networking.c +++ b/src/networking.c @@ -1707,7 +1707,7 @@ int processInlineBuffer(client *c) { } /* Handle the \r\n case. */ - if (newline && newline != c->querybuf+c->qb_pos && *(newline-1) == '\r') + if (newline != c->querybuf+c->qb_pos && *(newline-1) == '\r') newline--, linefeed_chars++; /* Split the input buffer up to the \r\n */ diff --git a/src/redis-cli.c b/src/redis-cli.c index 31d2360c9..2e8984c71 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -5301,7 +5301,7 @@ static clusterManagerNode *clusterNodeForResharding(char *id, clusterManagerLogErr(invalid_node_msg, id); *raise_err = 1; return NULL; - } else if (node != NULL && target != NULL) { + } else if (target != NULL) { if (!strcmp(node->name, target->name)) { clusterManagerLogErr( "*** It is not possible to use " "the target node as " -- cgit v1.2.1 From 9cb9f98d2f0d9114ac4528b2f9434a2fd2edfd60 Mon Sep 17 00:00:00 2001 From: Wang Yuan Date: Thu, 14 Jan 2021 04:36:03 +0800 Subject: Optimize performance of clusterGenNodesDescription for large clusters (#8182) Optimize the performance of clusterGenNodesDescription by only checking slot ownership of each slot once, instead of checking each slot for each node. --- src/cluster.c | 89 ++++++++++++++++++++------ src/cluster.h | 1 + tests/cluster/tests/18-cluster-nodes-slots.tcl | 62 ++++++++++++++++++ 3 files changed, 133 insertions(+), 19 deletions(-) create mode 100644 tests/cluster/tests/18-cluster-nodes-slots.tcl diff --git a/src/cluster.c b/src/cluster.c index 78c36e8d1..2a06ef6dc 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -779,6 +779,7 @@ clusterNode *createClusterNode(char *nodename, int flags) { node->configEpoch = 0; node->flags = flags; memset(node->slots,0,sizeof(node->slots)); + node->slots_info = NULL; node->numslots = 0; node->numslaves = 0; node->slaves = NULL; @@ -4144,8 +4145,8 @@ sds clusterGenNodeDescription(clusterNode *node) { sds ci; /* Node coordinates */ - ci = sdscatprintf(sdsempty(),"%.40s %s:%d@%d ", - node->name, + ci = sdscatlen(sdsempty(),node->name,CLUSTER_NAMELEN); + ci = sdscatfmt(ci," %s:%i@%i ", node->ip, node->port, node->cport); @@ -4154,40 +4155,46 @@ sds clusterGenNodeDescription(clusterNode *node) { ci = representClusterNodeFlags(ci, node->flags); /* Slave of... or just "-" */ + ci = sdscatlen(ci," ",1); if (node->slaveof) - ci = sdscatprintf(ci," %.40s ",node->slaveof->name); + ci = sdscatlen(ci,node->slaveof->name,CLUSTER_NAMELEN); else - ci = sdscatlen(ci," - ",3); + ci = sdscatlen(ci,"-",1); unsigned long long nodeEpoch = node->configEpoch; if (nodeIsSlave(node) && node->slaveof) { nodeEpoch = node->slaveof->configEpoch; } /* Latency from the POV of this node, config epoch, link status */ - ci = sdscatprintf(ci,"%lld %lld %llu %s", + ci = sdscatfmt(ci," %I %I %U %s", (long long) node->ping_sent, (long long) node->pong_received, nodeEpoch, (node->link || node->flags & CLUSTER_NODE_MYSELF) ? "connected" : "disconnected"); - /* Slots served by this instance */ - start = -1; - for (j = 0; j < CLUSTER_SLOTS; j++) { - int bit; + /* Slots served by this instance. If we already have slots info, + * append it diretly, otherwise, generate slots only if it has. */ + if (node->slots_info) { + ci = sdscatsds(ci, node->slots_info); + } else if (node->numslots > 0) { + start = -1; + for (j = 0; j < CLUSTER_SLOTS; j++) { + int bit; - if ((bit = clusterNodeGetSlotBit(node,j)) != 0) { - if (start == -1) start = j; - } - if (start != -1 && (!bit || j == CLUSTER_SLOTS-1)) { - if (bit && j == CLUSTER_SLOTS-1) j++; + if ((bit = clusterNodeGetSlotBit(node,j)) != 0) { + if (start == -1) start = j; + } + if (start != -1 && (!bit || j == CLUSTER_SLOTS-1)) { + if (bit && j == CLUSTER_SLOTS-1) j++; - if (start == j-1) { - ci = sdscatprintf(ci," %d",start); - } else { - ci = sdscatprintf(ci," %d-%d",start,j-1); + if (start == j-1) { + ci = sdscatfmt(ci," %i",start); + } else { + ci = sdscatfmt(ci," %i-%i",start,j-1); + } + start = -1; } - start = -1; } } @@ -4208,6 +4215,41 @@ sds clusterGenNodeDescription(clusterNode *node) { return ci; } +/* Generate the slot topology for all nodes and store the string representation + * in the slots_info struct on the node. This is used to improve the efficiency + * of clusterGenNodesDescription() because it removes looping of the slot space + * for generating the slot info for each node individually. */ +void clusterGenNodesSlotsInfo(int filter) { + clusterNode *n = NULL; + int start = -1; + + for (int i = 0; i <= CLUSTER_SLOTS; i++) { + /* Find start node and slot id. */ + if (n == NULL) { + if (i == CLUSTER_SLOTS) break; + n = server.cluster->slots[i]; + start = i; + continue; + } + + /* Generate slots info when occur different node with start + * or end of slot. */ + if (i == CLUSTER_SLOTS || n != server.cluster->slots[i]) { + if (!(n->flags & filter)) { + if (n->slots_info == NULL) n->slots_info = sdsempty(); + if (start == i-1) { + n->slots_info = sdscatfmt(n->slots_info," %i",start); + } else { + n->slots_info = sdscatfmt(n->slots_info," %i-%i",start,i-1); + } + } + if (i == CLUSTER_SLOTS) break; + n = server.cluster->slots[i]; + start = i; + } + } +} + /* Generate a csv-alike representation of the nodes we are aware of, * including the "myself" node, and return an SDS string containing the * representation (it is up to the caller to free it). @@ -4225,6 +4267,9 @@ sds clusterGenNodesDescription(int filter) { dictIterator *di; dictEntry *de; + /* Generate all nodes slots info firstly. */ + clusterGenNodesSlotsInfo(filter); + di = dictGetSafeIterator(server.cluster->nodes); while((de = dictNext(di)) != NULL) { clusterNode *node = dictGetVal(de); @@ -4234,6 +4279,12 @@ sds clusterGenNodesDescription(int filter) { ci = sdscatsds(ci,ni); sdsfree(ni); ci = sdscatlen(ci,"\n",1); + + /* Release slots info. */ + if (node->slots_info) { + sdsfree(node->slots_info); + node->slots_info = NULL; + } } dictReleaseIterator(di); return ci; diff --git a/src/cluster.h b/src/cluster.h index d58f350ce..716c0d49c 100644 --- a/src/cluster.h +++ b/src/cluster.h @@ -118,6 +118,7 @@ typedef struct clusterNode { int flags; /* CLUSTER_NODE_... */ uint64_t configEpoch; /* Last configEpoch observed for this node */ unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */ + sds slots_info; /* Slots info represented by string. */ int numslots; /* Number of slots handled by this node */ int numslaves; /* Number of slave nodes, if this is a master */ struct clusterNode **slaves; /* pointers to slave nodes */ diff --git a/tests/cluster/tests/18-cluster-nodes-slots.tcl b/tests/cluster/tests/18-cluster-nodes-slots.tcl new file mode 100644 index 000000000..ca0b3ce0d --- /dev/null +++ b/tests/cluster/tests/18-cluster-nodes-slots.tcl @@ -0,0 +1,62 @@ +# Optimize CLUSTER NODES command by generating all nodes slot topology firstly + +source "../tests/includes/init-tests.tcl" + +proc cluster_allocate_with_continuous_slots {n} { + set slot 16383 + set avg [expr ($slot+1) / $n] + while {$slot >= 0} { + set node [expr $slot/$avg >= $n ? $n-1 : $slot/$avg] + lappend slots_$node $slot + incr slot -1 + } + for {set j 0} {$j < $n} {incr j} { + R $j cluster addslots {*}[set slots_${j}] + } +} + +proc cluster_create_with_continuous_slots {masters slaves} { + cluster_allocate_with_continuous_slots $masters + if {$slaves} { + cluster_allocate_slaves $masters $slaves + } + assert_cluster_state ok +} + +test "Create a 2 nodes cluster" { + cluster_create_with_continuous_slots 2 2 +} + +test "Cluster should start ok" { + assert_cluster_state ok +} + +set master1 [Rn 0] +set master2 [Rn 1] + +test "Continuous slots distribution" { + assert_match "* 0-8191*" [$master1 CLUSTER NODES] + assert_match "* 8192-16383*" [$master2 CLUSTER NODES] + + $master1 CLUSTER DELSLOTS 4096 + assert_match "* 0-4095 4097-8191*" [$master1 CLUSTER NODES] + + $master2 CLUSTER DELSLOTS 12288 + assert_match "* 8192-12287 12289-16383*" [$master2 CLUSTER NODES] +} + +test "Discontinuous slots distribution" { + # Remove middle slots + $master1 CLUSTER DELSLOTS 4092 4094 + assert_match "* 0-4091 4093 4095 4097-8191*" [$master1 CLUSTER NODES] + $master2 CLUSTER DELSLOTS 12284 12286 + assert_match "* 8192-12283 12285 12287 12289-16383*" [$master2 CLUSTER NODES] + + # Remove head slots + $master1 CLUSTER DELSLOTS 0 2 + assert_match "* 1 3-4091 4093 4095 4097-8191*" [$master1 CLUSTER NODES] + + # Remove tail slots + $master2 CLUSTER DELSLOTS 16380 16382 16383 + assert_match "* 8192-12283 12285 12287 12289-16379 16381*" [$master2 CLUSTER NODES] +} -- cgit v1.2.1 From 108afbc0bbde9153e4cb08bf50a724c4feb9626e Mon Sep 17 00:00:00 2001 From: charsyam Date: Thu, 14 Jan 2021 09:44:04 +0900 Subject: Replace many calling zrealloc to one zmalloc in sentinelResetMasterAndChangeAddress (#8319) Replace many calling zrealloc to one zmalloc in sentinelResetMasterAndChangeAddress --- src/sentinel.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sentinel.c b/src/sentinel.c index 02260feb7..a374977de 100644 --- a/src/sentinel.c +++ b/src/sentinel.c @@ -1523,14 +1523,17 @@ int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *ip, newaddr = createSentinelAddr(ip,port); if (newaddr == NULL) return C_ERR; - /* Make a list of slaves to add back after the reset. - * Don't include the one having the address we are switching to. */ + /* There can be only 0 or 1 slave that has the newaddr. + * and It can add old master 1 more slave. + * so It allocates dictSize(master->slaves) + 1 */ + slaves = zmalloc(sizeof(sentinelAddr*)*(dictSize(master->slaves) + 1)); + + /* Don't include the one having the address we are switching to. */ di = dictGetIterator(master->slaves); while((de = dictNext(di)) != NULL) { sentinelRedisInstance *slave = dictGetVal(de); if (sentinelAddrIsEqual(slave->addr,newaddr)) continue; - slaves = zrealloc(slaves,sizeof(sentinelAddr*)*(numslaves+1)); slaves[numslaves++] = createSentinelAddr(slave->addr->ip, slave->addr->port); } @@ -1540,7 +1543,6 @@ int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *ip, * as a slave as well, so that we'll be able to sense / reconfigure * the old master. */ if (!sentinelAddrIsEqual(newaddr,master->addr)) { - slaves = zrealloc(slaves,sizeof(sentinelAddr*)*(numslaves+1)); slaves[numslaves++] = createSentinelAddr(master->addr->ip, master->addr->port); } -- cgit v1.2.1 From ebf20b83b2cfcaf326f037a8274848662a3a9603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Wed, 13 Jan 2021 15:14:51 +0100 Subject: Modules API reference formatting fixes Fixes markdown formatting errors and some functions not showing up in the generated documentation at all. Ruby script (gendoc.rb) fixes: * Modified automatic instertion of backquotes: * Don't add backquotes around names which are already preceded by a backquote. Fixes for example \`RedisModule_Reply\*\` which turning into \`\`RedisModule_Reply\`\*\` messes up the formatting. * Add backquotes around types such as RedisModuleString (in addition to function names `RedisModule_[A-z()]*` and macro names `REDISMODULE_[A-z]*`). * Require 4 spaces indentation for disabling automatic backquotes, i.e. code blocks. Fixes continuations of list items (indented 2 spaces). * More permissive extraction of doc comments: * Allow doc comments starting with `/**`. * Make space before `*` on each line optional. * Make space after `/*` and `/**` optional (needed when appearing on its own line). Markdown fixes in module.c: * Fix code blocks not indented enough (4 spaces needed). * Add black line before code blocks and lists where missing (needed). * Enclose special markdown characters `_*^<>` in backticks to prevent them from messing up formatting. * Lists with `1)` changed to `1.` for proper markdown lists. * Remove excessive indentation which causes text to be unintentionally rendered as code blocks. * Other minor formatting fixes. Other fixes in module.c: * Remove blank lines between doc comment and function definition. A blank line here makes the Ruby script exclude the function in docs. --- src/module.c | 501 ++++++++++++++++++++++++++------------------------ src/modules/gendoc.rb | 15 +- 2 files changed, 266 insertions(+), 250 deletions(-) diff --git a/src/module.c b/src/module.c index bf186f8b7..c8dd237ad 100644 --- a/src/module.c +++ b/src/module.c @@ -486,9 +486,9 @@ void *RM_PoolAlloc(RedisModuleCtx *ctx, size_t bytes) { * the value of the specified type. The function fails and returns * REDISMODULE_ERR if: * - * 1) The key is not open for writing. - * 2) The key is not empty. - * 3) The specified type is unknown. + * 1. The key is not open for writing. + * 2. The key is not empty. + * 3. The specified type is unknown. */ int moduleCreateEmptyKey(RedisModuleKey *key, int type) { robj *obj; @@ -933,9 +933,9 @@ int RM_SignalModifiedKey(RedisModuleCtx *ctx, RedisModuleString *keyname) { * keys, call replies and Redis string objects once the command returns. In most * cases this eliminates the need of calling the following functions: * - * 1) RedisModule_CloseKey() - * 2) RedisModule_FreeCallReply() - * 3) RedisModule_FreeString() + * 1. RedisModule_CloseKey() + * 2. RedisModule_FreeCallReply() + * 3. RedisModule_FreeString() * * These functions can still be used with automatic memory management enabled, * to optimize loops that make numerous allocations for example. */ @@ -1139,9 +1139,9 @@ void RM_FreeString(RedisModuleCtx *ctx, RedisModuleString *str) { * Normally you want to call this function when, at the same time * the following conditions are true: * - * 1) You have automatic memory management enabled. - * 2) You want to create string objects. - * 3) Those string objects you create need to live *after* the callback + * 1. You have automatic memory management enabled. + * 2. You want to create string objects. + * 3. Those string objects you create need to live *after* the callback * function(for example a command implementation) creating them returns. * * Usually you want this in order to store the created string object @@ -1188,7 +1188,7 @@ void RM_RetainString(RedisModuleCtx *ctx, RedisModuleString *str) { * returned RedisModuleString. * * It is possible to call this function with a NULL context. - */ +*/ RedisModuleString* RM_HoldString(RedisModuleCtx *ctx, RedisModuleString *str) { if (str->refcount == OBJ_STATIC_REFCOUNT) { return RM_CreateStringFromString(ctx, str); @@ -1742,7 +1742,7 @@ int RM_ReplicateVerbatim(RedisModuleCtx *ctx) { * 2. The ID increases monotonically. Clients connecting to the server later * are guaranteed to get IDs greater than any past ID previously seen. * - * Valid IDs are from 1 to 2^64-1. If 0 is returned it means there is no way + * Valid IDs are from 1 to 2^64 - 1. If 0 is returned it means there is no way * to fetch the ID in the context the function was currently called. * * After obtaining the ID, it is possible to check if the command execution @@ -2376,9 +2376,10 @@ int RM_ListPush(RedisModuleKey *key, int where, RedisModuleString *ele) { * that the user should be free with RM_FreeString() or by enabling * automatic memory. 'where' specifies if the element should be popped from * head or tail. The command returns NULL if: - * 1) The list is empty. - * 2) The key was not open for writing. - * 3) The key is not a list. */ + * + * 1. The list is empty. + * 2. The key was not open for writing. + * 3. The key is not a list. */ RedisModuleString *RM_ListPop(RedisModuleKey *key, int where) { if (!(key->mode & REDISMODULE_WRITE) || key->value == NULL || @@ -2610,8 +2611,8 @@ int zsetInitScoreRange(RedisModuleKey *key, double min, double max, int minex, i * The range is specified according to the two double values 'min' and 'max'. * Both can be infinite using the following two macros: * - * REDISMODULE_POSITIVE_INFINITE for positive infinite value - * REDISMODULE_NEGATIVE_INFINITE for negative infinite value + * * REDISMODULE_POSITIVE_INFINITE for positive infinite value + * * REDISMODULE_NEGATIVE_INFINITE for negative infinite value * * 'minex' and 'maxex' parameters, if true, respectively setup a range * where the min and max value are exclusive (not included) instead of @@ -2970,7 +2971,7 @@ int RM_HashSet(RedisModuleKey *key, int flags, ...) { * * RedisModuleString *first, *second; * RedisModule_HashGet(mykey,REDISMODULE_HASH_NONE,argv[1],&first, - * argv[2],&second,NULL); + * argv[2],&second,NULL); * * As with RedisModule_HashSet() the behavior of the command can be specified * passing flags different than REDISMODULE_HASH_NONE: @@ -3348,29 +3349,31 @@ fmterr: * * **cmdname**: The Redis command to call. * * **fmt**: A format specifier string for the command's arguments. Each * of the arguments should be specified by a valid type specification: - * b The argument is a buffer and is immediately followed by another + * + * * b: The argument is a buffer and is immediately followed by another * argument that is the buffer's length. - * c The argument is a pointer to a plain C string (null-terminated). - * l The argument is long long integer. - * s The argument is a RedisModuleString. - * v The argument(s) is a vector of RedisModuleString. + * * c: The argument is a pointer to a plain C string (null-terminated). + * * l: The argument is long long integer. + * * s: The argument is a RedisModuleString. + * * v: The argument(s) is a vector of RedisModuleString. * * The format specifier can also include modifiers: - * ! Sends the Redis command and its arguments to replicas and AOF. - * A Suppress AOF propagation, send only to replicas (requires `!`). - * R Suppress replicas propagation, send only to AOF (requires `!`). + * + * * !: Sends the Redis command and its arguments to replicas and AOF. + * * A: Suppress AOF propagation, send only to replicas (requires `!`). + * * R: Suppress replicas propagation, send only to AOF (requires `!`). * * **...**: The actual arguments to the Redis command. * * On success a RedisModuleCallReply object is returned, otherwise * NULL is returned and errno is set to the following values: * - * EBADF: wrong format specifier. - * EINVAL: wrong command arity. - * ENOENT: command does not exist. - * EPERM: operation in Cluster instance with key in non local slot. - * EROFS: operation in Cluster instance when a write command is sent - * in a readonly state. - * ENETDOWN: operation in Cluster instance when cluster is down. + * * EBADF: wrong format specifier. + * * EINVAL: wrong command arity. + * * ENOENT: command does not exist. + * * EPERM: operation in Cluster instance with key in non local slot. + * * EROFS: operation in Cluster instance when a write command is sent + * in a readonly state. + * * ENETDOWN: operation in Cluster instance when cluster is down. * * Example code fragment: * @@ -3686,7 +3689,7 @@ robj *moduleTypeDupOrReply(client *c, robj *fromkey, robj *tokey, robj *value) { * that should be populated with the methods callbacks and structure * version, like in the following example: * - * RedisModuleTypeMethods tm = { + * RedisModuleTypeMethods tm = { * .version = REDISMODULE_TYPE_METHOD_VERSION, * .rdb_load = myType_RDBLoadCallBack, * .rdb_save = myType_RDBSaveCallBack, @@ -3702,7 +3705,7 @@ robj *moduleTypeDupOrReply(client *c, robj *fromkey, robj *tokey, robj *value) { * .unlink = myType_UnlinkCallBack, * .copy = myType_CopyCallback, * .defrag = myType_DefragCallback - * } + * } * * * **rdb_load**: A callback function pointer that loads data from RDB files. * * **rdb_save**: A callback function pointer that saves data to RDB files. @@ -5103,9 +5106,9 @@ void moduleReleaseGIL(void) { * * The subscriber signature is: * - * int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, - * const char *event, - * RedisModuleString *key); + * int (*RedisModuleNotificationFunc) (RedisModuleCtx *ctx, int type, + * const char *event, + * RedisModuleString *key); * * `type` is the event type bit, that must match the mask given at registration * time. The event string is the actual command being executed, and key is the @@ -5369,28 +5372,27 @@ size_t RM_GetClusterSize(void) { return dictSize(server.cluster->nodes); } +clusterNode *clusterLookupNode(const char *name); /* We need access to internals */ + /* Populate the specified info for the node having as ID the specified 'id', * then returns REDISMODULE_OK. Otherwise if the node ID does not exist from * the POV of this local node, REDISMODULE_ERR is returned. * - * The arguments ip, master_id, port and flags can be NULL in case we don't - * need to populate back certain info. If an ip and master_id (only populated + * The arguments `ip`, `master_id`, `port` and `flags` can be NULL in case we don't + * need to populate back certain info. If an `ip` and `master_id` (only populated * if the instance is a slave) are specified, they point to buffers holding - * at least REDISMODULE_NODE_ID_LEN bytes. The strings written back as ip - * and master_id are not null terminated. + * at least REDISMODULE_NODE_ID_LEN bytes. The strings written back as `ip` + * and `master_id` are not null terminated. * * The list of flags reported is the following: * - * * REDISMODULE_NODE_MYSELF This node - * * REDISMODULE_NODE_MASTER The node is a master - * * REDISMODULE_NODE_SLAVE The node is a replica - * * REDISMODULE_NODE_PFAIL We see the node as failing - * * REDISMODULE_NODE_FAIL The cluster agrees the node is failing - * * REDISMODULE_NODE_NOFAILOVER The slave is configured to never failover + * * REDISMODULE_NODE_MYSELF: This node + * * REDISMODULE_NODE_MASTER: The node is a master + * * REDISMODULE_NODE_SLAVE: The node is a replica + * * REDISMODULE_NODE_PFAIL: We see the node as failing + * * REDISMODULE_NODE_FAIL: The cluster agrees the node is failing + * * REDISMODULE_NODE_NOFAILOVER: The slave is configured to never failover */ - -clusterNode *clusterLookupNode(const char *name); /* We need access to internals */ - int RM_GetClusterNodeInfo(RedisModuleCtx *ctx, const char *id, char *ip, char *master_id, int *port, int *flags) { UNUSED(ctx); @@ -5434,18 +5436,18 @@ int RM_GetClusterNodeInfo(RedisModuleCtx *ctx, const char *id, char *ip, char *m * a different distributed system, but still want to use the Redis Cluster * message bus. Flags that can be set: * - * CLUSTER_MODULE_FLAG_NO_FAILOVER - * CLUSTER_MODULE_FLAG_NO_REDIRECTION + * * CLUSTER_MODULE_FLAG_NO_FAILOVER + * * CLUSTER_MODULE_FLAG_NO_REDIRECTION * * With the following effects: * - * NO_FAILOVER: prevent Redis Cluster slaves to failover a failing master. - * Also disables the replica migration feature. + * * NO_FAILOVER: prevent Redis Cluster slaves to failover a failing master. + * Also disables the replica migration feature. * - * NO_REDIRECTION: Every node will accept any key, without trying to perform - * partitioning according to the user Redis Cluster algorithm. - * Slots informations will still be propagated across the - * cluster, but without effects. */ + * * NO_REDIRECTION: Every node will accept any key, without trying to perform + * partitioning according to the user Redis Cluster algorithm. + * Slots informations will still be propagated across the + * cluster, but without effects. */ void RM_SetClusterFlags(RedisModuleCtx *ctx, uint64_t flags) { UNUSED(ctx); if (flags & REDISMODULE_CLUSTER_FLAG_NO_FAILOVER) @@ -5964,15 +5966,15 @@ int RM_DictDel(RedisModuleDict *d, RedisModuleString *key, void *oldval) { * comparison operator to use in order to seek the first element. The * operators available are: * - * "^" -- Seek the first (lexicographically smaller) key. - * "$" -- Seek the last (lexicographically biffer) key. - * ">" -- Seek the first element greater than the specified key. - * ">=" -- Seek the first element greater or equal than the specified key. - * "<" -- Seek the first element smaller than the specified key. - * "<=" -- Seek the first element smaller or equal than the specified key. - * "==" -- Seek the first element matching exactly the specified key. + * * `^` -- Seek the first (lexicographically smaller) key. + * * `$` -- Seek the last (lexicographically biffer) key. + * * `>` -- Seek the first element greater than the specified key. + * * `>=` -- Seek the first element greater or equal than the specified key. + * * `<` -- Seek the first element smaller than the specified key. + * * `<=` -- Seek the first element smaller or equal than the specified key. + * * `==` -- Seek the first element matching exactly the specified key. * - * Note that for "^" and "$" the passed key is not used, and the user may + * Note that for `^` and `$` the passed key is not used, and the user may * just pass NULL with a length of 0. * * If the element to start the iteration cannot be seeked based on the @@ -6017,11 +6019,11 @@ int RM_DictIteratorReseek(RedisModuleDictIter *di, const char *op, RedisModuleSt return RM_DictIteratorReseekC(di,op,key->ptr,sdslen(key->ptr)); } -/* Return the current item of the dictionary iterator 'di' and steps to the +/* Return the current item of the dictionary iterator `di` and steps to the * next element. If the iterator already yield the last element and there * are no other elements to return, NULL is returned, otherwise a pointer - * to a string representing the key is provided, and the '*keylen' length - * is set by reference (if keylen is not NULL). The '*dataptr', if not NULL + * to a string representing the key is provided, and the `*keylen` length + * is set by reference (if keylen is not NULL). The `*dataptr`, if not NULL * is set to the value of the pointer stored at the returned key as auxiliary * data (as set by the RedisModule_DictSet API). * @@ -6035,7 +6037,7 @@ int RM_DictIteratorReseek(RedisModuleDictIter *di, const char *op, RedisModuleSt * } * * The returned pointer is of type void because sometimes it makes sense - * to cast it to a char* sometimes to an unsigned char* depending on the + * to cast it to a `char*` sometimes to an unsigned `char*` depending on the * fact it contains or not binary data, so this API ends being more * comfortable to use. * @@ -6119,8 +6121,8 @@ int RM_DictCompare(RedisModuleDictIter *di, const char *op, RedisModuleString *k int RM_InfoEndDictField(RedisModuleInfoCtx *ctx); /* Used to start a new section, before adding any fields. the section name will - * be prefixed by "_" and must only include A-Z,a-z,0-9. - * NULL or empty string indicates the default section (only ) is used. + * be prefixed by `_` and must only include A-Z,a-z,0-9. + * NULL or empty string indicates the default section (only ``) is used. * When return value is REDISMODULE_ERR, the section should and will be skipped. */ int RM_InfoAddSection(RedisModuleInfoCtx *ctx, char *name) { sds full_name = sdsdup(ctx->module->name); @@ -6180,8 +6182,8 @@ int RM_InfoEndDictField(RedisModuleInfoCtx *ctx) { } /* Used by RedisModuleInfoFunc to add info fields. - * Each field will be automatically prefixed by "_". - * Field names or values must not include \r\n of ":" */ + * Each field will be automatically prefixed by `_`. + * Field names or values must not include `\r\n` or `:`. */ int RM_InfoAddFieldString(RedisModuleInfoCtx *ctx, char *field, RedisModuleString *value) { if (!ctx->in_section) return REDISMODULE_ERR; @@ -6200,6 +6202,7 @@ int RM_InfoAddFieldString(RedisModuleInfoCtx *ctx, char *field, RedisModuleStrin return REDISMODULE_OK; } +/* See RedisModule_InfoAddFieldString(). */ int RM_InfoAddFieldCString(RedisModuleInfoCtx *ctx, char *field, char *value) { if (!ctx->in_section) return REDISMODULE_ERR; @@ -6218,6 +6221,7 @@ int RM_InfoAddFieldCString(RedisModuleInfoCtx *ctx, char *field, char *value) { return REDISMODULE_OK; } +/* See RedisModule_InfoAddFieldString(). */ int RM_InfoAddFieldDouble(RedisModuleInfoCtx *ctx, char *field, double value) { if (!ctx->in_section) return REDISMODULE_ERR; @@ -6236,6 +6240,7 @@ int RM_InfoAddFieldDouble(RedisModuleInfoCtx *ctx, char *field, double value) { return REDISMODULE_OK; } +/* See RedisModule_InfoAddFieldString(). */ int RM_InfoAddFieldLongLong(RedisModuleInfoCtx *ctx, char *field, long long value) { if (!ctx->in_section) return REDISMODULE_ERR; @@ -6254,6 +6259,7 @@ int RM_InfoAddFieldLongLong(RedisModuleInfoCtx *ctx, char *field, long long valu return REDISMODULE_OK; } +/* See RedisModule_InfoAddFieldString(). */ int RM_InfoAddFieldULongLong(RedisModuleInfoCtx *ctx, char *field, unsigned long long value) { if (!ctx->in_section) return REDISMODULE_ERR; @@ -6272,6 +6278,8 @@ int RM_InfoAddFieldULongLong(RedisModuleInfoCtx *ctx, char *field, unsigned long return REDISMODULE_OK; } +/* Registers callback for the INFO command. The callback should add INFO fields + * by calling the `RedisModule_InfoAddField*()` functions. */ int RM_RegisterInfoFunc(RedisModuleCtx *ctx, RedisModuleInfoFunc cb) { ctx->module->info_cb = cb; return REDISMODULE_OK; @@ -6711,7 +6719,6 @@ const RedisModuleString *RM_CommandFilterArgGet(RedisModuleCommandFilterCtx *fct * after the filter context is destroyed, so it must not be auto-memory * allocated, freed or used elsewhere. */ - int RM_CommandFilterArgInsert(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) { int i; @@ -6733,7 +6740,6 @@ int RM_CommandFilterArgInsert(RedisModuleCommandFilterCtx *fctx, int pos, RedisM * filter context is destroyed, so it must not be auto-memory allocated, freed * or used elsewhere. */ - int RM_CommandFilterArgReplace(RedisModuleCommandFilterCtx *fctx, int pos, RedisModuleString *arg) { if (pos < 0 || pos >= fctx->argc) return REDISMODULE_ERR; @@ -6774,10 +6780,10 @@ size_t RM_MallocSize(void* ptr){ /* Return the a number between 0 to 1 indicating the amount of memory * currently used, relative to the Redis "maxmemory" configuration. * - * 0 - No memory limit configured. - * Between 0 and 1 - The percentage of the memory used normalized in 0-1 range. - * Exactly 1 - Memory limit reached. - * Greater 1 - More memory used than the configured limit. + * * 0 - No memory limit configured. + * * Between 0 and 1 - The percentage of the memory used normalized in 0-1 range. + * * Exactly 1 - Memory limit reached. + * * Greater 1 - More memory used than the configured limit. */ float RM_GetUsedMemoryRatio(){ float level; @@ -6840,21 +6846,22 @@ void RM_ScanCursorDestroy(RedisModuleScanCursor *cursor) { * the selected db. * * Callback for scan implementation. - * void scan_callback(RedisModuleCtx *ctx, RedisModuleString *keyname, - * RedisModuleKey *key, void *privdata); - * ctx - the redis module context provided to for the scan. - * keyname - owned by the caller and need to be retained if used after this - * function. * - * key - holds info on the key and value, it is provided as best effort, in - * some cases it might be NULL, in which case the user should (can) use - * RedisModule_OpenKey (and CloseKey too). - * when it is provided, it is owned by the caller and will be free when the - * callback returns. + * void scan_callback(RedisModuleCtx *ctx, RedisModuleString *keyname, + * RedisModuleKey *key, void *privdata); * - * privdata - the user data provided to RedisModule_Scan. + * - `ctx`: the redis module context provided to for the scan. + * - `keyname`: owned by the caller and need to be retained if used after this + * function. + * - `key`: holds info on the key and value, it is provided as best effort, in + * some cases it might be NULL, in which case the user should (can) use + * RedisModule_OpenKey() (and CloseKey too). + * when it is provided, it is owned by the caller and will be free when the + * callback returns. + * - `privdata`: the user data provided to RedisModule_Scan(). * * The way it should be used: + * * RedisModuleCursor *c = RedisModule_ScanCursorCreate(); * while(RedisModule_Scan(ctx, c, callback, privateData)); * RedisModule_ScanCursorDestroy(c); @@ -6938,7 +6945,9 @@ static void moduleScanKeyCallback(void *privdata, const dictEntry *de) { /* Scan api that allows a module to scan the elements in a hash, set or sorted set key * * Callback for scan implementation. - * void scan_callback(RedisModuleKey *key, RedisModuleString* field, RedisModuleString* value, void *privdata); + * + * void scan_callback(RedisModuleKey *key, RedisModuleString* field, RedisModuleString* value, void *privdata); + * * - key - the redis key context provided to for the scan. * - field - field name, owned by the caller and need to be retained if used * after this function. @@ -6947,6 +6956,7 @@ static void moduleScanKeyCallback(void *privdata, const dictEntry *de) { * - privdata - the user data provided to RedisModule_ScanKey. * * The way it should be used: + * * RedisModuleCursor *c = RedisModule_ScanCursorCreate(); * RedisModuleKey *key = RedisModule_OpenKey(...) * while(RedisModule_ScanKey(key, c, callback, privateData)); @@ -6955,6 +6965,7 @@ static void moduleScanKeyCallback(void *privdata, const dictEntry *de) { * * It is also possible to use this API from another thread while the lock is acquired during * the actuall call to RM_ScanKey, and re-opening the key each time: + * * RedisModuleCursor *c = RedisModule_ScanCursorCreate(); * RedisModule_ThreadSafeContextLock(ctx); * RedisModuleKey *key = RedisModule_OpenKey(...) @@ -7159,10 +7170,10 @@ void ModuleForkDoneHandler(int exitcode, int bysignal) { * * The callback must be of this type: * - * int (*RedisModuleEventCallback)(RedisModuleCtx *ctx, - * RedisModuleEvent eid, - * uint64_t subevent, - * void *data); + * int (*RedisModuleEventCallback)(RedisModuleCtx *ctx, + * RedisModuleEvent eid, + * uint64_t subevent, + * void *data); * * The 'ctx' is a normal Redis module context that the callback can use in * order to call other modules APIs. The 'eid' is the event itself, this @@ -7176,200 +7187,200 @@ void ModuleForkDoneHandler(int exitcode, int bysignal) { * * Here is a list of events you can use as 'eid' and related sub events: * - * RedisModuleEvent_ReplicationRoleChanged + * * RedisModuleEvent_ReplicationRoleChanged: * - * This event is called when the instance switches from master - * to replica or the other way around, however the event is - * also called when the replica remains a replica but starts to - * replicate with a different master. + * This event is called when the instance switches from master + * to replica or the other way around, however the event is + * also called when the replica remains a replica but starts to + * replicate with a different master. * - * The following sub events are available: + * The following sub events are available: * - * REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_MASTER - * REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_REPLICA + * * REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_MASTER + * * REDISMODULE_SUBEVENT_REPLROLECHANGED_NOW_REPLICA * - * The 'data' field can be casted by the callback to a - * RedisModuleReplicationInfo structure with the following fields: + * The 'data' field can be casted by the callback to a + * RedisModuleReplicationInfo structure with the following fields: * - * int master; // true if master, false if replica - * char *masterhost; // master instance hostname for NOW_REPLICA - * int masterport; // master instance port for NOW_REPLICA - * char *replid1; // Main replication ID - * char *replid2; // Secondary replication ID - * uint64_t repl1_offset; // Main replication offset - * uint64_t repl2_offset; // Offset of replid2 validity + * int master; // true if master, false if replica + * char *masterhost; // master instance hostname for NOW_REPLICA + * int masterport; // master instance port for NOW_REPLICA + * char *replid1; // Main replication ID + * char *replid2; // Secondary replication ID + * uint64_t repl1_offset; // Main replication offset + * uint64_t repl2_offset; // Offset of replid2 validity * - * RedisModuleEvent_Persistence + * * RedisModuleEvent_Persistence * - * This event is called when RDB saving or AOF rewriting starts - * and ends. The following sub events are available: + * This event is called when RDB saving or AOF rewriting starts + * and ends. The following sub events are available: * - * REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START - * REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START - * REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START - * REDISMODULE_SUBEVENT_PERSISTENCE_ENDED - * REDISMODULE_SUBEVENT_PERSISTENCE_FAILED + * * REDISMODULE_SUBEVENT_PERSISTENCE_RDB_START + * * REDISMODULE_SUBEVENT_PERSISTENCE_AOF_START + * * REDISMODULE_SUBEVENT_PERSISTENCE_SYNC_RDB_START + * * REDISMODULE_SUBEVENT_PERSISTENCE_ENDED + * * REDISMODULE_SUBEVENT_PERSISTENCE_FAILED * - * The above events are triggered not just when the user calls the - * relevant commands like BGSAVE, but also when a saving operation - * or AOF rewriting occurs because of internal server triggers. - * The SYNC_RDB_START sub events are happening in the forground due to - * SAVE command, FLUSHALL, or server shutdown, and the other RDB and - * AOF sub events are executed in a background fork child, so any - * action the module takes can only affect the generated AOF or RDB, - * but will not be reflected in the parent process and affect connected - * clients and commands. Also note that the AOF_START sub event may end - * up saving RDB content in case of an AOF with rdb-preamble. + * The above events are triggered not just when the user calls the + * relevant commands like BGSAVE, but also when a saving operation + * or AOF rewriting occurs because of internal server triggers. + * The SYNC_RDB_START sub events are happening in the forground due to + * SAVE command, FLUSHALL, or server shutdown, and the other RDB and + * AOF sub events are executed in a background fork child, so any + * action the module takes can only affect the generated AOF or RDB, + * but will not be reflected in the parent process and affect connected + * clients and commands. Also note that the AOF_START sub event may end + * up saving RDB content in case of an AOF with rdb-preamble. * - * RedisModuleEvent_FlushDB + * * RedisModuleEvent_FlushDB * - * The FLUSHALL, FLUSHDB or an internal flush (for instance - * because of replication, after the replica synchronization) - * happened. The following sub events are available: + * The FLUSHALL, FLUSHDB or an internal flush (for instance + * because of replication, after the replica synchronization) + * happened. The following sub events are available: * - * REDISMODULE_SUBEVENT_FLUSHDB_START - * REDISMODULE_SUBEVENT_FLUSHDB_END + * * REDISMODULE_SUBEVENT_FLUSHDB_START + * * REDISMODULE_SUBEVENT_FLUSHDB_END * - * The data pointer can be casted to a RedisModuleFlushInfo - * structure with the following fields: + * The data pointer can be casted to a RedisModuleFlushInfo + * structure with the following fields: * - * int32_t async; // True if the flush is done in a thread. - * See for instance FLUSHALL ASYNC. - * In this case the END callback is invoked - * immediately after the database is put - * in the free list of the thread. - * int32_t dbnum; // Flushed database number, -1 for all the DBs - * in the case of the FLUSHALL operation. + * int32_t async; // True if the flush is done in a thread. + * // See for instance FLUSHALL ASYNC. + * // In this case the END callback is invoked + * // immediately after the database is put + * // in the free list of the thread. + * int32_t dbnum; // Flushed database number, -1 for all the DBs + * // in the case of the FLUSHALL operation. * - * The start event is called *before* the operation is initated, thus - * allowing the callback to call DBSIZE or other operation on the - * yet-to-free keyspace. + * The start event is called *before* the operation is initated, thus + * allowing the callback to call DBSIZE or other operation on the + * yet-to-free keyspace. * - * RedisModuleEvent_Loading + * * RedisModuleEvent_Loading * - * Called on loading operations: at startup when the server is - * started, but also after a first synchronization when the - * replica is loading the RDB file from the master. - * The following sub events are available: + * Called on loading operations: at startup when the server is + * started, but also after a first synchronization when the + * replica is loading the RDB file from the master. + * The following sub events are available: * - * REDISMODULE_SUBEVENT_LOADING_RDB_START - * REDISMODULE_SUBEVENT_LOADING_AOF_START - * REDISMODULE_SUBEVENT_LOADING_REPL_START - * REDISMODULE_SUBEVENT_LOADING_ENDED - * REDISMODULE_SUBEVENT_LOADING_FAILED + * * REDISMODULE_SUBEVENT_LOADING_RDB_START + * * REDISMODULE_SUBEVENT_LOADING_AOF_START + * * REDISMODULE_SUBEVENT_LOADING_REPL_START + * * REDISMODULE_SUBEVENT_LOADING_ENDED + * * REDISMODULE_SUBEVENT_LOADING_FAILED * - * Note that AOF loading may start with an RDB data in case of - * rdb-preamble, in which case you'll only receive an AOF_START event. + * Note that AOF loading may start with an RDB data in case of + * rdb-preamble, in which case you'll only receive an AOF_START event. * * - * RedisModuleEvent_ClientChange + * * RedisModuleEvent_ClientChange * - * Called when a client connects or disconnects. - * The data pointer can be casted to a RedisModuleClientInfo - * structure, documented in RedisModule_GetClientInfoById(). - * The following sub events are available: + * Called when a client connects or disconnects. + * The data pointer can be casted to a RedisModuleClientInfo + * structure, documented in RedisModule_GetClientInfoById(). + * The following sub events are available: * - * REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED - * REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED + * * REDISMODULE_SUBEVENT_CLIENT_CHANGE_CONNECTED + * * REDISMODULE_SUBEVENT_CLIENT_CHANGE_DISCONNECTED * - * RedisModuleEvent_Shutdown + * * RedisModuleEvent_Shutdown * - * The server is shutting down. No subevents are available. + * The server is shutting down. No subevents are available. * - * RedisModuleEvent_ReplicaChange + * * RedisModuleEvent_ReplicaChange * - * This event is called when the instance (that can be both a - * master or a replica) get a new online replica, or lose a - * replica since it gets disconnected. - * The following sub events are available: + * This event is called when the instance (that can be both a + * master or a replica) get a new online replica, or lose a + * replica since it gets disconnected. + * The following sub events are available: * - * REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE - * REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE + * * REDISMODULE_SUBEVENT_REPLICA_CHANGE_ONLINE + * * REDISMODULE_SUBEVENT_REPLICA_CHANGE_OFFLINE * - * No additional information is available so far: future versions - * of Redis will have an API in order to enumerate the replicas - * connected and their state. + * No additional information is available so far: future versions + * of Redis will have an API in order to enumerate the replicas + * connected and their state. * - * RedisModuleEvent_CronLoop + * * RedisModuleEvent_CronLoop * - * This event is called every time Redis calls the serverCron() - * function in order to do certain bookkeeping. Modules that are - * required to do operations from time to time may use this callback. - * Normally Redis calls this function 10 times per second, but - * this changes depending on the "hz" configuration. - * No sub events are available. + * This event is called every time Redis calls the serverCron() + * function in order to do certain bookkeeping. Modules that are + * required to do operations from time to time may use this callback. + * Normally Redis calls this function 10 times per second, but + * this changes depending on the "hz" configuration. + * No sub events are available. * - * The data pointer can be casted to a RedisModuleCronLoop - * structure with the following fields: + * The data pointer can be casted to a RedisModuleCronLoop + * structure with the following fields: * - * int32_t hz; // Approximate number of events per second. + * int32_t hz; // Approximate number of events per second. * - * RedisModuleEvent_MasterLinkChange + * * RedisModuleEvent_MasterLinkChange * - * This is called for replicas in order to notify when the - * replication link becomes functional (up) with our master, - * or when it goes down. Note that the link is not considered - * up when we just connected to the master, but only if the - * replication is happening correctly. - * The following sub events are available: + * This is called for replicas in order to notify when the + * replication link becomes functional (up) with our master, + * or when it goes down. Note that the link is not considered + * up when we just connected to the master, but only if the + * replication is happening correctly. + * The following sub events are available: * - * REDISMODULE_SUBEVENT_MASTER_LINK_UP - * REDISMODULE_SUBEVENT_MASTER_LINK_DOWN + * * REDISMODULE_SUBEVENT_MASTER_LINK_UP + * * REDISMODULE_SUBEVENT_MASTER_LINK_DOWN * - * RedisModuleEvent_ModuleChange + * * RedisModuleEvent_ModuleChange * - * This event is called when a new module is loaded or one is unloaded. - * The following sub events are available: + * This event is called when a new module is loaded or one is unloaded. + * The following sub events are available: * - * REDISMODULE_SUBEVENT_MODULE_LOADED - * REDISMODULE_SUBEVENT_MODULE_UNLOADED + * * REDISMODULE_SUBEVENT_MODULE_LOADED + * * REDISMODULE_SUBEVENT_MODULE_UNLOADED * - * The data pointer can be casted to a RedisModuleModuleChange - * structure with the following fields: + * The data pointer can be casted to a RedisModuleModuleChange + * structure with the following fields: * - * const char* module_name; // Name of module loaded or unloaded. - * int32_t module_version; // Module version. + * const char* module_name; // Name of module loaded or unloaded. + * int32_t module_version; // Module version. * - * RedisModuleEvent_LoadingProgress + * * RedisModuleEvent_LoadingProgress * - * This event is called repeatedly called while an RDB or AOF file - * is being loaded. - * The following sub events are availble: + * This event is called repeatedly called while an RDB or AOF file + * is being loaded. + * The following sub events are availble: * - * REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB - * REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF + * * REDISMODULE_SUBEVENT_LOADING_PROGRESS_RDB + * * REDISMODULE_SUBEVENT_LOADING_PROGRESS_AOF * - * The data pointer can be casted to a RedisModuleLoadingProgress - * structure with the following fields: + * The data pointer can be casted to a RedisModuleLoadingProgress + * structure with the following fields: * - * int32_t hz; // Approximate number of events per second. - * int32_t progress; // Approximate progress between 0 and 1024, - * or -1 if unknown. + * int32_t hz; // Approximate number of events per second. + * int32_t progress; // Approximate progress between 0 and 1024, + * or -1 if unknown. * - * RedisModuleEvent_SwapDB + * * RedisModuleEvent_SwapDB * - * This event is called when a SWAPDB command has been successfully - * Executed. - * For this event call currently there is no subevents available. + * This event is called when a SWAPDB command has been successfully + * Executed. + * For this event call currently there is no subevents available. * - * The data pointer can be casted to a RedisModuleSwapDbInfo - * structure with the following fields: + * The data pointer can be casted to a RedisModuleSwapDbInfo + * structure with the following fields: * - * int32_t dbnum_first; // Swap Db first dbnum - * int32_t dbnum_second; // Swap Db second dbnum + * int32_t dbnum_first; // Swap Db first dbnum + * int32_t dbnum_second; // Swap Db second dbnum * - * RedisModuleEvent_ReplBackup + * * RedisModuleEvent_ReplBackup * - * Called when diskless-repl-load config is set to swapdb, - * And redis needs to backup the the current database for the - * possibility to be restored later. A module with global data and - * maybe with aux_load and aux_save callbacks may need to use this - * notification to backup / restore / discard its globals. - * The following sub events are available: + * Called when diskless-repl-load config is set to swapdb, + * And redis needs to backup the the current database for the + * possibility to be restored later. A module with global data and + * maybe with aux_load and aux_save callbacks may need to use this + * notification to backup / restore / discard its globals. + * The following sub events are available: * - * REDISMODULE_SUBEVENT_REPL_BACKUP_CREATE - * REDISMODULE_SUBEVENT_REPL_BACKUP_RESTORE - * REDISMODULE_SUBEVENT_REPL_BACKUP_DISCARD + * * REDISMODULE_SUBEVENT_REPL_BACKUP_CREATE + * * REDISMODULE_SUBEVENT_REPL_BACKUP_RESTORE + * * REDISMODULE_SUBEVENT_REPL_BACKUP_DISCARD * * * The function returns REDISMODULE_OK if the module was successfully subscribed @@ -8064,7 +8075,8 @@ int RM_GetLFU(RedisModuleKey *key, long long *lfu_freq) { * the module can check if a certain set of flags are supported * by the redis server version in use. * Example: - * int supportedFlags = RM_GetContextFlagsAll() + * + * int supportedFlags = RM_GetContextFlagsAll(); * if (supportedFlags & REDISMODULE_CTX_FLAGS_MULTI) { * // REDISMODULE_CTX_FLAGS_MULTI is supported * } else{ @@ -8080,7 +8092,8 @@ int RM_GetContextFlagsAll() { * the module can check if a certain set of flags are supported * by the redis server version in use. * Example: - * int supportedFlags = RM_GetKeyspaceNotificationFlagsAll() + * + * int supportedFlags = RM_GetKeyspaceNotificationFlagsAll(); * if (supportedFlags & REDISMODULE_NOTIFY_LOADED) { * // REDISMODULE_NOTIFY_LOADED is supported * } else{ @@ -8150,8 +8163,8 @@ int RM_ModuleTypeReplaceValue(RedisModuleKey *key, moduleType *mt, void *new_val * an error condition. Error conditions are indicated by setting errno * as folllows: * - * ENOENT: Specified command does not exist. - * EINVAL: Invalid command arity specified. + * * ENOENT: Specified command does not exist. + * * EINVAL: Invalid command arity specified. * * NOTE: The returned array is not a Redis Module object so it does not * get automatically freed even when auto-memory is used. The caller @@ -8247,11 +8260,11 @@ int RM_DefragShouldStop(RedisModuleDefragCtx *ctx) { * data type. * * This behavior is reserved to cases where late defrag is performed. Late - * defrag is selected for keys that implement the free_effort callback and - * return a free_effort value that is larger than the defrag + * defrag is selected for keys that implement the `free_effort` callback and + * return a `free_effort` value that is larger than the defrag * 'active-defrag-max-scan-fields' configuration directive. * - * Smaller keys, keys that do not implement free_effort or the global + * Smaller keys, keys that do not implement `free_effort` or the global * defrag callback are not called in late-defrag mode. In those cases, a * call to this function will return REDISMODULE_ERR. * diff --git a/src/modules/gendoc.rb b/src/modules/gendoc.rb index ee6572884..249c8b6ea 100644 --- a/src/modules/gendoc.rb +++ b/src/modules/gendoc.rb @@ -4,16 +4,18 @@ # Convert the C comment to markdown def markdown(s) s = s.gsub(/\*\/$/,"") - s = s.gsub(/^ \* {0,1}/,"") - s = s.gsub(/^\/\* /,"") + s = s.gsub(/^ ?\* ?/,"") + s = s.gsub(/^\/\*\*? ?/,"") s.chop! while s[-1] == "\n" || s[-1] == " " lines = s.split("\n") newlines = [] + # Backquote function, macro and type names, except if already backquoted and + # in code blocks indented by 4 spaces. lines.each{|l| - if l[0] != ' ' - l = l.gsub(/RM_[A-z()]+/){|x| "`#{x}`"} - l = l.gsub(/RedisModule_[A-z()]+/){|x| "`#{x}`"} - l = l.gsub(/REDISMODULE_[A-z]+/){|x| "`#{x}`"} + if not l.start_with?(' ') + l = l.gsub(/(?\n\n" src = File.open("../module.c").to_a src.each_with_index{|line,i| if line =~ /RM_/ && line[0] != ' ' && line[0] != '#' && line[0] != '/' -- cgit v1.2.1 From fcb3dfe56d3e2e0732c6787fe11789252eaf9ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20S=C3=B6derqvist?= Date: Thu, 14 Jan 2021 20:34:00 +0100 Subject: Rename non-API RM-prefixed functions to hide them from API docs The prefix is changed from `RM_` to `module` on the following internal functions, to prevent them from appearing in the API docs: RM_LogRaw -> moduleLogRaw RM_FreeCallReplyRec -> moduleFreeCallReplyRec RM_ZsetAddFlagsToCoreFlags -> moduleZsetAddFlagsToCoreFlags RM_ZsetAddFlagsFromCoreFlags -> moduleZsetAddFlagsFromCoreFlags --- src/module.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/module.c b/src/module.c index c8dd237ad..fd3f90d84 100644 --- a/src/module.c +++ b/src/module.c @@ -2399,7 +2399,7 @@ RedisModuleString *RM_ListPop(RedisModuleKey *key, int where) { /* Conversion from/to public flags of the Modules API and our private flags, * so that we have everything decoupled. */ -int RM_ZsetAddFlagsToCoreFlags(int flags) { +int moduleZsetAddFlagsToCoreFlags(int flags) { int retflags = 0; if (flags & REDISMODULE_ZADD_XX) retflags |= ZADD_XX; if (flags & REDISMODULE_ZADD_NX) retflags |= ZADD_NX; @@ -2409,7 +2409,7 @@ int RM_ZsetAddFlagsToCoreFlags(int flags) { } /* See previous function comment. */ -int RM_ZsetAddFlagsFromCoreFlags(int flags) { +int moduleZsetAddFlagsFromCoreFlags(int flags) { int retflags = 0; if (flags & ZADD_ADDED) retflags |= REDISMODULE_ZADD_ADDED; if (flags & ZADD_UPDATED) retflags |= REDISMODULE_ZADD_UPDATED; @@ -2454,12 +2454,12 @@ int RM_ZsetAdd(RedisModuleKey *key, double score, RedisModuleString *ele, int *f if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR; if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_ZSET); - if (flagsptr) flags = RM_ZsetAddFlagsToCoreFlags(*flagsptr); + if (flagsptr) flags = moduleZsetAddFlagsToCoreFlags(*flagsptr); if (zsetAdd(key->value,score,ele->ptr,&flags,NULL) == 0) { if (flagsptr) *flagsptr = 0; return REDISMODULE_ERR; } - if (flagsptr) *flagsptr = RM_ZsetAddFlagsFromCoreFlags(flags); + if (flagsptr) *flagsptr = moduleZsetAddFlagsFromCoreFlags(flags); return REDISMODULE_OK; } @@ -2481,7 +2481,7 @@ int RM_ZsetIncrby(RedisModuleKey *key, double score, RedisModuleString *ele, int if (!(key->mode & REDISMODULE_WRITE)) return REDISMODULE_ERR; if (key->value && key->value->type != OBJ_ZSET) return REDISMODULE_ERR; if (key->value == NULL) moduleCreateEmptyKey(key,REDISMODULE_KEYTYPE_ZSET); - if (flagsptr) flags = RM_ZsetAddFlagsToCoreFlags(*flagsptr); + if (flagsptr) flags = moduleZsetAddFlagsToCoreFlags(*flagsptr); flags |= ZADD_INCR; if (zsetAdd(key->value,score,ele->ptr,&flags,newscore) == 0) { if (flagsptr) *flagsptr = 0; @@ -2492,7 +2492,7 @@ int RM_ZsetIncrby(RedisModuleKey *key, double score, RedisModuleString *ele, int *flagsptr = 0; return REDISMODULE_ERR; } - if (flagsptr) *flagsptr = RM_ZsetAddFlagsFromCoreFlags(flags); + if (flagsptr) *flagsptr = moduleZsetAddFlagsFromCoreFlags(flags); return REDISMODULE_OK; } @@ -3163,9 +3163,8 @@ void moduleParseCallReply_Array(RedisModuleCallReply *reply) { reply->type = REDISMODULE_REPLY_ARRAY; } -/* Free a Call reply and all the nested replies it contains if it's an - * array. */ -void RM_FreeCallReply_Rec(RedisModuleCallReply *reply, int freenested){ +/* Recursive free reply function. */ +void moduleFreeCallReplyRec(RedisModuleCallReply *reply, int freenested){ /* Don't free nested replies by default: the user must always free the * toplevel reply. However be gentle and don't crash if the module * misuses the API. */ @@ -3175,7 +3174,7 @@ void RM_FreeCallReply_Rec(RedisModuleCallReply *reply, int freenested){ if (reply->type == REDISMODULE_REPLY_ARRAY) { size_t j; for (j = 0; j < reply->len; j++) - RM_FreeCallReply_Rec(reply->val.array+j,1); + moduleFreeCallReplyRec(reply->val.array+j,1); zfree(reply->val.array); } } @@ -3190,13 +3189,14 @@ void RM_FreeCallReply_Rec(RedisModuleCallReply *reply, int freenested){ } } -/* Wrapper for the recursive free reply function. This is needed in order - * to have the first level function to return on nested replies, but only - * if called by the module API. */ +/* Free a Call reply and all the nested replies it contains if it's an + * array. */ void RM_FreeCallReply(RedisModuleCallReply *reply) { - + /* This is a wrapper for the recursive free reply function. This is needed + * in order to have the first level function to return on nested replies, + * but only if called by the module API. */ RedisModuleCtx *ctx = reply->ctx; - RM_FreeCallReply_Rec(reply,0); + moduleFreeCallReplyRec(reply,0); autoMemoryFreed(ctx,REDISMODULE_AM_REPLY,reply); } @@ -4386,7 +4386,7 @@ const RedisModuleString *RM_GetKeyNameFromModuleKey(RedisModuleKey *key) { * RM_LogIOError() * */ -void RM_LogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_list ap) { +void moduleLogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_list ap) { char msg[LOG_MAX_LEN]; size_t name_len; int level; @@ -4425,7 +4425,7 @@ void RM_LogRaw(RedisModule *module, const char *levelstr, const char *fmt, va_li void RM_Log(RedisModuleCtx *ctx, const char *levelstr, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - RM_LogRaw(ctx? ctx->module: NULL,levelstr,fmt,ap); + moduleLogRaw(ctx? ctx->module: NULL,levelstr,fmt,ap); va_end(ap); } @@ -4437,7 +4437,7 @@ void RM_Log(RedisModuleCtx *ctx, const char *levelstr, const char *fmt, ...) { void RM_LogIOError(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - RM_LogRaw(io->type->module,levelstr,fmt,ap); + moduleLogRaw(io->type->module,levelstr,fmt,ap); va_end(ap); } -- cgit v1.2.1 From 294f93af97a6964c28f8d9cabbccc5da8950f9b0 Mon Sep 17 00:00:00 2001 From: Yang Bodong Date: Fri, 15 Jan 2021 21:32:58 +0800 Subject: Add lazyfree-lazy-user-flush config to control default behavior of FLUSH[ALL|DB], SCRIPT FLUSH (#8258) * Adds ASYNC and SYNC arguments to SCRIPT FLUSH * Adds SYNC argument to FLUSHDB and FLUSHALL * Adds new config to control the default behavior of FLUSHDB, FLUSHALL and SCRIPT FLUASH. the new behavior is as follows: * FLUSH[ALL|DB],SCRIPT FLUSH: Determine sync or async according to the value of lazyfree-lazy-user-flush. * FLUSH[ALL|DB],SCRIPT FLUSH ASYNC: Always flushes the database in an async manner. * FLUSH[ALL|DB],SCRIPT FLUSH SYNC: Always flushes the database in a sync manner. --- redis.conf | 7 +++++++ src/config.c | 1 + src/db.c | 18 ++++++++++-------- src/lazyfree.c | 18 ++++++++++++++++++ src/scripting.c | 32 +++++++++++++++++++++++++------- src/server.h | 2 ++ tests/unit/scripting.tcl | 9 +++++++++ 7 files changed, 72 insertions(+), 15 deletions(-) diff --git a/redis.conf b/redis.conf index a5062fda9..5f9eed7fe 100644 --- a/redis.conf +++ b/redis.conf @@ -1089,6 +1089,13 @@ replica-lazy-flush no lazyfree-lazy-user-del no +# FLUSHDB, FLUSHALL, and SCRIPT FLUSH support both asynchronous and synchronous +# deletion, which can be controlled by passing the [SYNC|ASYNC] flags into the +# commands. When neither flag is passed, this directive will be used to determine +# if the data should be deleted asynchronously. + +lazyfree-lazy-user-flush no + ################################ THREADED I/O ################################# # Redis is mostly single threaded, however there are certain threaded diff --git a/src/config.c b/src/config.c index 2e109dbae..2442ace8e 100644 --- a/src/config.c +++ b/src/config.c @@ -2385,6 +2385,7 @@ standardConfig configs[] = { createBoolConfig("lazyfree-lazy-expire", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_expire, 0, NULL, NULL), createBoolConfig("lazyfree-lazy-server-del", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_server_del, 0, NULL, NULL), createBoolConfig("lazyfree-lazy-user-del", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_user_del , 0, NULL, NULL), + createBoolConfig("lazyfree-lazy-user-flush", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_user_flush , 0, NULL, NULL), createBoolConfig("repl-disable-tcp-nodelay", NULL, MODIFIABLE_CONFIG, server.repl_disable_tcp_nodelay, 0, NULL, NULL), createBoolConfig("repl-diskless-sync", NULL, MODIFIABLE_CONFIG, server.repl_diskless_sync, 0, NULL, NULL), createBoolConfig("gopher-enabled", NULL, MODIFIABLE_CONFIG, server.gopher_enabled, 0, NULL, NULL), diff --git a/src/db.c b/src/db.c index 13126988f..29fef13fb 100644 --- a/src/db.c +++ b/src/db.c @@ -595,21 +595,23 @@ void signalFlushedDb(int dbid, int async) { /* Return the set of flags to use for the emptyDb() call for FLUSHALL * and FLUSHDB commands. * - * Currently the command just attempts to parse the "ASYNC" option. It - * also checks if the command arity is wrong. + * sync: flushes the database in an sync manner. + * async: flushes the database in an async manner. + * no option: determine sync or async according to the value of lazyfree-lazy-user-flush. * * On success C_OK is returned and the flags are stored in *flags, otherwise * C_ERR is returned and the function sends an error to the client. */ int getFlushCommandFlags(client *c, int *flags) { /* Parse the optional ASYNC option. */ - if (c->argc > 1) { - if (c->argc > 2 || strcasecmp(c->argv[1]->ptr,"async")) { - addReplyErrorObject(c,shared.syntaxerr); - return C_ERR; - } + if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"sync")) { + *flags = EMPTYDB_NO_FLAGS; + } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"async")) { *flags = EMPTYDB_ASYNC; + } else if (c->argc == 1) { + *flags = server.lazyfree_lazy_user_flush ? EMPTYDB_ASYNC : EMPTYDB_NO_FLAGS; } else { - *flags = EMPTYDB_NO_FLAGS; + addReplyErrorObject(c,shared.syntaxerr); + return C_ERR; } return C_OK; } diff --git a/src/lazyfree.c b/src/lazyfree.c index 8b9f0e2dc..f18b2027f 100644 --- a/src/lazyfree.c +++ b/src/lazyfree.c @@ -49,6 +49,14 @@ void lazyFreeTrackingTable(void *args[]) { atomicIncr(lazyfreed_objects,len); } +void lazyFreeLuaScripts(void *args[]) { + dict *lua_scripts = args[0]; + long long len = dictSize(lua_scripts); + dictRelease(lua_scripts); + atomicDecr(lazyfree_objects,len); + atomicIncr(lazyfreed_objects,len); +} + /* Return the number of currently pending objects to free. */ size_t lazyfreeGetPendingObjectsCount(void) { size_t aux; @@ -212,3 +220,13 @@ void freeTrackingRadixTreeAsync(rax *tracking) { atomicIncr(lazyfree_objects,tracking->numele); bioCreateLazyFreeJob(lazyFreeTrackingTable,1,tracking); } + +/* Free lua_scripts dict, if the dict is huge enough, free it in async way. */ +void freeLuaScriptsAsync(dict *lua_scripts) { + if (dictSize(lua_scripts) > LAZYFREE_THRESHOLD) { + atomicIncr(lazyfree_objects,dictSize(lua_scripts)); + bioCreateLazyFreeJob(lazyFreeLuaScripts,1,lua_scripts); + } else { + dictRelease(lua_scripts); + } +} diff --git a/src/scripting.c b/src/scripting.c index 75604e4d8..41469ee2e 100644 --- a/src/scripting.c +++ b/src/scripting.c @@ -1282,14 +1282,17 @@ void scriptingInit(int setup) { /* Release resources related to Lua scripting. * This function is used in order to reset the scripting environment. */ -void scriptingRelease(void) { - dictRelease(server.lua_scripts); +void scriptingRelease(int async) { + if (async) + freeLuaScriptsAsync(server.lua_scripts); + else + dictRelease(server.lua_scripts); server.lua_scripts_mem = 0; lua_close(server.lua); } -void scriptingReset(void) { - scriptingRelease(); +void scriptingReset(int async) { + scriptingRelease(async); scriptingInit(0); } @@ -1711,8 +1714,12 @@ void scriptCommand(client *c) { " Set the debug mode for subsequent scripts executed.", "EXISTS [ ...]", " Return information about the existence of the scripts in the script cache.", -"FLUSH", +"FLUSH [ASYNC|SYNC]", " Flush the Lua scripts cache. Very dangerous on replicas.", +" When called without the optional mode argument, the behavior is determined by the", +" lazyfree-lazy-user-flush configuration directive. Valid modes are:", +" * ASYNC: Asynchronously flush the scripts cache.", +" * SYNC: Synchronously flush the scripts cache.", "KILL", " Kill the currently executing Lua script.", "LOAD