From 56ec1ff1cec53fc8d0709060c70ba311e07d402a Mon Sep 17 00:00:00 2001 From: binfeng-xin <106955810+binfeng-xin@users.noreply.github.com> Date: Mon, 27 Nov 2023 11:16:41 +0800 Subject: [PATCH] Call signalModifiedKey after the key modification is completed (#11144) Fix `signalModifiedKey()` order, call it after key modification completed, to ensure the state of key eventual consistency. When a key is modified, Redis calls `signalModifiedKey` to notify other systems, such as the watch system of transactions and the tracking system of client side caching. However, in some commands, the `signalModifiedKey` call happens during the key modification process instead of after the key modification is completed. This can potentially cause issues, as systems relying on `signalModifiedKey` may receive the "write in flight" status of the key rather than its final state. These commands include: 1. PFADD 2. LSET, LMOVE, LREM 3. ZPOPMIN, ZPOPMAX, BZPOPMIN, BZPOPMAX, ZMPOP, BZMPOP Currently there is no problem with Redis, but it is better to adjust the order of `signalModifiedKey()`, to avoid issues in future development on Redis. --------- Co-authored-by: zhaozhao.zz --- src/hyperloglog.c | 2 +- src/t_list.c | 26 ++++++++++++-------------- src/t_zset.c | 2 +- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/hyperloglog.c b/src/hyperloglog.c index 1a74f4793..4f6f1eb45 100644 --- a/src/hyperloglog.c +++ b/src/hyperloglog.c @@ -1220,10 +1220,10 @@ void pfaddCommand(client *c) { } hdr = o->ptr; if (updated) { + HLL_INVALIDATE_CACHE(hdr); signalModifiedKey(c,c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_STRING,"pfadd",c->argv[1],c->db->id); server.dirty += updated; - HLL_INVALIDATE_CACHE(hdr); } addReply(c, updated ? shared.cone : shared.czero); } diff --git a/src/t_list.c b/src/t_list.c index 8da0a21c4..966199e0e 100644 --- a/src/t_list.c +++ b/src/t_list.c @@ -631,15 +631,14 @@ void lsetCommand(client *c) { listTypeTryConversionAppend(o,c->argv,3,3,NULL,NULL); if (listTypeReplaceAtIndex(o,index,value)) { - addReply(c,shared.ok); - signalModifiedKey(c,c->db,c->argv[1]); - notifyKeyspaceEvent(NOTIFY_LIST,"lset",c->argv[1],c->db->id); - server.dirty++; - /* We might replace a big item with a small one or vice versa, but we've * already handled the growing case in listTypeTryConversionAppend() * above, so here we just need to try the conversion for shrinking. */ listTypeTryConversion(o,LIST_CONV_SHRINKING,NULL,NULL); + addReply(c,shared.ok); + signalModifiedKey(c,c->db,c->argv[1]); + notifyKeyspaceEvent(NOTIFY_LIST,"lset",c->argv[1],c->db->id); + server.dirty++; } else { addReplyErrorObject(c,shared.outofrangeerr); } @@ -1086,15 +1085,14 @@ void lremCommand(client *c) { listTypeReleaseIterator(li); if (removed) { - signalModifiedKey(c,c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_LIST,"lrem",c->argv[1],c->db->id); - } - - if (listTypeLength(subject) == 0) { - dbDelete(c->db,c->argv[1]); - notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id); - } else if (removed) { - listTypeTryConversion(subject,LIST_CONV_SHRINKING,NULL,NULL); + if (listTypeLength(subject) == 0) { + dbDelete(c->db,c->argv[1]); + notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id); + } else { + listTypeTryConversion(subject,LIST_CONV_SHRINKING,NULL,NULL); + } + signalModifiedKey(c,c->db,c->argv[1]); } addReplyLongLong(c,removed); @@ -1107,9 +1105,9 @@ void lmoveHandlePush(client *c, robj *dstkey, robj *dstobj, robj *value, dstobj = createListListpackObject(); dbAdd(c->db,dstkey,dstobj); } - signalModifiedKey(c,c->db,dstkey); listTypeTryConversionAppend(dstobj,&value,0,0,NULL,NULL); listTypePush(dstobj,value,where); + signalModifiedKey(c,c->db,dstkey); notifyKeyspaceEvent(NOTIFY_LIST, where == LIST_HEAD ? "lpush" : "rpush", dstkey, diff --git a/src/t_zset.c b/src/t_zset.c index e8fea7e47..762f4aee7 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -4036,7 +4036,6 @@ void genericZpopCommand(client *c, robj **keyv, int keyc, int where, int emitkey if (result_count == 0) { /* Do this only for the first iteration. */ char *events[2] = {"zpopmin","zpopmax"}; notifyKeyspaceEvent(NOTIFY_ZSET,events[where],key,c->db->id); - signalModifiedKey(c,c->db,key); } if (use_nested_array) { @@ -4055,6 +4054,7 @@ void genericZpopCommand(client *c, robj **keyv, int keyc, int where, int emitkey dbDelete(c->db,key); notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id); } + signalModifiedKey(c,c->db,key); if (c->cmd->proc == zmpopCommand) { /* Always replicate it as ZPOP[MIN|MAX] with COUNT option instead of ZMPOP. */