Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package redis for openSUSE:Factory checked in at 2024-01-10 21:50:57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/redis (Old) and /work/SRC/openSUSE:Factory/.redis.new.21961 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "redis" Wed Jan 10 21:50:57 2024 rev:93 rq:1137732 version:7.2.4 Changes: -------- --- /work/SRC/openSUSE:Factory/redis/redis.changes 2023-11-05 12:19:19.714313876 +0100 +++ /work/SRC/openSUSE:Factory/.redis.new.21961/redis.changes 2024-01-10 21:51:07.829643722 +0100 @@ -1,0 +2,17 @@ +Tue Jan 9 13:02:41 UTC 2024 - Marcus Rueckert <mrueck...@suse.de> + +- redis 7.2.4: (boo#1218646) + - Security fixes + - (CVE-2023-41056) In some cases, Redis may incorrectly handle + resizing of memory buffers which can result in incorrect + accounting of buffer sizes and lead to heap overflow and + potential remote code execution. + - Bug fixes + - Fix crashes of cluster commands clusters with mixed versions + of 7.0 and 7.2 (#12805, #12832) + - Fix slot ownership not being properly handled when deleting a + slot from a node (#12564) + - Fix atomicity issues with the RedisModuleEvent_Key module API + event (#12733) + +------------------------------------------------------------------- Old: ---- redis-7.2.3.tar.gz New: ---- redis-7.2.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ redis.spec ++++++ --- /var/tmp/diff_new_pack.HSSY6Q/_old 2024-01-10 21:51:08.453666383 +0100 +++ /var/tmp/diff_new_pack.HSSY6Q/_new 2024-01-10 21:51:08.453666383 +0100 @@ -1,7 +1,7 @@ # # spec file for package redis # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -20,7 +20,7 @@ %define _log_dir %{_localstatedir}/log/%{name} %define _conf_dir %{_sysconfdir}/%{name} Name: redis -Version: 7.2.3 +Version: 7.2.4 Release: 0 Summary: Persistent key-value database License: BSD-3-Clause ++++++ redis-7.2.3.tar.gz -> redis-7.2.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/00-RELEASENOTES new/redis-7.2.4/00-RELEASENOTES --- old/redis-7.2.3/00-RELEASENOTES 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/00-RELEASENOTES 2024-01-09 12:51:49.000000000 +0100 @@ -13,6 +13,26 @@ ================================================================================ +Redis 7.2.4 Released Tue 09 Jan 2024 10:45:52 IST +================================================================================ + +Upgrade urgency SECURITY: See security fixes below. + +Security fixes +============== +* (CVE-2023-41056) In some cases, Redis may incorrectly handle resizing of memory + buffers which can result in incorrect accounting of buffer sizes and lead to + heap overflow and potential remote code execution. + +Bug fixes +========= + +* Fix crashes of cluster commands clusters with mixed versions of 7.0 and 7.2 (#12805, #12832) +* Fix slot ownership not being properly handled when deleting a slot from a node (#12564) +* Fix atomicity issues with the RedisModuleEvent_Key module API event (#12733) + + +================================================================================ Redis 7.2.3 Released Wed 01 Nov 2023 12:00:00 IST ================================================================================ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/src/cluster.c new/redis-7.2.4/src/cluster.c --- old/redis-7.2.3/src/cluster.c 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/src/cluster.c 2024-01-09 12:51:49.000000000 +0100 @@ -1687,6 +1687,7 @@ serverAssert(retval == DICT_OK); memcpy(node->name, newname, CLUSTER_NAMELEN); clusterAddNode(node); + clusterAddNodeToShard(node->shard_id, node); } void clusterAddNodeToShard(const char *shard_id, clusterNode *node) { @@ -2234,6 +2235,7 @@ node->tls_port = msg_tls_port; node->cport = ntohs(g->cport); clusterAddNode(node); + clusterAddNodeToShard(node->shard_id, node); } } @@ -2411,7 +2413,6 @@ } clusterDelSlot(j); clusterAddSlot(sender,j); - bitmapClearBit(server.cluster->owner_not_claiming_slot, j); clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG| CLUSTER_TODO_UPDATE_STATE| CLUSTER_TODO_FSYNC_CONFIG); @@ -2676,11 +2677,24 @@ /* We know this will be valid since we validated it ahead of time */ ext = getNextPingExt(ext); } + /* If the node did not send us a hostname extension, assume * they don't have an announced hostname. Otherwise, we'll * set it now. */ updateAnnouncedHostname(sender, ext_hostname); updateAnnouncedHumanNodename(sender, ext_humannodename); + + /* If the node did not send us a shard-id extension, it means the sender + * does not support it (old version), node->shard_id is randomly generated. + * A cluster-wide consensus for the node's shard_id is not necessary. + * The key is maintaining consistency of the shard_id on each individual 7.2 node. + * As the cluster progressively upgrades to version 7.2, we can expect the shard_ids + * across all nodes to naturally converge and align. + * + * If sender is a replica, set the shard_id to the shard_id of its master. + * Otherwise, we'll set it now. */ + if (ext_shardid == NULL) ext_shardid = clusterNodeGetMaster(sender)->shard_id; + updateShardId(sender, ext_shardid); } @@ -3024,6 +3038,10 @@ clusterNodeAddSlave(master,sender); sender->slaveof = master; + /* Update the shard_id when a replica is connected to its + * primary in the very first time. */ + updateShardId(sender, master->shard_id); + /* Update config. */ clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG); } @@ -4942,6 +4960,8 @@ /* Clear the slot bit. */ serverAssert(clusterNodeClearSlotBit(n,slot) == 1); server.cluster->slots[slot] = NULL; + /* Make owner_not_claiming_slot flag consistent with slot ownership information. */ + bitmapClearBit(server.cluster->owner_not_claiming_slot, slot); return C_OK; } @@ -5709,7 +5729,7 @@ addReplyBulkCString(c, "slots"); /* Use slot_info_pairs from the primary only */ - while (n->slaveof != NULL) n = n->slaveof; + n = clusterNodeGetMaster(n); if (n->slot_info_pairs != NULL) { serverAssert((n->slot_info_pairs_count % 2) == 0); @@ -7643,6 +7663,11 @@ return (*server.db->slots_to_keys).by_slot[hashslot].count; } +clusterNode *clusterNodeGetMaster(clusterNode *node) { + while (node->slaveof != NULL) node = node->slaveof; + return node; +} + /* ----------------------------------------------------------------------------- * Operation(s) on channel rax tree. * -------------------------------------------------------------------------- */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/src/cluster.h new/redis-7.2.4/src/cluster.h --- old/redis-7.2.3/src/cluster.h 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/src/cluster.h 2024-01-09 12:51:49.000000000 +0100 @@ -413,6 +413,7 @@ void clusterInitListeners(void); void clusterCron(void); void clusterBeforeSleep(void); +clusterNode *clusterNodeGetMaster(clusterNode *node); clusterNode *getNodeByQuery(client *c, struct redisCommand *cmd, robj **argv, int argc, int *hashslot, int *ask); int verifyClusterNodeId(const char *name, int length); clusterNode *clusterLookupNode(const char *name, int length); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/src/dict.c new/redis-7.2.4/src/dict.c --- old/redis-7.2.3/src/dict.c 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/src/dict.c 2024-01-09 12:51:49.000000000 +0100 @@ -1414,30 +1414,25 @@ * table (global setting) or we should avoid it but the ratio between * elements/buckets is over the "safe" threshold, we resize doubling * the number of buckets. */ - if (!dictTypeExpandAllowed(d)) - return DICT_OK; if ((dict_can_resize == DICT_RESIZE_ENABLE && d->ht_used[0] >= DICTHT_SIZE(d->ht_size_exp[0])) || (dict_can_resize != DICT_RESIZE_FORBID && d->ht_used[0] / DICTHT_SIZE(d->ht_size_exp[0]) > dict_force_resize_ratio)) { + if (!dictTypeExpandAllowed(d)) + return DICT_OK; return dictExpand(d, d->ht_used[0] + 1); } return DICT_OK; } -/* TODO: clz optimization */ /* Our hash table capability is a power of two */ static signed char _dictNextExp(unsigned long size) { - unsigned char e = DICT_HT_INITIAL_EXP; - + if (size <= DICT_HT_INITIAL_SIZE) return DICT_HT_INITIAL_EXP; if (size >= LONG_MAX) return (8*sizeof(long)-1); - while(1) { - if (((unsigned long)1<<e) >= size) - return e; - e++; - } + + return 8*sizeof(long) - __builtin_clzl(size-1); } /* Finds and returns the position within the dict where the provided key should diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/src/evict.c new/redis-7.2.4/src/evict.c --- old/redis-7.2.3/src/evict.c 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/src/evict.c 2024-01-09 12:51:49.000000000 +0100 @@ -667,6 +667,7 @@ * * AOF and Output buffer memory will be freed eventually so * we only care about memory used by the key space. */ + enterExecutionUnit(1, 0); delta = (long long) zmalloc_used_memory(); latencyStartMonitor(eviction_latency); dbGenericDelete(db,keyobj,server.lazyfree_lazy_eviction,DB_FLAG_KEY_EVICTED); @@ -679,6 +680,7 @@ notifyKeyspaceEvent(NOTIFY_EVICTED, "evicted", keyobj, db->id); propagateDeletion(db,keyobj,server.lazyfree_lazy_eviction); + exitExecutionUnit(); postExecutionUnitOperations(); decrRefCount(keyobj); keys_freed++; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/src/expire.c new/redis-7.2.4/src/expire.c --- old/redis-7.2.3/src/expire.c 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/src/expire.c 2024-01-09 12:51:49.000000000 +0100 @@ -54,10 +54,12 @@ int activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) { long long t = dictGetSignedIntegerVal(de); if (now > t) { + enterExecutionUnit(1, 0); sds key = dictGetKey(de); robj *keyobj = createStringObject(key,sdslen(key)); deleteExpiredKeyAndPropagate(db,keyobj); decrRefCount(keyobj); + exitExecutionUnit(); return 1; } else { return 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/src/module.c new/redis-7.2.4/src/module.c --- old/redis-7.2.3/src/module.c 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/src/module.c 2024-01-09 12:51:49.000000000 +0100 @@ -12115,6 +12115,19 @@ return REDISMODULE_OK; } +/* Unregister module-related things, called when moduleLoad fails or moduleUnload. */ +void moduleUnregisterCleanup(RedisModule *module) { + moduleFreeAuthenticatedClients(module); + moduleUnregisterCommands(module); + moduleUnsubscribeNotifications(module); + moduleUnregisterSharedAPI(module); + moduleUnregisterUsedAPI(module); + moduleUnregisterFilters(module); + moduleUnsubscribeAllServerEvents(module); + moduleRemoveConfigs(module); + moduleUnregisterAuthCBs(module); +} + /* Load a module and initialize it. On success C_OK is returned, otherwise * C_ERR is returned. */ int moduleLoad(const char *path, void **module_argv, int module_argc, int is_loadex) { @@ -12149,11 +12162,7 @@ serverLog(LL_WARNING, "Module %s initialization failed. Module not loaded",path); if (ctx.module) { - moduleUnregisterCommands(ctx.module); - moduleUnregisterSharedAPI(ctx.module); - moduleUnregisterUsedAPI(ctx.module); - moduleRemoveConfigs(ctx.module); - moduleUnregisterAuthCBs(ctx.module); + moduleUnregisterCleanup(ctx.module); moduleFreeModuleStructure(ctx.module); } moduleFreeContext(&ctx); @@ -12194,8 +12203,6 @@ } if (post_load_err) { - /* Unregister module auth callbacks (if any exist) that this Module registered onload. */ - moduleUnregisterAuthCBs(ctx.module); moduleUnload(ctx.module->name, NULL); moduleFreeContext(&ctx); return C_ERR; @@ -12253,17 +12260,7 @@ } } - moduleFreeAuthenticatedClients(module); - moduleUnregisterCommands(module); - moduleUnregisterSharedAPI(module); - moduleUnregisterUsedAPI(module); - moduleUnregisterFilters(module); - moduleUnregisterAuthCBs(module); - moduleRemoveConfigs(module); - - /* Remove any notification subscribers this module might have */ - moduleUnsubscribeNotifications(module); - moduleUnsubscribeAllServerEvents(module); + moduleUnregisterCleanup(module); /* Unload the dynamic library. */ if (dlclose(module->handle) == -1) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/src/sds.c new/redis-7.2.4/src/sds.c --- old/redis-7.2.3/src/sds.c 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/src/sds.c 2024-01-09 12:51:49.000000000 +0100 @@ -349,20 +349,22 @@ * type. */ int use_realloc = (oldtype==type || (type < oldtype && type > SDS_TYPE_8)); size_t newlen = use_realloc ? oldhdrlen+size+1 : hdrlen+size+1; - int alloc_already_optimal = 0; - #if defined(USE_JEMALLOC) - /* je_nallocx returns the expected allocation size for the newlen. - * We aim to avoid calling realloc() when using Jemalloc if there is no - * change in the allocation size, as it incurs a cost even if the - * allocation size stays the same. */ - alloc_already_optimal = (je_nallocx(newlen, 0) == zmalloc_size(sh)); - #endif - if (use_realloc && !alloc_already_optimal) { - newsh = s_realloc(sh, newlen); - if (newsh == NULL) return NULL; - s = (char*)newsh+oldhdrlen; - } else if (!alloc_already_optimal) { + if (use_realloc) { + int alloc_already_optimal = 0; + #if defined(USE_JEMALLOC) + /* je_nallocx returns the expected allocation size for the newlen. + * We aim to avoid calling realloc() when using Jemalloc if there is no + * change in the allocation size, as it incurs a cost even if the + * allocation size stays the same. */ + alloc_already_optimal = (je_nallocx(newlen, 0) == zmalloc_size(sh)); + #endif + if (!alloc_already_optimal) { + newsh = s_realloc(sh, newlen); + if (newsh == NULL) return NULL; + s = (char*)newsh+oldhdrlen; + } + } else { newsh = s_malloc(newlen); if (newsh == NULL) return NULL; memcpy((char*)newsh+hdrlen, s, len); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/src/version.h new/redis-7.2.4/src/version.h --- old/redis-7.2.3/src/version.h 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/src/version.h 2024-01-09 12:51:49.000000000 +0100 @@ -1,2 +1,2 @@ -#define REDIS_VERSION "7.2.3" -#define REDIS_VERSION_NUM 0x00070203 +#define REDIS_VERSION "7.2.4" +#define REDIS_VERSION_NUM 0x00070204 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/tests/modules/commandfilter.c new/redis-7.2.4/tests/modules/commandfilter.c --- old/redis-7.2.3/tests/modules/commandfilter.c 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/tests/modules/commandfilter.c 2024-01-09 12:51:49.000000000 +0100 @@ -1,6 +1,7 @@ #include "redismodule.h" #include <string.h> +#include <strings.h> static RedisModuleString *log_key_name; @@ -92,7 +93,7 @@ return REDISMODULE_OK; } -int CommandFilter_UnfilteredClientdId(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) +int CommandFilter_UnfilteredClientId(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { if (argc < 2) return RedisModule_WrongArity(ctx); @@ -192,7 +193,7 @@ if (RedisModule_Init(ctx,"commandfilter",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) return REDISMODULE_ERR; - if (argc != 2) { + if (argc != 2 && argc != 3) { RedisModule_Log(ctx, "warning", "Log key name not specified"); return REDISMODULE_ERR; } @@ -219,7 +220,7 @@ return REDISMODULE_ERR; if (RedisModule_CreateCommand(ctx, unfiltered_clientid_name, - CommandFilter_UnfilteredClientdId, "admin", 1,1,1) == REDISMODULE_ERR) + CommandFilter_UnfilteredClientId, "admin", 1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; if ((filter = RedisModule_RegisterCommandFilter(ctx, CommandFilter_CommandFilter, @@ -229,6 +230,16 @@ if ((filter1 = RedisModule_RegisterCommandFilter(ctx, CommandFilter_BlmoveSwap, 0)) == NULL) return REDISMODULE_ERR; + if (argc == 3) { + const char *ptr = RedisModule_StringPtrLen(argv[2], NULL); + if (!strcasecmp(ptr, "noload")) { + /* This is a hint that we return ERR at the last moment of OnLoad. */ + RedisModule_FreeString(ctx, log_key_name); + if (retained) RedisModule_FreeString(NULL, retained); + return REDISMODULE_ERR; + } + } + return REDISMODULE_OK; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/tests/modules/hooks.c new/redis-7.2.4/tests/modules/hooks.c --- old/redis-7.2.3/tests/modules/hooks.c 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/tests/modules/hooks.c 2024-01-09 12:51:49.000000000 +0100 @@ -33,6 +33,7 @@ #include "redismodule.h" #include <stdio.h> #include <string.h> +#include <strings.h> #include <assert.h> /* We need to store events to be able to test and see what we got, and we can't @@ -407,9 +408,6 @@ return REDISMODULE_ERR; \ } - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx,"testhook",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR) return REDISMODULE_ERR; @@ -471,6 +469,18 @@ if (RedisModule_CreateCommand(ctx,"hooks.pexpireat", cmdKeyExpiry,"",0,0,0) == REDISMODULE_ERR) return REDISMODULE_ERR; + if (argc == 1) { + const char *ptr = RedisModule_StringPtrLen(argv[0], NULL); + if (!strcasecmp(ptr, "noload")) { + /* This is a hint that we return ERR at the last moment of OnLoad. */ + RedisModule_FreeDict(ctx, event_log); + RedisModule_FreeDict(ctx, removed_event_log); + RedisModule_FreeDict(ctx, removed_subevent_type); + RedisModule_FreeDict(ctx, removed_expiry_log); + return REDISMODULE_ERR; + } + } + return REDISMODULE_OK; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/tests/modules/keyspace_events.c new/redis-7.2.4/tests/modules/keyspace_events.c --- old/redis-7.2.3/tests/modules/keyspace_events.c 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/tests/modules/keyspace_events.c 2024-01-09 12:51:49.000000000 +0100 @@ -36,6 +36,7 @@ #include "redismodule.h" #include <stdio.h> #include <string.h> +#include <strings.h> #include <unistd.h> ustime_t cached_time = 0; @@ -318,9 +319,6 @@ /* This function must be present on each Redis module. It is used in order to * register the commands into the Redis server. */ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { - REDISMODULE_NOT_USED(argv); - REDISMODULE_NOT_USED(argc); - if (RedisModule_Init(ctx,"testkeyspace",1,REDISMODULE_APIVER_1) == REDISMODULE_ERR){ return REDISMODULE_ERR; } @@ -405,6 +403,16 @@ return REDISMODULE_ERR; } + if (argc == 1) { + const char *ptr = RedisModule_StringPtrLen(argv[0], NULL); + if (!strcasecmp(ptr, "noload")) { + /* This is a hint that we return ERR at the last moment of OnLoad. */ + RedisModule_FreeDict(ctx, loaded_event_log); + RedisModule_FreeDict(ctx, module_event_log); + return REDISMODULE_ERR; + } + } + return REDISMODULE_OK; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/tests/modules/postnotifications.c new/redis-7.2.4/tests/modules/postnotifications.c --- old/redis-7.2.3/tests/modules/postnotifications.c 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/tests/modules/postnotifications.c 2024-01-09 12:51:49.000000000 +0100 @@ -90,6 +90,10 @@ return REDISMODULE_OK; /* do not count the evicted key */ } + if (strncmp(key_str, "before_evicted", 14) == 0) { + return REDISMODULE_OK; /* do not count the before_evicted key */ + } + RedisModuleString *new_key = RedisModule_CreateString(NULL, "evicted", 7); RedisModule_AddPostNotificationJob(ctx, KeySpace_PostNotificationString, new_key, KeySpace_PostNotificationStringFreePD); return REDISMODULE_OK; @@ -186,6 +190,55 @@ return REDISMODULE_OK; } +typedef struct KeySpace_EventPostNotificationCtx { + RedisModuleString *triggered_on; + RedisModuleString *new_key; +} KeySpace_EventPostNotificationCtx; + +static void KeySpace_ServerEventPostNotificationFree(void *pd) { + KeySpace_EventPostNotificationCtx *pn_ctx = pd; + RedisModule_FreeString(NULL, pn_ctx->new_key); + RedisModule_FreeString(NULL, pn_ctx->triggered_on); + RedisModule_Free(pn_ctx); +} + +static void KeySpace_ServerEventPostNotification(RedisModuleCtx *ctx, void *pd) { + REDISMODULE_NOT_USED(ctx); + KeySpace_EventPostNotificationCtx *pn_ctx = pd; + RedisModuleCallReply* rep = RedisModule_Call(ctx, "lpush", "!ss", pn_ctx->new_key, pn_ctx->triggered_on); + RedisModule_FreeCallReply(rep); +} + +static void KeySpace_ServerEventCallback(RedisModuleCtx *ctx, RedisModuleEvent eid, uint64_t subevent, void *data) { + REDISMODULE_NOT_USED(eid); + REDISMODULE_NOT_USED(data); + if (subevent > 3) { + RedisModule_Log(ctx, "warning", "Got an unexpected subevent '%ld'", subevent); + return; + } + static const char* events[] = { + "before_deleted", + "before_expired", + "before_evicted", + "before_overwritten", + }; + + const RedisModuleString *key_name = RedisModule_GetKeyNameFromModuleKey(((RedisModuleKeyInfo*)data)->key); + const char *key_str = RedisModule_StringPtrLen(key_name, NULL); + + for (int i = 0 ; i < 4 ; ++i) { + const char *event = events[i]; + if (strncmp(key_str, event , strlen(event)) == 0) { + return; /* don't log any event on our tracking keys */ + } + } + + KeySpace_EventPostNotificationCtx *pn_ctx = RedisModule_Alloc(sizeof(*pn_ctx)); + pn_ctx->triggered_on = RedisModule_HoldString(NULL, (RedisModuleString*)key_name); + pn_ctx->new_key = RedisModule_CreateString(NULL, events[subevent], strlen(events[subevent])); + RedisModule_AddPostNotificationJob(ctx, KeySpace_ServerEventPostNotification, pn_ctx, KeySpace_ServerEventPostNotificationFree); +} + /* This function must be present on each Redis module. It is used in order to * register the commands into the Redis server. */ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { @@ -200,6 +253,14 @@ return REDISMODULE_ERR; } + int with_key_events = 0; + if (argc >= 1) { + const char *arg = RedisModule_StringPtrLen(argv[0], 0); + if (strcmp(arg, "with_key_events") == 0) { + with_key_events = 1; + } + } + RedisModule_SetModuleOptions(ctx, REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS); if(RedisModule_SubscribeToKeyspaceEvents(ctx, REDISMODULE_NOTIFY_STRING, KeySpace_NotificationString) != REDISMODULE_OK){ @@ -222,6 +283,12 @@ return REDISMODULE_ERR; } + if (with_key_events) { + if(RedisModule_SubscribeToServerEvent(ctx, RedisModuleEvent_Key, KeySpace_ServerEventCallback) != REDISMODULE_OK){ + return REDISMODULE_ERR; + } + } + if (RedisModule_CreateCommand(ctx, "postnotification.async_set", KeySpace_PostNotificationsAsyncSet, "write", 0, 0, 0) == REDISMODULE_ERR){ return REDISMODULE_ERR; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/tests/unit/moduleapi/async_rm_call.tcl new/redis-7.2.4/tests/unit/moduleapi/async_rm_call.tcl --- old/redis-7.2.3/tests/unit/moduleapi/async_rm_call.tcl 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/tests/unit/moduleapi/async_rm_call.tcl 2024-01-09 12:51:49.000000000 +0100 @@ -314,6 +314,14 @@ r lpush l a assert_equal [$rd read] {OK} + # Explanation of the first multi exec block: + # {lpop l} - pop the value by our blocking command 'blpop_and_set_multiple_keys' + # {set string_foo 1} - the action of our blocking command 'blpop_and_set_multiple_keys' + # {set string_bar 2} - the action of our blocking command 'blpop_and_set_multiple_keys' + # {incr string_changed{string_foo}} - post notification job that was registered when 'string_foo' changed + # {incr string_changed{string_bar}} - post notification job that was registered when 'string_bar' changed + # {incr string_total} - post notification job that was registered when string_changed{string_foo} changed + # {incr string_total} - post notification job that was registered when string_changed{string_bar} changed assert_replication_stream $repl { {select *} {lpush l a} @@ -355,6 +363,25 @@ r lpush l a assert_equal [$rd read] {OK} + # Explanation of the first multi exec block: + # {lpop l} - pop the value by our blocking command 'blpop_and_set_multiple_keys' + # {set string_foo 1} - the action of our blocking command 'blpop_and_set_multiple_keys' + # {set string_bar 2} - the action of our blocking command 'blpop_and_set_multiple_keys' + # {incr string_changed{string_foo}} - post notification job that was registered when 'string_foo' changed + # {incr string_changed{string_bar}} - post notification job that was registered when 'string_bar' changed + # {incr string_total} - post notification job that was registered when string_changed{string_foo} changed + # {incr string_total} - post notification job that was registered when string_changed{string_bar} changed + # + # Explanation of the second multi exec block: + # {lpop l} - pop the value by our blocking command 'blpop_and_set_multiple_keys' + # {del string_foo} - lazy expiration of string_foo when 'blpop_and_set_multiple_keys' tries to write to it. + # {set string_foo 1} - the action of our blocking command 'blpop_and_set_multiple_keys' + # {set string_bar 2} - the action of our blocking command 'blpop_and_set_multiple_keys' + # {incr expired} - the post notification job, registered after string_foo got expired + # {incr string_changed{string_foo}} - post notification job triggered when we set string_foo + # {incr string_changed{string_bar}} - post notification job triggered when we set string_bar + # {incr string_total} - post notification job triggered when we incr 'string_changed{string_foo}' + # {incr string_total} - post notification job triggered when we incr 'string_changed{string_bar}' assert_replication_stream $repl { {select *} {lpush l a} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/tests/unit/moduleapi/commandfilter.tcl new/redis-7.2.4/tests/unit/moduleapi/commandfilter.tcl --- old/redis-7.2.3/tests/unit/moduleapi/commandfilter.tcl 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/tests/unit/moduleapi/commandfilter.tcl 2024-01-09 12:51:49.000000000 +0100 @@ -95,7 +95,7 @@ test "Unload the module - commandfilter" { assert_equal {OK} [r module unload commandfilter] } -} +} test {RM_CommandFilterArgInsert and script argv caching} { # coverage for scripts calling commands that expand the argv array @@ -162,4 +162,14 @@ $rr close } -} \ No newline at end of file +} + +start_server {} { + test {OnLoad failure will handle un-registration} { + catch {r module load $testmodule log-key 0 noload} + r set mykey @log + assert_equal [r lrange log-key 0 -1] {} + r rpush mylist elem1 @delme elem2 + assert_equal [r lrange mylist 0 -1] {elem1 @delme elem2} + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/tests/unit/moduleapi/hooks.tcl new/redis-7.2.4/tests/unit/moduleapi/hooks.tcl --- old/redis-7.2.3/tests/unit/moduleapi/hooks.tcl 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/tests/unit/moduleapi/hooks.tcl 2024-01-09 12:51:49.000000000 +0100 @@ -310,4 +310,12 @@ assert_equal [string match {*module-event-shutdown*} [exec tail -5 < $replica_stdout]] 1 } } + + start_server {} { + test {OnLoad failure will handle un-registration} { + catch {r module load $testmodule noload} + r flushall + r ping + } + } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/tests/unit/moduleapi/keyspace_events.tcl new/redis-7.2.4/tests/unit/moduleapi/keyspace_events.tcl --- old/redis-7.2.3/tests/unit/moduleapi/keyspace_events.tcl 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/tests/unit/moduleapi/keyspace_events.tcl 2024-01-09 12:51:49.000000000 +0100 @@ -102,4 +102,17 @@ assert_equal {OK} [r set x 1 EX 1] } } + + start_server {} { + test {OnLoad failure will handle un-registration} { + catch {r module load $testmodule noload} + r set x 1 + r hset y f v + r lpush z 1 2 3 + r sadd p 1 2 3 + r zadd t 1 f1 2 f2 + r xadd s * f v + r ping + } + } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/redis-7.2.3/tests/unit/moduleapi/postnotifications.tcl new/redis-7.2.4/tests/unit/moduleapi/postnotifications.tcl --- old/redis-7.2.3/tests/unit/moduleapi/postnotifications.tcl 2023-11-01 13:38:13.000000000 +0100 +++ new/redis-7.2.4/tests/unit/moduleapi/postnotifications.tcl 2024-01-09 12:51:49.000000000 +0100 @@ -1,7 +1,8 @@ set testmodule [file normalize tests/modules/postnotifications.so] tags "modules" { - start_server [list overrides [list loadmodule "$testmodule"]] { + start_server {} { + r module load $testmodule with_key_events test {Test write on post notification callback} { set repl [attach_to_replication_stream] @@ -9,11 +10,12 @@ r set string_x 1 assert_equal {1} [r get string_changed{string_x}] assert_equal {1} [r get string_total] - + r set string_x 2 assert_equal {2} [r get string_changed{string_x}] assert_equal {2} [r get string_total] + # the {lpush before_overwritten string_x} is a post notification job registered when 'string_x' was overwritten assert_replication_stream $repl { {multi} {select *} @@ -23,6 +25,7 @@ {exec} {multi} {set string_x 2} + {lpush before_overwritten string_x} {incr string_changed{string_x}} {incr string_total} {exec} @@ -37,7 +40,7 @@ assert_equal {OK} [r postnotification.async_set] assert_equal {1} [r get string_changed{string_x}] assert_equal {1} [r get string_total] - + assert_replication_stream $repl { {multi} {select *} @@ -63,12 +66,14 @@ fail "Failed waiting for x to expired" } + # the {lpush before_expired x} is a post notification job registered before 'x' got expired assert_replication_stream $repl { {select *} {set x 1} {pexpireat x *} {multi} {del x} + {lpush before_expired x} {incr expired} {exec} } @@ -85,12 +90,14 @@ after 10 assert_equal {} [r get x] + # the {lpush before_expired x} is a post notification job registered before 'x' got expired assert_replication_stream $repl { {select *} {set x 1} {pexpireat x *} {multi} {del x} + {lpush before_expired x} {incr expired} {exec} } @@ -108,6 +115,7 @@ after 10 assert_equal {OK} [r set read_x 1] + # the {lpush before_expired x} is a post notification job registered before 'x' got expired assert_replication_stream $repl { {select *} {set x 1} @@ -115,6 +123,7 @@ {multi} {set read_x 1} {del x} + {lpush before_expired x} {incr expired} {exec} } @@ -143,16 +152,18 @@ r flushall set repl [attach_to_replication_stream] r set x 1 - r config set maxmemory-policy allkeys-random + r config set maxmemory-policy allkeys-random r config set maxmemory 1 assert_error {OOM *} {r set y 1} + # the {lpush before_evicted x} is a post notification job registered before 'x' got evicted assert_replication_stream $repl { {select *} {set x 1} {multi} {del x} + {lpush before_evicted x} {incr evicted} {exec} } @@ -164,7 +175,8 @@ set testmodule2 [file normalize tests/modules/keyspace_events.so] tags "modules" { - start_server [list overrides [list loadmodule "$testmodule"]] { + start_server {} { + r module load $testmodule with_key_events r module load $testmodule2 test {Test write on post notification callback} { set repl [attach_to_replication_stream] @@ -172,7 +184,7 @@ r set string_x 1 assert_equal {1} [r get string_changed{string_x}] assert_equal {1} [r get string_total] - + r set string_x 2 assert_equal {2} [r get string_changed{string_x}] assert_equal {2} [r get string_total] @@ -181,6 +193,7 @@ assert_equal {1} [r get string_changed{string1_x}] assert_equal {3} [r get string_total] + # the {lpush before_overwritten string_x} is a post notification job registered before 'string_x' got overwritten assert_replication_stream $repl { {multi} {select *} @@ -190,6 +203,7 @@ {exec} {multi} {set string_x 2} + {lpush before_overwritten string_x} {incr string_changed{string_x}} {incr string_total} {exec} @@ -202,4 +216,4 @@ close_replication_stream $repl } } -} \ No newline at end of file +} ++++++ redis.hashes ++++++ --- /var/tmp/diff_new_pack.HSSY6Q/_old 2024-01-10 21:51:09.037687591 +0100 +++ /var/tmp/diff_new_pack.HSSY6Q/_new 2024-01-10 21:51:09.041687737 +0100 @@ -170,4 +170,6 @@ hash redis-7.0.14.tar.gz sha256 7e1cdf347f4970ea39d5b7fdb19aedec4c21942e202de65bdeb782d38d2f299f http://download.redis.io/releases/redis-7.0.14.tar.gz hash redis-7.2.2.tar.gz sha256 ca999be08800edc6d265379c4c7aafad92f0ee400692e4e2d69829ab4b4c3d08 http://download.redis.io/releases/redis-7.2.2.tar.gz hash redis-7.2.3.tar.gz sha256 3e2b196d6eb4ddb9e743088bfc2915ccbb42d40f5a8a3edd8cb69c716ec34be7 http://download.redis.io/releases/redis-7.2.3.tar.gz +hash redis-7.0.15.tar.gz sha256 98066f5363504b26c34dd20fbcc3c957990d764cdf42576c836fc021073f4341 http://download.redis.io/releases/redis-7.0.15.tar.gz +hash redis-7.2.4.tar.gz sha256 8d104c26a154b29fd67d6568b4f375212212ad41e0c2caa3d66480e78dbd3b59 http://download.redis.io/releases/redis-7.2.4.tar.gz