From da04e6ed44bf80c5e2fe282522f924e8f55921cb Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 24 Jan 2013 16:20:53 +0100 Subject: [PATCH] Keyspace events added for more commands. --- src/bitops.c | 3 ++ src/db.c | 2 ++ src/sort.c | 4 +-- src/t_hash.c | 10 +++++- src/t_list.c | 46 +++++++++++++++++++++------ src/t_set.c | 31 +++++++++++++++--- src/t_string.c | 4 ++- src/t_zset.c | 85 ++++++++++++++++++++++++++++++-------------------- 8 files changed, 134 insertions(+), 51 deletions(-) diff --git a/src/bitops.c b/src/bitops.c index 75f3317a9..cda3adc7d 100644 --- a/src/bitops.c +++ b/src/bitops.c @@ -153,6 +153,7 @@ void setbitCommand(redisClient *c) { byteval |= ((on & 0x1) << bit); ((uint8_t*)o->ptr)[byte] = byteval; signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("setbit",c->argv[1],c->db->id); server.dirty++; addReply(c, bitval ? shared.cone : shared.czero); } @@ -346,9 +347,11 @@ void bitopCommand(redisClient *c) { if (maxlen) { o = createObject(REDIS_STRING,res); setKey(c->db,targetkey,o); + notifyKeyspaceEvent("set",targetkey,c->db->id); decrRefCount(o); } else if (dbDelete(c->db,targetkey)) { signalModifiedKey(c->db,targetkey); + notifyKeyspaceEvent("del",targetkey,c->db->id); } server.dirty++; addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */ diff --git a/src/db.c b/src/db.c index b45c73e5a..10e618246 100644 --- a/src/db.c +++ b/src/db.c @@ -587,12 +587,14 @@ void expireGenericCommand(redisClient *c, long long basetime, int unit) { rewriteClientCommandVector(c,2,aux,key); decrRefCount(aux); signalModifiedKey(c->db,key); + notifyKeyspaceEvent("del",key,c->db->id); addReply(c, shared.cone); return; } else { setExpire(c->db,key,when); addReply(c,shared.cone); signalModifiedKey(c->db,key); + notifyKeyspaceEvent("expire",key,c->db->id); server.dirty++; return; } diff --git a/src/sort.c b/src/sort.c index cd54072f3..a0971473d 100644 --- a/src/sort.c +++ b/src/sort.c @@ -504,9 +504,11 @@ void sortCommand(redisClient *c) { } if (outputlen) { setKey(c->db,storekey,sobj); + notifyKeyspaceEvent("sortstore",storekey,c->db->id); server.dirty += outputlen; } else if (dbDelete(c->db,storekey)) { signalModifiedKey(c->db,storekey); + notifyKeyspaceEvent("del",storekey,c->db->id); server.dirty++; } decrRefCount(sobj); @@ -525,5 +527,3 @@ void sortCommand(redisClient *c) { } zfree(vector); } - - diff --git a/src/t_hash.c b/src/t_hash.c index 414fb1bf8..e395ee265 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -474,6 +474,7 @@ void hsetCommand(redisClient *c) { update = hashTypeSet(o,c->argv[2],c->argv[3]); addReply(c, update ? shared.czero : shared.cone); signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("hset",c->argv[1],c->db->id); server.dirty++; } @@ -489,6 +490,7 @@ void hsetnxCommand(redisClient *c) { hashTypeSet(o,c->argv[2],c->argv[3]); addReply(c, shared.cone); signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("hset",c->argv[1],c->db->id); server.dirty++; } } @@ -510,6 +512,7 @@ void hmsetCommand(redisClient *c) { } addReply(c, shared.ok); signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("hset",c->argv[1],c->db->id); server.dirty++; } @@ -543,6 +546,7 @@ void hincrbyCommand(redisClient *c) { decrRefCount(new); addReplyLongLong(c,value); signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("hincrby",c->argv[1],c->db->id); server.dirty++; } @@ -569,6 +573,7 @@ void hincrbyfloatCommand(redisClient *c) { hashTypeSet(o,c->argv[2],new); addReplyBulk(c,new); signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("hincrbyfloat",c->argv[1],c->db->id); server.dirty++; /* Always replicate HINCRBYFLOAT as an HSET command with the final value @@ -649,7 +654,7 @@ void hmgetCommand(redisClient *c) { void hdelCommand(redisClient *c) { robj *o; - int j, deleted = 0; + int j, deleted = 0, keyremoved = 0; if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,o,REDIS_HASH)) return; @@ -659,12 +664,15 @@ void hdelCommand(redisClient *c) { deleted++; if (hashTypeLength(o) == 0) { dbDelete(c->db,c->argv[1]); + keyremoved = 1; break; } } } if (deleted) { signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("hdel",c->argv[1],c->db->id); + if (keyremoved) notifyKeyspaceEvent("del",c->argv[1],c->db->id); server.dirty += deleted; } addReplyLongLong(c,deleted); diff --git a/src/t_list.c b/src/t_list.c index d3d3f6c69..ea5f4718d 100644 --- a/src/t_list.c +++ b/src/t_list.c @@ -316,7 +316,12 @@ void pushGenericCommand(redisClient *c, int where) { pushed++; } addReplyLongLong(c, waiting + (lobj ? listTypeLength(lobj) : 0)); - if (pushed) signalModifiedKey(c->db,c->argv[1]); + if (pushed) { + char *event = (where == REDIS_HEAD) ? "lpush" : "rpush"; + + signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent(event,c->argv[1],c->db->id); + } server.dirty += pushed; } @@ -338,10 +343,6 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) { checkType(c,subject,REDIS_LIST)) return; if (refval != NULL) { - /* Note: we expect refval to be string-encoded because it is *not* the - * last argument of the multi-bulk LINSERT. */ - redisAssertWithInfo(c,refval,refval->encoding == REDIS_ENCODING_RAW); - /* We're not sure if this value can be inserted yet, but we cannot * convert the list inside the iterator. We don't want to loop over * the list twice (once to see if the value can be inserted and once @@ -366,6 +367,7 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) { ziplistLen(subject->ptr) > server.list_max_ziplist_entries) listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST); signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("linsert",c->argv[1],c->db->id); server.dirty++; } else { /* Notify client of a failed insert */ @@ -373,8 +375,11 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) { return; } } else { + char *event = (where == REDIS_HEAD) ? "lpush" : "rpush"; + listTypePush(subject,val,where); signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent(event,c->argv[1],c->db->id); server.dirty++; } @@ -469,6 +474,7 @@ void lsetCommand(redisClient *c) { decrRefCount(value); addReply(c,shared.ok); signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("lset",c->argv[1],c->db->id); server.dirty++; } } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { @@ -481,6 +487,7 @@ void lsetCommand(redisClient *c) { incrRefCount(value); addReply(c,shared.ok); signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("lset",c->argv[1],c->db->id); server.dirty++; } } else { @@ -496,9 +503,15 @@ void popGenericCommand(redisClient *c, int where) { if (value == NULL) { addReply(c,shared.nullbulk); } else { + char *event = (where == REDIS_HEAD) ? "lpop" : "rpop"; + addReplyBulk(c,value); decrRefCount(value); - if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]); + notifyKeyspaceEvent(event,c->argv[1],c->db->id); + if (listTypeLength(o) == 0) { + notifyKeyspaceEvent("del",c->argv[1],c->db->id); + dbDelete(c->db,c->argv[1]); + } signalModifiedKey(c->db,c->argv[1]); server.dirty++; } @@ -618,7 +631,12 @@ void ltrimCommand(redisClient *c) { } else { redisPanic("Unknown list encoding"); } - if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[1]); + + notifyKeyspaceEvent("ltrim",c->argv[1],c->db->id); + if (listTypeLength(o) == 0) { + dbDelete(c->db,c->argv[1]); + notifyKeyspaceEvent("del",c->argv[1],c->db->id); + } signalModifiedKey(c->db,c->argv[1]); server.dirty++; addReply(c,shared.ok); @@ -693,6 +711,7 @@ void rpoplpushHandlePush(redisClient *c, robj *dstkey, robj *dstobj, robj *value } signalModifiedKey(c->db,dstkey); listTypePush(dstobj,value,REDIS_HEAD); + notifyKeyspaceEvent("lpush",dstkey,c->db->id); /* Always send the pushed value to the client. */ addReplyBulk(c,value); } @@ -722,7 +741,11 @@ void rpoplpushCommand(redisClient *c) { decrRefCount(value); /* Delete the source list when it is empty */ - if (listTypeLength(sobj) == 0) dbDelete(c->db,touchedkey); + notifyKeyspaceEvent("rpop",touchedkey,c->db->id); + if (listTypeLength(sobj) == 0) { + dbDelete(c->db,touchedkey); + notifyKeyspaceEvent("del",touchedkey,c->db->id); + } signalModifiedKey(c->db,touchedkey); decrRefCount(touchedkey); server.dirty++; @@ -1046,6 +1069,7 @@ void blockingPopGenericCommand(redisClient *c, int where) { } else { if (listTypeLength(o) != 0) { /* Non empty list, this is like a non normal [LR]POP. */ + char *event = (where == REDIS_HEAD) ? "lpop" : "rpop"; robj *value = listTypePop(o,where); redisAssert(value != NULL); @@ -1053,7 +1077,11 @@ void blockingPopGenericCommand(redisClient *c, int where) { addReplyBulk(c,c->argv[j]); addReplyBulk(c,value); decrRefCount(value); - if (listTypeLength(o) == 0) dbDelete(c->db,c->argv[j]); + notifyKeyspaceEvent(event,c->argv[j],c->db->id); + if (listTypeLength(o) == 0) { + dbDelete(c->db,c->argv[j]); + notifyKeyspaceEvent("del",c->argv[j],c->db->id); + } signalModifiedKey(c->db,c->argv[j]); server.dirty++; diff --git a/src/t_set.c b/src/t_set.c index 0909d0301..70052a55e 100644 --- a/src/t_set.c +++ b/src/t_set.c @@ -266,14 +266,17 @@ void saddCommand(redisClient *c) { c->argv[j] = tryObjectEncoding(c->argv[j]); if (setTypeAdd(set,c->argv[j])) added++; } - if (added) signalModifiedKey(c->db,c->argv[1]); + if (added) { + signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("sadd",c->argv[1],c->db->id); + } server.dirty += added; addReplyLongLong(c,added); } void sremCommand(redisClient *c) { robj *set; - int j, deleted = 0; + int j, deleted = 0, keyremoved = 0; if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,set,REDIS_SET)) return; @@ -283,12 +286,15 @@ void sremCommand(redisClient *c) { deleted++; if (setTypeSize(set) == 0) { dbDelete(c->db,c->argv[1]); + keyremoved = 1; break; } } } if (deleted) { signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("srem",c->argv[1],c->db->id); + if (keyremoved) notifyKeyspaceEvent("del",c->argv[1],c->db->id); server.dirty += deleted; } addReplyLongLong(c,deleted); @@ -322,9 +328,13 @@ void smoveCommand(redisClient *c) { addReply(c,shared.czero); return; } + notifyKeyspaceEvent("srem",c->argv[1],c->db->id); /* Remove the src set from the database when empty */ - if (setTypeSize(srcset) == 0) dbDelete(c->db,c->argv[1]); + if (setTypeSize(srcset) == 0) { + dbDelete(c->db,c->argv[1]); + notifyKeyspaceEvent("del",c->argv[1],c->db->id); + } signalModifiedKey(c->db,c->argv[1]); signalModifiedKey(c->db,c->argv[2]); server.dirty++; @@ -336,7 +346,10 @@ void smoveCommand(redisClient *c) { } /* An extra key has changed when ele was successfully added to dstset */ - if (setTypeAdd(dstset,ele)) server.dirty++; + if (setTypeAdd(dstset,ele)) { + server.dirty++; + notifyKeyspaceEvent("sadd",c->argv[2],c->db->id); + } addReply(c,shared.cone); } @@ -378,6 +391,7 @@ void spopCommand(redisClient *c) { incrRefCount(ele); setTypeRemove(set,ele); } + notifyKeyspaceEvent("spop",c->argv[1],c->db->id); /* Replicate/AOF this command as an SREM operation */ aux = createStringObject("SREM",4); @@ -386,7 +400,10 @@ void spopCommand(redisClient *c) { decrRefCount(aux); addReplyBulk(c,ele); - if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]); + if (setTypeSize(set) == 0) { + dbDelete(c->db,c->argv[1]); + notifyKeyspaceEvent("del",c->argv[1],c->db->id); + } signalModifiedKey(c->db,c->argv[1]); server.dirty++; } @@ -691,6 +708,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum, if (setTypeSize(dstset) > 0) { dbAdd(c->db,dstkey,dstset); addReplyLongLong(c,setTypeSize(dstset)); + notifyKeyspaceEvent("sinterstore",dstkey,c->db->id); } else { decrRefCount(dstset); addReply(c,shared.czero); @@ -856,6 +874,9 @@ void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj * if (setTypeSize(dstset) > 0) { dbAdd(c->db,dstkey,dstset); addReplyLongLong(c,setTypeSize(dstset)); + notifyKeyspaceEvent( + op == REDIS_OP_UNION ? "sunionstore" : "sdiffstore", + dstkey,c->db->id); } else { decrRefCount(dstset); addReply(c,shared.czero); diff --git a/src/t_string.c b/src/t_string.c index a3e58e5b9..ed7840467 100644 --- a/src/t_string.c +++ b/src/t_string.c @@ -110,6 +110,7 @@ void getsetCommand(redisClient *c) { if (getGenericCommand(c) == REDIS_ERR) return; c->argv[2] = tryObjectEncoding(c->argv[2]); setKey(c->db,c->argv[1],c->argv[2]); + notifyKeyspaceEvent("set",c->argv[1],c->db->id); server.dirty++; } @@ -171,6 +172,7 @@ void setrangeCommand(redisClient *c) { o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value)); memcpy((char*)o->ptr+offset,value,sdslen(value)); signalModifiedKey(c->db,c->argv[1]); + notifyKeyspaceEvent("setrange",c->argv[1],c->db->id); server.dirty++; } addReplyLongLong(c,sdslen(o->ptr)); @@ -255,7 +257,7 @@ void msetGenericCommand(redisClient *c, int nx) { for (j = 1; j < c->argc; j += 2) { c->argv[j+1] = tryObjectEncoding(c->argv[j+1]); setKey(c->db,c->argv[j],c->argv[j+1]); - notifyKeyspaceEvent("set",c->argv[j+1],c->db->id); + notifyKeyspaceEvent("set",c->argv[j],c->db->id); } server.dirty += (c->argc-1)/2; addReply(c, nx ? shared.cone : shared.ok); diff --git a/src/t_zset.c b/src/t_zset.c index 0d92f681f..4a9383c12 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -844,9 +844,9 @@ void zaddGenericCommand(redisClient *c, int incr) { robj *ele; robj *zobj; robj *curobj; - double score = 0, *scores, curscore = 0.0; + double score = 0, *scores = NULL, curscore = 0.0; int j, elements = (c->argc-2)/2; - int added = 0; + int added = 0, updated = 0; if (c->argc % 2) { addReply(c,shared.syntaxerr); @@ -859,11 +859,7 @@ void zaddGenericCommand(redisClient *c, int incr) { scores = zmalloc(sizeof(double)*elements); for (j = 0; j < elements; j++) { if (getDoubleFromObjectOrReply(c,c->argv[2+j*2],&scores[j],NULL) - != REDIS_OK) - { - zfree(scores); - return; - } + != REDIS_OK) goto cleanup; } /* Lookup the key and create the sorted set if does not exist. */ @@ -880,8 +876,7 @@ void zaddGenericCommand(redisClient *c, int incr) { } else { if (zobj->type != REDIS_ZSET) { addReply(c,shared.wrongtypeerr); - zfree(scores); - return; + goto cleanup; } } @@ -898,10 +893,7 @@ void zaddGenericCommand(redisClient *c, int incr) { score += curscore; if (isnan(score)) { addReplyError(c,nanerr); - /* Don't need to check if the sorted set is empty - * because we know it has at least one element. */ - zfree(scores); - return; + goto cleanup; } } @@ -909,9 +901,8 @@ void zaddGenericCommand(redisClient *c, int incr) { if (score != curscore) { zobj->ptr = zzlDelete(zobj->ptr,eptr); zobj->ptr = zzlInsert(zobj->ptr,ele,score); - - signalModifiedKey(c->db,key); server.dirty++; + updated++; } } else { /* Optimize: check if the element is too large or the list @@ -921,10 +912,8 @@ void zaddGenericCommand(redisClient *c, int incr) { zsetConvert(zobj,REDIS_ENCODING_SKIPLIST); if (sdslen(ele->ptr) > server.zset_max_ziplist_value) zsetConvert(zobj,REDIS_ENCODING_SKIPLIST); - - signalModifiedKey(c->db,key); server.dirty++; - if (!incr) added++; + added++; } } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) { zset *zs = zobj->ptr; @@ -943,8 +932,7 @@ void zaddGenericCommand(redisClient *c, int incr) { addReplyError(c,nanerr); /* Don't need to check if the sorted set is empty * because we know it has at least one element. */ - zfree(scores); - return; + goto cleanup; } } @@ -956,29 +944,32 @@ void zaddGenericCommand(redisClient *c, int incr) { znode = zslInsert(zs->zsl,score,curobj); incrRefCount(curobj); /* Re-inserted in skiplist. */ dictGetVal(de) = &znode->score; /* Update score ptr. */ - - signalModifiedKey(c->db,key); server.dirty++; + updated++; } } else { znode = zslInsert(zs->zsl,score,ele); incrRefCount(ele); /* Inserted in skiplist. */ redisAssertWithInfo(c,NULL,dictAdd(zs->dict,ele,&znode->score) == DICT_OK); incrRefCount(ele); /* Added to dictionary. */ - - signalModifiedKey(c->db,key); server.dirty++; - if (!incr) added++; + added++; } } else { redisPanic("Unknown sorted set encoding"); } } - zfree(scores); if (incr) /* ZINCRBY */ addReplyDouble(c,score); else /* ZADD */ addReplyLongLong(c,added); + +cleanup: + zfree(scores); + if (added || updated) { + signalModifiedKey(c->db,key); + notifyKeyspaceEvent(incr ? "zincr" : "zadd", key, c->db->id); + } } void zaddCommand(redisClient *c) { @@ -992,7 +983,7 @@ void zincrbyCommand(redisClient *c) { void zremCommand(redisClient *c) { robj *key = c->argv[1]; robj *zobj; - int deleted = 0, j; + int deleted = 0, keyremoved = 0, j; if ((zobj = lookupKeyWriteOrReply(c,key,shared.czero)) == NULL || checkType(c,zobj,REDIS_ZSET)) return; @@ -1038,6 +1029,8 @@ void zremCommand(redisClient *c) { } if (deleted) { + notifyKeyspaceEvent("zrem",key,c->db->id); + if (keyremoved) notifyKeyspaceEvent("del",key,c->db->id); signalModifiedKey(c->db,key); server.dirty += deleted; } @@ -1048,6 +1041,7 @@ void zremrangebyscoreCommand(redisClient *c) { robj *key = c->argv[1]; robj *zobj; zrangespec range; + int keyremoved = 0; unsigned long deleted; /* Parse the range arguments. */ @@ -1061,17 +1055,27 @@ void zremrangebyscoreCommand(redisClient *c) { if (zobj->encoding == REDIS_ENCODING_ZIPLIST) { zobj->ptr = zzlDeleteRangeByScore(zobj->ptr,range,&deleted); - if (zzlLength(zobj->ptr) == 0) dbDelete(c->db,key); + if (zzlLength(zobj->ptr) == 0) { + dbDelete(c->db,key); + keyremoved = 1; + } } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) { zset *zs = zobj->ptr; deleted = zslDeleteRangeByScore(zs->zsl,range,zs->dict); if (htNeedsResize(zs->dict)) dictResize(zs->dict); - if (dictSize(zs->dict) == 0) dbDelete(c->db,key); + if (dictSize(zs->dict) == 0) { + dbDelete(c->db,key); + keyremoved = 1; + } } else { redisPanic("Unknown sorted set encoding"); } - if (deleted) signalModifiedKey(c->db,key); + if (deleted) { + signalModifiedKey(c->db,key); + notifyKeyspaceEvent("zrembyscore",key,c->db->id); + if (keyremoved) notifyKeyspaceEvent("del",key,c->db->id); + } server.dirty += deleted; addReplyLongLong(c,deleted); } @@ -1083,6 +1087,7 @@ void zremrangebyrankCommand(redisClient *c) { long end; int llen; unsigned long deleted; + int keyremoved = 0; if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) || (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return; @@ -1107,19 +1112,29 @@ void zremrangebyrankCommand(redisClient *c) { if (zobj->encoding == REDIS_ENCODING_ZIPLIST) { /* Correct for 1-based rank. */ zobj->ptr = zzlDeleteRangeByRank(zobj->ptr,start+1,end+1,&deleted); - if (zzlLength(zobj->ptr) == 0) dbDelete(c->db,key); + if (zzlLength(zobj->ptr) == 0) { + dbDelete(c->db,key); + keyremoved = 1; + } } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) { zset *zs = zobj->ptr; /* Correct for 1-based rank. */ deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict); if (htNeedsResize(zs->dict)) dictResize(zs->dict); - if (dictSize(zs->dict) == 0) dbDelete(c->db,key); + if (dictSize(zs->dict) == 0) { + dbDelete(c->db,key); + keyremoved = 1; + } } else { redisPanic("Unknown sorted set encoding"); } - if (deleted) signalModifiedKey(c->db,key); + if (deleted) { + signalModifiedKey(c->db,key); + notifyKeyspaceEvent("zrembyrank",key,c->db->id); + if (keyremoved) notifyKeyspaceEvent("del",key,c->db->id); + } server.dirty += deleted; addReplyLongLong(c,deleted); } @@ -1661,6 +1676,7 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) { if (dbDelete(c->db,dstkey)) { signalModifiedKey(c->db,dstkey); + notifyKeyspaceEvent("del",dstkey,c->db->id); touched = 1; server.dirty++; } @@ -1673,6 +1689,9 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) { dbAdd(c->db,dstkey,dstobj); addReplyLongLong(c,zsetLength(dstobj)); if (!touched) signalModifiedKey(c->db,dstkey); + notifyKeyspaceEvent( + (op == REDIS_OP_UNION) ? "zunionstore" : "zinterstore", + dstkey,c->db->id); server.dirty++; } else { decrRefCount(dstobj);