Merge commit 'e91ca9fee9c56ef319b407b104afc435be0c53cb' into redis_6_merge

Former-commit-id: 516c8c6e231cdfc87a3db5be401407cb7afa3937
This commit is contained in:
John Sully 2020-04-14 20:42:48 -04:00
commit 4f56e9c3b0
14 changed files with 136 additions and 49 deletions

View File

@ -114,7 +114,10 @@ Redis 6.0 is mostly a strict superset of 5.0, you should not have any problem
upgrading your application from 5.0 to 6.0. However this is a list of small upgrading your application from 5.0 to 6.0. However this is a list of small
non-backward compatible changes introduced in the 6.0 release: non-backward compatible changes introduced in the 6.0 release:
* Nothing found yet. * The SPOP <count> command no longer returns null when the set key does not
exist. Now it returns the empty set as it should and as happens when it is
called with a 0 argument. This is technically a fix, however it changes the
old behavior.
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

@ -403,6 +403,7 @@ extern "C" void aeDeleteEventLoop(aeEventLoop *eventLoop) {
close(eventLoop->fdCmdRead); close(eventLoop->fdCmdRead);
close(eventLoop->fdCmdWrite); close(eventLoop->fdCmdWrite);
/* Free the time events list. */
auto *te = eventLoop->timeEventHead; auto *te = eventLoop->timeEventHead;
while (te) while (te)
{ {

View File

@ -1848,13 +1848,14 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
serverLog(LL_VERBOSE, serverLog(LL_VERBOSE,
"Background AOF rewrite signal handler took %lldus", ustime()-now); "Background AOF rewrite signal handler took %lldus", ustime()-now);
} else if (!bysignal && exitcode != 0) { } else if (!bysignal && exitcode != 0) {
/* SIGUSR1 is whitelisted, so we have a way to kill a child without
* tirggering an error condition. */
if (bysignal != SIGUSR1)
g_pserver->aof_lastbgrewrite_status = C_ERR; g_pserver->aof_lastbgrewrite_status = C_ERR;
serverLog(LL_WARNING, serverLog(LL_WARNING,
"Background AOF rewrite terminated with error"); "Background AOF rewrite terminated with error");
} else { } else {
/* SIGUSR1 is whitelisted, so we have a way to kill a child without
* tirggering an error condition. */
if (bysignal != SIGUSR1)
g_pserver->aof_lastbgrewrite_status = C_ERR; g_pserver->aof_lastbgrewrite_status = C_ERR;
serverLog(LL_WARNING, serverLog(LL_WARNING,

View File

@ -110,12 +110,12 @@ clientBufferLimitsConfig clientBufferLimitsDefaults[CLIENT_TYPE_OBUF_COUNT] = {
/* Generic config infrastructure function pointers /* Generic config infrastructure function pointers
* int is_valid_fn(val, err) * int is_valid_fn(val, err)
* Return 1 when val is valid, and 0 when invalid. * Return 1 when val is valid, and 0 when invalid.
* Optionslly set err to a static error string. * Optionally set err to a static error string.
* int update_fn(val, prev, err) * int update_fn(val, prev, err)
* This function is called only for CONFIG SET command (not at config file parsing) * This function is called only for CONFIG SET command (not at config file parsing)
* It is called after the actual config is applied, * It is called after the actual config is applied,
* Return 1 for success, and 0 for failure. * Return 1 for success, and 0 for failure.
* Optionslly set err to a static error string. * Optionally set err to a static error string.
* On failure the config change will be reverted. * On failure the config change will be reverted.
*/ */
@ -567,7 +567,8 @@ void loadServerConfigFromString(char *config) {
return; return;
loaderr: loaderr:
fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR ***\n"); fprintf(stderr, "\n*** FATAL CONFIG FILE ERROR (Redis %s) ***\n",
KEYDB_REAL_VERSION);
fprintf(stderr, "Reading the configuration file, at line %d\n", linenum); fprintf(stderr, "Reading the configuration file, at line %d\n", linenum);
fprintf(stderr, ">>> '%s'\n", lines[i]); fprintf(stderr, ">>> '%s'\n", lines[i]);
fprintf(stderr, "%s\n", err); fprintf(stderr, "%s\n", err);
@ -777,7 +778,7 @@ void configSetCommand(client *c) {
* config_set_memory_field(name,var) */ * config_set_memory_field(name,var) */
} config_set_memory_field( } config_set_memory_field(
"client-query-buffer-limit",cserver.client_max_querybuf_len) { "client-query-buffer-limit",cserver.client_max_querybuf_len) {
/* Everyhing else is an error... */ /* Everything else is an error... */
} config_set_else { } config_set_else {
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s", addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
(char*)ptrFromObj(c->argv[2])); (char*)ptrFromObj(c->argv[2]));
@ -1745,16 +1746,15 @@ static int enumConfigSet(typeData data, sds value, int update, const char **err)
sds enumerr = sdsnew("argument must be one of the following: "); sds enumerr = sdsnew("argument must be one of the following: ");
configEnum *enumNode = data.enumd.enum_value; configEnum *enumNode = data.enumd.enum_value;
while(enumNode->name != NULL) { while(enumNode->name != NULL) {
enumerr = sdscatlen(enumerr, enumNode->name, strlen(enumNode->name)); enumerr = sdscatlen(enumerr, enumNode->name,
strlen(enumNode->name));
enumerr = sdscatlen(enumerr, ", ", 2); enumerr = sdscatlen(enumerr, ", ", 2);
enumNode++; enumNode++;
} }
sdsrange(enumerr,0,-3); /* Remove final ", ". */
enumerr[sdslen(enumerr) - 2] = '\0';
/* Make sure we don't overrun the fixed buffer */
enumerr[LOADBUF_SIZE - 1] = '\0';
strncpy(loadbuf, enumerr, LOADBUF_SIZE); strncpy(loadbuf, enumerr, LOADBUF_SIZE);
loadbuf[LOADBUF_SIZE - 1] = '\0';
sdsfree(enumerr); sdsfree(enumerr);
*err = loadbuf; *err = loadbuf;

View File

@ -1559,6 +1559,8 @@ int expireIfNeeded(redisDb *db, robj *key) {
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
* API to get key arguments from commands * API to get key arguments from commands
* ---------------------------------------------------------------------------*/ * ---------------------------------------------------------------------------*/
#define MAX_KEYS_BUFFER 256
thread_local static int getKeysTempBuffer[MAX_KEYS_BUFFER];
/* The base case is to use the keys position as given in the command table /* The base case is to use the keys position as given in the command table
* (firstkey, lastkey, step). */ * (firstkey, lastkey, step). */
@ -1573,7 +1575,12 @@ int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, in
last = cmd->lastkey; last = cmd->lastkey;
if (last < 0) last = argc+last; if (last < 0) last = argc+last;
keys = (int*)zmalloc(sizeof(int)*((last - cmd->firstkey)+1), MALLOC_SHARED);
int count = ((last - cmd->firstkey)+1);
keys = getKeysTempBuffer;
if (count > MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int)*count);
for (j = cmd->firstkey; j <= last; j += cmd->keystep) { for (j = cmd->firstkey; j <= last; j += cmd->keystep) {
if (j >= argc) { if (j >= argc) {
/* Modules commands, and standard commands with a not fixed number /* Modules commands, and standard commands with a not fixed number
@ -1583,7 +1590,7 @@ int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, in
* return no keys and expect the command implementation to report * return no keys and expect the command implementation to report
* an arity or syntax error. */ * an arity or syntax error. */
if (cmd->flags & CMD_MODULE || cmd->arity < 0) { if (cmd->flags & CMD_MODULE || cmd->arity < 0) {
zfree(keys); getKeysFreeResult(keys);
*numkeys = 0; *numkeys = 0;
return NULL; return NULL;
} else { } else {
@ -1619,6 +1626,7 @@ int *getKeysFromCommand(struct redisCommand *cmd, robj **argv, int argc, int *nu
/* Free the result of getKeysFromCommand. */ /* Free the result of getKeysFromCommand. */
void getKeysFreeResult(int *result) { void getKeysFreeResult(int *result) {
if (result != getKeysTempBuffer)
zfree(result); zfree(result);
} }
@ -1640,7 +1648,9 @@ int *zunionInterGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *nu
/* Keys in z{union,inter}store come from two places: /* Keys in z{union,inter}store come from two places:
* argv[1] = storage key, * argv[1] = storage key,
* argv[3...n] = keys to intersect */ * argv[3...n] = keys to intersect */
keys = (int*)zmalloc(sizeof(int)*(num+1), MALLOC_SHARED); keys = getKeysTempBuffer;
if (num+1>MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int)*(num+1));
/* Add all key positions for argv[3...n] to keys[] */ /* Add all key positions for argv[3...n] to keys[] */
for (i = 0; i < num; i++) keys[i] = 3+i; for (i = 0; i < num; i++) keys[i] = 3+i;
@ -1666,7 +1676,10 @@ int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
return NULL; return NULL;
} }
keys = (int*)zmalloc(sizeof(int)*num, MALLOC_SHARED); keys = getKeysTempBuffer;
if (num>MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int)*num);
*numkeys = num; *numkeys = num;
/* Add all key positions for argv[3...n] to keys[] */ /* Add all key positions for argv[3...n] to keys[] */
@ -1687,7 +1700,7 @@ int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
UNUSED(cmd); UNUSED(cmd);
num = 0; num = 0;
keys = (int*)zmalloc(sizeof(int)*2, MALLOC_SHARED); /* Alloc 2 places for the worst case. */ keys = getKeysTempBuffer; /* Alloc 2 places for the worst case. */
keys[num++] = 1; /* <sort-key> is always present. */ keys[num++] = 1; /* <sort-key> is always present. */
@ -1745,7 +1758,10 @@ int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkey
} }
} }
keys = (int*)zmalloc(sizeof(int)*num, MALLOC_SHARED); keys = getKeysTempBuffer;
if (num>MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int)*num);
for (i = 0; i < num; i++) keys[i] = first+i; for (i = 0; i < num; i++) keys[i] = first+i;
*numkeys = num; *numkeys = num;
return keys; return keys;
@ -1778,7 +1794,9 @@ int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numk
* argv[1] = key, * argv[1] = key,
* argv[5...n] = stored key if present * argv[5...n] = stored key if present
*/ */
keys = (int*)zmalloc(sizeof(int) * num, MALLOC_SHARED); keys = getKeysTempBuffer;
if (num>MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int) * num);
/* Add all key positions to keys[] */ /* Add all key positions to keys[] */
keys[0] = 1; keys[0] = 1;
@ -1796,7 +1814,7 @@ int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys
UNUSED(cmd); UNUSED(cmd);
if (argc >= 3 && !strcasecmp(szFromObj(argv[1]),"usage")) { if (argc >= 3 && !strcasecmp(szFromObj(argv[1]),"usage")) {
keys = (int*)zmalloc(sizeof(int) * 1); keys = getKeysTempBuffer;
keys[0] = 2; keys[0] = 2;
*numkeys = 1; *numkeys = 1;
return keys; return keys;
@ -1843,7 +1861,10 @@ int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
num /= 2; /* We have half the keys as there are arguments because num /= 2; /* We have half the keys as there are arguments because
there are also the IDs, one per key. */ there are also the IDs, one per key. */
keys = (int*)zmalloc(sizeof(int) * num, MALLOC_SHARED); keys = getKeysTempBuffer;
if (num>MAX_KEYS_BUFFER)
keys = (int*)zmalloc(sizeof(int) * num);
for (i = streams_pos+1; i < argc-num; i++) keys[i-streams_pos-1] = i; for (i = streams_pos+1; i < argc-num; i++) keys[i-streams_pos-1] = i;
*numkeys = num; *numkeys = num;
return keys; return keys;

View File

@ -694,9 +694,12 @@ NULL
sds stats = sdsempty(); sds stats = sdsempty();
char buf[4096]; char buf[4096];
if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK) if (getLongFromObjectOrReply(c, c->argv[2], &dbid, NULL) != C_OK) {
sdsfree(stats);
return; return;
}
if (dbid < 0 || dbid >= cserver.dbnum) { if (dbid < 0 || dbid >= cserver.dbnum) {
sdsfree(stats);
addReplyError(c,"Out of range database"); addReplyError(c,"Out of range database");
return; return;
} }

View File

@ -871,6 +871,10 @@ unsigned long dictScan(dict *d,
if (dictSize(d) == 0) return 0; if (dictSize(d) == 0) return 0;
/* Having a safe iterator means no rehashing can happen, see _dictRehashStep.
* This is needed in case the scan callback tries to do dictFind or alike. */
d->iterators++;
if (!dictIsRehashing(d)) { if (!dictIsRehashing(d)) {
t0 = &(d->ht[0]); t0 = &(d->ht[0]);
m0 = t0->sizemask; m0 = t0->sizemask;
@ -937,6 +941,9 @@ unsigned long dictScan(dict *d,
} while (v & (m0 ^ m1)); } while (v & (m0 ^ m1));
} }
/* undo the ++ at the top */
d->iterators--;
return v; return v;
} }

View File

@ -1056,6 +1056,17 @@ RedisModuleString *RM_CreateStringFromLongLong(RedisModuleCtx *ctx, long long ll
return RM_CreateString(ctx,buf,len); return RM_CreateString(ctx,buf,len);
} }
/* Like RedisModule_CreatString(), but creates a string starting from a double
* integer instead of taking a buffer and its length.
*
* The returned string must be released with RedisModule_FreeString() or by
* enabling automatic memory management. */
RedisModuleString *RM_CreateStringFromDouble(RedisModuleCtx *ctx, double d) {
char buf[128];
size_t len = d2string(buf,sizeof(buf),d);
return RM_CreateString(ctx,buf,len);
}
/* Like RedisModule_CreatString(), but creates a string starting from a long /* Like RedisModule_CreatString(), but creates a string starting from a long
* double. * double.
* *
@ -3605,6 +3616,8 @@ void moduleTypeNameByID(char *name, uint64_t moduleid) {
* // Optional fields * // Optional fields
* .digest = myType_DigestCallBack, * .digest = myType_DigestCallBack,
* .mem_usage = myType_MemUsageCallBack, * .mem_usage = myType_MemUsageCallBack,
* .aux_load = myType_AuxRDBLoadCallBack,
* .aux_save = myType_AuxRDBSaveCallBack,
* } * }
* *
* * **rdb_load**: A callback function pointer that loads data from RDB files. * * **rdb_load**: A callback function pointer that loads data from RDB files.
@ -3612,6 +3625,10 @@ void moduleTypeNameByID(char *name, uint64_t moduleid) {
* * **aof_rewrite**: A callback function pointer that rewrites data as commands. * * **aof_rewrite**: A callback function pointer that rewrites data as commands.
* * **digest**: A callback function pointer that is used for `DEBUG DIGEST`. * * **digest**: A callback function pointer that is used for `DEBUG DIGEST`.
* * **free**: A callback function pointer that can free a type value. * * **free**: A callback function pointer that can free a type value.
* * **aux_save**: A callback function pointer that saves out of keyspace data to RDB files.
* 'when' argument is either REDISMODULE_AUX_BEFORE_RDB or REDISMODULE_AUX_AFTER_RDB.
* * **aux_load**: A callback function pointer that loads out of keyspace data from RDB files.
* Similar to aux_save, returns REDISMODULE_OK on success, and ERR otherwise.
* *
* The **digest* and **mem_usage** methods should currently be omitted since * The **digest* and **mem_usage** methods should currently be omitted since
* they are not yet implemented inside the Redis modules core. * they are not yet implemented inside the Redis modules core.
@ -6641,24 +6658,32 @@ void RM_ScanCursorDestroy(RedisModuleScanCursor *cursor) {
zfree(cursor); zfree(cursor);
} }
/* Scan api that allows a module to scan all the keys and value in the selected db. /* Scan API that allows a module to scan all the keys and value in
* the selected db.
* *
* Callback for scan implementation. * Callback for scan implementation.
* void scan_callback(RedisModuleCtx *ctx, RedisModuleString *keyname, RedisModuleKey *key, void *privdata); * void scan_callback(RedisModuleCtx *ctx, RedisModuleString *keyname,
* - ctx - the redis module context provided to for the scan. * RedisModuleKey *key, void *privdata);
* - keyname - owned by the caller and need to be retained if used after this function. * ctx - the redis module context provided to for the scan.
* - key - holds info on the key and value, it is provided as best effort, in some cases it might * keyname - owned by the caller and need to be retained if used after this
* be NULL, in which case the user should (can) use RedisModule_OpenKey (and CloseKey too). * function.
* 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. * 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: * The way it should be used:
* RedisModuleCursor *c = RedisModule_ScanCursorCreate(); * RedisModuleCursor *c = RedisModule_ScanCursorCreate();
* while(RedisModule_Scan(ctx, c, callback, privateData)); * while(RedisModule_Scan(ctx, c, callback, privateData));
* RedisModule_ScanCursorDestroy(c); * RedisModule_ScanCursorDestroy(c);
* *
* It is also possible to use this API from another thread while the lock is acquired durring * It is also possible to use this API from another thread while the lock
* the actuall call to RM_Scan: * is acquired durring the actuall call to RM_Scan:
*
* RedisModuleCursor *c = RedisModule_ScanCursorCreate(); * RedisModuleCursor *c = RedisModule_ScanCursorCreate();
* RedisModule_ThreadSafeContextLock(ctx); * RedisModule_ThreadSafeContextLock(ctx);
* while(RedisModule_Scan(ctx, c, callback, privateData)){ * while(RedisModule_Scan(ctx, c, callback, privateData)){
@ -6668,9 +6693,26 @@ void RM_ScanCursorDestroy(RedisModuleScanCursor *cursor) {
* } * }
* RedisModule_ScanCursorDestroy(c); * RedisModule_ScanCursorDestroy(c);
* *
* The function will return 1 if there are more elements to scan and 0 otherwise, * The function will return 1 if there are more elements to scan and
* possibly setting errno if the call failed. * 0 otherwise, possibly setting errno if the call failed.
* It is also possible to restart and existing cursor using RM_CursorRestart. */ *
* It is also possible to restart and existing cursor using RM_CursorRestart.
*
* IMPORTANT: This API is very similar to the Redis SCAN command from the
* point of view of the guarantees it provides. This means that the API
* may report duplicated keys, but guarantees to report at least one time
* every key that was there from the start to the end of the scanning process.
*
* NOTE: If you do database changes within the callback, you should be aware
* that the internal state of the database may change. For instance it is safe
* to delete or modify the current key, but may not be safe to delete any
* other key.
* Moreover playing with the Redis keyspace while iterating may have the
* effect of returning more duplicates. A safe pattern is to store the keys
* names you want to modify elsewhere, and perform the actions on the keys
* later when the iteration is complete. Howerver this can cost a lot of
* memory, so it may make sense to just operate on the current key when
* possible during the iteration, given that this is safe. */
int RM_Scan(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata) { int RM_Scan(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata) {
if (cursor->done) { if (cursor->done) {
errno = ENOENT; errno = ENOENT;
@ -6750,7 +6792,15 @@ static void moduleScanKeyCallback(void *privdata, const dictEntry *de) {
* *
* The function will return 1 if there are more elements to scan and 0 otherwise, * The function will return 1 if there are more elements to scan and 0 otherwise,
* possibly setting errno if the call failed. * possibly setting errno if the call failed.
* It is also possible to restart and existing cursor using RM_CursorRestart. */ * It is also possible to restart and existing cursor using RM_CursorRestart.
*
* NOTE: Certain operations are unsafe while iterating the object. For instance
* while the API guarantees to return at least one time all the elements that
* are present in the data structure consistently from the start to the end
* of the iteration (see HSCAN and similar commands documentation), the more
* you play with the elements, the more duplicates you may get. In general
* deleting the current element of the data structure is safe, while removing
* the key you are iterating is not safe. */
int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata) { int RM_ScanKey(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata) {
if (key == NULL || key->value == NULL) { if (key == NULL || key->value == NULL) {
errno = EINVAL; errno = EINVAL;
@ -6856,7 +6906,7 @@ int RM_Fork(RedisModuleForkDoneHandler cb, void *user_data) {
g_pserver->module_child_pid = childpid; g_pserver->module_child_pid = childpid;
moduleForkInfo.done_handler = cb; moduleForkInfo.done_handler = cb;
moduleForkInfo.done_handler_user_data = user_data; moduleForkInfo.done_handler_user_data = user_data;
serverLog(LL_NOTICE, "Module fork started pid: %d ", childpid); serverLog(LL_VERBOSE, "Module fork started pid: %d ", childpid);
} }
return childpid; return childpid;
} }
@ -6879,7 +6929,7 @@ int TerminateModuleForkChild(int child_pid, int wait) {
g_pserver->module_child_pid != child_pid) return C_ERR; g_pserver->module_child_pid != child_pid) return C_ERR;
int statloc; int statloc;
serverLog(LL_NOTICE,"Killing running module fork child: %ld", serverLog(LL_VERBOSE,"Killing running module fork child: %ld",
(long) g_pserver->module_child_pid); (long) g_pserver->module_child_pid);
if (kill(g_pserver->module_child_pid,SIGUSR1) != -1 && wait) { if (kill(g_pserver->module_child_pid,SIGUSR1) != -1 && wait) {
while(wait4(g_pserver->module_child_pid,&statloc,0,NULL) != while(wait4(g_pserver->module_child_pid,&statloc,0,NULL) !=
@ -7806,6 +7856,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(CreateStringFromCallReply); REGISTER_API(CreateStringFromCallReply);
REGISTER_API(CreateString); REGISTER_API(CreateString);
REGISTER_API(CreateStringFromLongLong); REGISTER_API(CreateStringFromLongLong);
REGISTER_API(CreateStringFromDouble);
REGISTER_API(CreateStringFromLongDouble); REGISTER_API(CreateStringFromLongDouble);
REGISTER_API(CreateStringFromString); REGISTER_API(CreateStringFromString);
REGISTER_API(CreateStringPrintf); REGISTER_API(CreateStringPrintf);

View File

@ -2340,7 +2340,7 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
io.ver = 2; io.ver = 2;
/* Call the rdb_load method of the module providing the 10 bit /* Call the rdb_load method of the module providing the 10 bit
* encoding version in the lower 10 bits of the module ID. */ * encoding version in the lower 10 bits of the module ID. */
if (mt->aux_load(&io,moduleid&1023, when) || io.error) { if (mt->aux_load(&io,moduleid&1023, when) != REDISMODULE_OK || io.error) {
moduleTypeNameByID(name,moduleid); moduleTypeNameByID(name,moduleid);
serverLog(LL_WARNING,"The RDB file contains module AUX data for the module type '%s', that the responsible module is not able to load. Check for modules log above for additional clues.", name); serverLog(LL_WARNING,"The RDB file contains module AUX data for the module type '%s', that the responsible module is not able to load. Check for modules log above for additional clues.", name);
exit(1); exit(1);

View File

@ -471,6 +471,7 @@ size_t REDISMODULE_API_FUNC(RedisModule_CallReplyLength)(RedisModuleCallReply *r
RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx); RedisModuleCallReply *REDISMODULE_API_FUNC(RedisModule_CallReplyArrayElement)(RedisModuleCallReply *reply, size_t idx);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len); RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateString)(RedisModuleCtx *ctx, const char *ptr, size_t len);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll); RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(RedisModuleCtx *ctx, long long ll);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly); RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str); RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...); RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...);
@ -730,6 +731,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(CreateStringFromCallReply); REDISMODULE_GET_API(CreateStringFromCallReply);
REDISMODULE_GET_API(CreateString); REDISMODULE_GET_API(CreateString);
REDISMODULE_GET_API(CreateStringFromLongLong); REDISMODULE_GET_API(CreateStringFromLongLong);
REDISMODULE_GET_API(CreateStringFromDouble);
REDISMODULE_GET_API(CreateStringFromLongDouble); REDISMODULE_GET_API(CreateStringFromLongDouble);
REDISMODULE_GET_API(CreateStringFromString); REDISMODULE_GET_API(CreateStringFromString);
REDISMODULE_GET_API(CreateStringPrintf); REDISMODULE_GET_API(CreateStringPrintf);

View File

@ -3767,7 +3767,6 @@ void replicationCron(void) {
propagateMasterStaleKeys(); propagateMasterStaleKeys();
/* Remove the RDB file used for replication if Redis is not running /* Remove the RDB file used for replication if Redis is not running
* with any persistence. */ * with any persistence. */
removeRDBUsedToSyncReplicas(); removeRDBUsedToSyncReplicas();

View File

@ -4020,6 +4020,7 @@ void addReplyCommand(client *c, struct redisCommand *cmd) {
flagcount += addReplyCommandFlag(c,cmd,CMD_SKIP_SLOWLOG, "skip_slowlog"); flagcount += addReplyCommandFlag(c,cmd,CMD_SKIP_SLOWLOG, "skip_slowlog");
flagcount += addReplyCommandFlag(c,cmd,CMD_ASKING, "asking"); flagcount += addReplyCommandFlag(c,cmd,CMD_ASKING, "asking");
flagcount += addReplyCommandFlag(c,cmd,CMD_FAST, "fast"); flagcount += addReplyCommandFlag(c,cmd,CMD_FAST, "fast");
flagcount += addReplyCommandFlag(c,cmd,CMD_NO_AUTH, "no_auth");
if ((cmd->getkeys_proc && !(cmd->flags & CMD_MODULE)) || if ((cmd->getkeys_proc && !(cmd->flags & CMD_MODULE)) ||
cmd->flags & CMD_MODULE_GETKEYS) cmd->flags & CMD_MODULE_GETKEYS)
{ {

View File

@ -420,7 +420,7 @@ void spopWithCountCommand(client *c) {
/* Make sure a key with the name inputted exists, and that it's type is /* Make sure a key with the name inputted exists, and that it's type is
* indeed a set. Otherwise, return nil */ * indeed a set. Otherwise, return nil */
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.null[c->resp])) if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.emptyset[c->resp]))
== NULL || checkType(c,set,OBJ_SET)) return; == NULL || checkType(c,set,OBJ_SET)) return;
/* If count is zero, serve an empty set ASAP to avoid special /* If count is zero, serve an empty set ASAP to avoid special

View File

@ -1073,9 +1073,7 @@ size_t streamReplyWithRangeFromConsumerPEL(client *c, stream *s, streamID *start
* by the user by other means. In that case we signal it emitting * by the user by other means. In that case we signal it emitting
* the ID but then a NULL entry for the fields. */ * the ID but then a NULL entry for the fields. */
addReplyArrayLen(c,2); addReplyArrayLen(c,2);
streamID id; addReplyStreamID(c,&thisid);
streamDecodeID(ri.key,&id);
addReplyStreamID(c,&id);
addReplyNullArray(c); addReplyNullArray(c);
} else { } else {
streamNACK *nack = (streamNACK*)ri.data; streamNACK *nack = (streamNACK*)ri.data;