From b9bc4f9132e92b54cb0521511a7859dc34ad5cf9 Mon Sep 17 00:00:00 2001 From: antirez Date: Fri, 25 Jan 2013 13:19:08 +0100 Subject: [PATCH] Keyspace events: it is now possible to select subclasses of events. When keyspace events are enabled, the overhead is not sever but noticeable, so this commit introduces the ability to select subclasses of events in order to avoid to generate events the user is not interested in. The events can be selected using redis.conf or CONFIG SET / GET. --- redis.conf | 37 ++++++++++-- src/bitops.c | 6 +- src/config.c | 24 ++++++-- src/db.c | 13 +++-- src/notify.c | 117 ++++++++++++++++++++++++++------------ src/redis.c | 6 +- src/redis.h | 23 +++++++- src/sort.c | 5 +- src/t_hash.c | 16 +++--- src/t_list.c | 33 ++++++----- src/t_set.c | 23 ++++---- src/t_string.c | 18 +++--- src/t_zset.c | 22 ++++--- tests/assets/default.conf | 2 +- 14 files changed, 237 insertions(+), 108 deletions(-) diff --git a/redis.conf b/redis.conf index d7bdcfd5..a6932cdf 100644 --- a/redis.conf +++ b/redis.conf @@ -457,10 +457,39 @@ slowlog-max-len 128 # PUBLISH __keyspace@0__:foo del # PUBLISH __keyevent@0__:del foo # -# While the overhead of this feature is relatively small most users don't -# need it so it is disabled by default. You can enable it setting the -# following configuration option to yes. -notify-keyspace-events no +# It is possible to select the events that Redis will notify among a set +# of classes. Every class is identified by a single character: +# +# K Keyspace events, published with __keyspace@__ prefix. +# E Keyevent events, published with __keyevent@__ prefix. +# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... +# $ String commands +# l List commands +# s Set commands +# h Hash commands +# z Sorted set commands +# x Expired events (events generated every time a key expires) +# e Evicted events (events generated when a key is evicted for maxmemory) +# A Alias for g$lshzxe, so that the "AKE" string means all the events. +# +# The "notify-keyspace-events" takes as argument a string that is composed +# by zero or multiple characters. The empty string means that notifications +# are disabled at all. +# +# Example: to enable list and generic events, from the point of view of the +# event name, use: +# +# notify-keyspace-events Elg +# +# Example 2: to get the stream of the expired keys subscribing to channel +# name __keyevent@0__:expired use: +# +# notify-keyspace-events Ex +# +# By default all notifications are disabled because most users don't need +# this feature and the feature has some overhead. Note that if you don't +# specify at least one of K or E, no events will be delivered. +notify-keyspace-events "" ############################### ADVANCED CONFIG ############################### diff --git a/src/bitops.c b/src/bitops.c index cda3adc7..f2d03f9b 100644 --- a/src/bitops.c +++ b/src/bitops.c @@ -153,7 +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); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"setbit",c->argv[1],c->db->id); server.dirty++; addReply(c, bitval ? shared.cone : shared.czero); } @@ -347,11 +347,11 @@ void bitopCommand(redisClient *c) { if (maxlen) { o = createObject(REDIS_STRING,res); setKey(c->db,targetkey,o); - notifyKeyspaceEvent("set",targetkey,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",targetkey,c->db->id); decrRefCount(o); } else if (dbDelete(c->db,targetkey)) { signalModifiedKey(c->db,targetkey); - notifyKeyspaceEvent("del",targetkey,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",targetkey,c->db->id); } server.dirty++; addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */ diff --git a/src/config.c b/src/config.c index bfe73482..4ab19c15 100644 --- a/src/config.c +++ b/src/config.c @@ -385,9 +385,13 @@ void loadServerConfigFromString(char *config) { } else if (!strcasecmp(argv[0],"slave-priority") && argc == 2) { server.slave_priority = atoi(argv[1]); } else if (!strcasecmp(argv[0],"notify-keyspace-events") && argc == 2) { - if ((server.notify_keyspace_events = yesnotoi(argv[1])) == -1) { - err = "argument must be 'yes' or 'no'"; goto loaderr; + int flags = keyspaceEventsStringToFlags(argv[1]); + + if (flags == -1) { + err = "Invalid event class character. Use 'g$lshzxeA'."; + goto loaderr; } + server.notify_keyspace_events = flags; } else if (!strcasecmp(argv[0],"sentinel")) { /* argc == 1 is handled by main() as we need to enter the sentinel * mode ASAP. */ @@ -707,10 +711,10 @@ void configSetCommand(redisClient *c) { if (yn == -1) goto badfmt; server.rdb_compression = yn; } else if (!strcasecmp(c->argv[2]->ptr,"notify-keyspace-events")) { - int yn = yesnotoi(o->ptr); + int flags = keyspaceEventsStringToFlags(o->ptr); - if (yn == -1) goto badfmt; - server.notify_keyspace_events = yn; + if (flags == -1) goto badfmt; + server.notify_keyspace_events = flags; } else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) { if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) goto badfmt; @@ -820,7 +824,6 @@ void configGetCommand(redisClient *c) { config_get_bool_field("rdbcompression", server.rdb_compression); config_get_bool_field("rdbchecksum", server.rdb_checksum); config_get_bool_field("activerehashing", server.activerehashing); - config_get_bool_field("notify-keyspace-events", server.notify_keyspace_events); /* Everything we can't handle with macros follows. */ @@ -935,6 +938,15 @@ void configGetCommand(redisClient *c) { addReplyBulkCString(c,buf); matches++; } + if (stringmatch(pattern,"notify-keyspace-events",0)) { + robj *flagsobj = createObject(REDIS_STRING, + keyspaceEventsFlagsToString(server.notify_keyspace_events)); + + addReplyBulkCString(c,"notify-keyspace-events"); + addReplyBulk(c,flagsobj); + decrRefCount(flagsobj); + matches++; + } setDeferredMultiBulkLength(c,replylen,matches*2); } diff --git a/src/db.c b/src/db.c index 4d838b05..f460f146 100644 --- a/src/db.c +++ b/src/db.c @@ -238,7 +238,8 @@ void delCommand(redisClient *c) { for (j = 1; j < c->argc; j++) { if (dbDelete(c->db,c->argv[j])) { signalModifiedKey(c->db,c->argv[j]); - notifyKeyspaceEvent("del",c->argv[j],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC, + "del",c->argv[j],c->db->id); server.dirty++; deleted++; } @@ -386,8 +387,10 @@ void renameGenericCommand(redisClient *c, int nx) { dbDelete(c->db,c->argv[1]); signalModifiedKey(c->db,c->argv[1]); signalModifiedKey(c->db,c->argv[2]); - notifyKeyspaceEvent("rename_from",c->argv[1],c->db->id); - notifyKeyspaceEvent("rename_to",c->argv[2],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"rename_from", + c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"rename_to", + c->argv[2],c->db->id); server.dirty++; addReply(c,nx ? shared.cone : shared.ok); } @@ -576,14 +579,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); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"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); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"expire",key,c->db->id); server.dirty++; return; } diff --git a/src/notify.c b/src/notify.c index da1d4d89..f8e01829 100644 --- a/src/notify.c +++ b/src/notify.c @@ -30,50 +30,97 @@ #include "redis.h" /* This file implements keyspace events notification via Pub/Sub ad - * described at http://redis.io/topics/keyspace-events. + * described at http://redis.io/topics/keyspace-events. */ + +/* Turn a string representing notification classes into an integer + * representing notification classes flags xored. * - * The API provided to the rest of the Redis core is a simple function: + * The function returns -1 if the input contains characters not mapping to + * any class. */ +int keyspaceEventsStringToFlags(char *classes) { + char *p = classes; + int c, flags = 0; + + while((c = *p++) != '\0') { + switch(c) { + case 'A': flags |= REDIS_NOTIFY_ALL; break; + case 'g': flags |= REDIS_NOTIFY_GENERIC; break; + case '$': flags |= REDIS_NOTIFY_STRING; break; + case 'l': flags |= REDIS_NOTIFY_LIST; break; + case 's': flags |= REDIS_NOTIFY_SET; break; + case 'h': flags |= REDIS_NOTIFY_HASH; break; + case 'z': flags |= REDIS_NOTIFY_ZSET; break; + case 'x': flags |= REDIS_NOTIFY_EXPIRED; break; + case 'e': flags |= REDIS_NOTIFY_EVICTED; break; + case 'K': flags |= REDIS_NOTIFY_KEYSPACE; break; + case 'E': flags |= REDIS_NOTIFY_KEYEVENT; break; + default: return -1; + } + } + return flags; +} + +/* This function does exactly the revese of the function above: it gets + * as input an integer with the xored flags and returns a string representing + * the selected classes. The string returned is an sds string that needs to + * be released with sdsfree(). */ +sds keyspaceEventsFlagsToString(int flags) { + sds res; + + if ((flags & REDIS_NOTIFY_ALL) == REDIS_NOTIFY_ALL) + return sdsnew("A"); + res = sdsempty(); + if (flags & REDIS_NOTIFY_GENERIC) res = sdscatlen(res,"g",1); + if (flags & REDIS_NOTIFY_STRING) res = sdscatlen(res,"$",1); + if (flags & REDIS_NOTIFY_LIST) res = sdscatlen(res,"l",1); + if (flags & REDIS_NOTIFY_SET) res = sdscatlen(res,"s",1); + if (flags & REDIS_NOTIFY_HASH) res = sdscatlen(res,"h",1); + if (flags & REDIS_NOTIFY_ZSET) res = sdscatlen(res,"z",1); + if (flags & REDIS_NOTIFY_EXPIRED) res = sdscatlen(res,"x",1); + if (flags & REDIS_NOTIFY_EVICTED) res = sdscatlen(res,"e",1); + if (flags & REDIS_NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1); + if (flags & REDIS_NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1); + return res; +} + +/* The API provided to the rest of the Redis core is a simple function: * * notifyKeyspaceEvent(char *event, robj *key, int dbid); * * 'event' is a C string representing the event name. * 'key' is a Redis object representing the key name. - * 'dbid' is the database ID where the key lives. - */ - -void notifyKeyspaceEvent(char *event, robj *key, int dbid) { - sds keyspace_chan, keyevent_chan; - int len; + * 'dbid' is the database ID where the key lives. */ +void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) { + sds chan; + robj *chanobj; + int len = -1; char buf[24]; - robj *chan1, *chan2, *eventobj; - if (!server.notify_keyspace_events) return; + /* If notifications for this class of events are off, return ASAP. */ + if (!(server.notify_keyspace_events & type)) return; - /* The prefix of the two channels is identical if not for - * 'keyspace' that is 'keyevent' in the event channel name, so - * we build a single prefix and overwrite 'event' with 'space'. */ - keyspace_chan = sdsnewlen("__keyspace@",11); - len = ll2string(buf,sizeof(buf),dbid); - keyspace_chan = sdscatlen(keyspace_chan, buf, len); - keyspace_chan = sdscatlen(keyspace_chan, "__:", 3); - keyevent_chan = sdsdup(keyspace_chan); /* Dup the prefix. */ - memcpy(keyevent_chan+5,"event",5); /* Fix it. */ + /* __keyspace@__: notifications. */ + if (server.notify_keyspace_events & REDIS_NOTIFY_KEYSPACE) { + robj *eventobj; - eventobj = createStringObject(event,strlen(event)); + chan = sdsnewlen("__keyspace@",11); + len = ll2string(buf,sizeof(buf),dbid); + chan = sdscatlen(chan, buf, len); + chan = sdscatlen(chan, "__:", 3); + eventobj = createStringObject(event,strlen(event)); + chanobj = createObject(REDIS_STRING, chan); + pubsubPublishMessage(chanobj, eventobj); + decrRefCount(chanobj); + } - /* The keyspace channel name has a trailing key name, while - * the keyevent channel name has a trailing event name. */ - keyspace_chan = sdscatsds(keyspace_chan, key->ptr); - keyevent_chan = sdscatsds(keyevent_chan, eventobj->ptr); - chan1 = createObject(REDIS_STRING, keyspace_chan); - chan2 = createObject(REDIS_STRING, keyevent_chan); - - /* Finally publish the two notifications. */ - pubsubPublishMessage(chan1, eventobj); - pubsubPublishMessage(chan2, key); - - /* Release objects. */ - decrRefCount(eventobj); - decrRefCount(chan1); - decrRefCount(chan2); + /* __keyevente@__: notifications. */ + if (server.notify_keyspace_events & REDIS_NOTIFY_KEYEVENT) { + chan = sdsnewlen("__keyevent@",11); + if (len == -1) len = ll2string(buf,sizeof(buf),dbid); + chan = sdscatlen(chan, buf, len); + chan = sdscatlen(chan, "__:", 3); + chanobj = createObject(REDIS_STRING, chan); + pubsubPublishMessage(chanobj, key); + decrRefCount(chanobj); + } } diff --git a/src/redis.c b/src/redis.c index 7bbb65fc..6786eedd 100644 --- a/src/redis.c +++ b/src/redis.c @@ -666,7 +666,8 @@ void activeExpireCycle(void) { propagateExpire(db,keyobj); dbDelete(db,keyobj); - notifyKeyspaceEvent("expired",keyobj,db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED, + "expired",keyobj,db->id); decrRefCount(keyobj); expired++; server.stat_expiredkeys++; @@ -2360,7 +2361,8 @@ int freeMemoryIfNeeded(void) { delta -= (long long) zmalloc_used_memory(); mem_freed += delta; server.stat_evictedkeys++; - notifyKeyspaceEvent("evicted",keyobj,db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_EVICTED, "evicted", + keyobj, db->id); decrRefCount(keyobj); keys_freed++; diff --git a/src/redis.h b/src/redis.h index f645fddc..635232f9 100644 --- a/src/redis.h +++ b/src/redis.h @@ -291,6 +291,20 @@ #define REDIS_PROPAGATE_AOF 1 #define REDIS_PROPAGATE_REPL 2 +/* Keyspace changes notification classes. Every class is associated with a + * character for configuration purposes. */ +#define REDIS_NOTIFY_KEYSPACE (1<<0) /* K */ +#define REDIS_NOTIFY_KEYEVENT (1<<1) /* E */ +#define REDIS_NOTIFY_GENERIC (1<<2) /* g */ +#define REDIS_NOTIFY_STRING (1<<3) /* $ */ +#define REDIS_NOTIFY_LIST (1<<4) /* l */ +#define REDIS_NOTIFY_SET (1<<5) /* s */ +#define REDIS_NOTIFY_HASH (1<<6) /* h */ +#define REDIS_NOTIFY_ZSET (1<<7) /* z */ +#define REDIS_NOTIFY_EXPIRED (1<<8) /* x */ +#define REDIS_NOTIFY_EVICTED (1<<9) /* e */ +#define REDIS_NOTIFY_ALL (REDIS_NOTIFY_GENERIC | REDIS_NOTIFY_STRING | REDIS_NOTIFY_LIST | REDIS_NOTIFY_SET | REDIS_NOTIFY_HASH | REDIS_NOTIFY_ZSET | REDIS_NOTIFY_EXPIRED | REDIS_NOTIFY_EVICTED) /* A */ + /* Using the following macro you can run code inside serverCron() with the * specified period, specified in milliseconds. * The actual resolution depends on server.hz. */ @@ -646,7 +660,8 @@ struct redisServer { /* Pubsub */ dict *pubsub_channels; /* Map channels to list of subscribed clients */ list *pubsub_patterns; /* A list of pubsub_patterns */ - int notify_keyspace_events; /* Propagate keyspace events via Pub/Sub. */ + int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an + xor of REDIS_NOTIFY... flags. */ /* Scripting */ lua_State *lua; /* The Lua interpreter. We use just one for all clients */ redisClient *lua_client; /* The "fake client" to query Redis from Lua */ @@ -1001,7 +1016,11 @@ int pubsubUnsubscribeAllPatterns(redisClient *c, int notify); void freePubsubPattern(void *p); int listMatchPubsubPattern(void *a, void *b); int pubsubPublishMessage(robj *channel, robj *message); -void notifyKeyspaceEvent(char *event, robj *key, int dbid); + +/* Keyspace events notification */ +void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid); +int keyspaceEventsStringToFlags(char *classes); +sds keyspaceEventsFlagsToString(int flags); /* Configuration */ void loadServerConfig(char *filename, char *options); diff --git a/src/sort.c b/src/sort.c index a0971473..4b504025 100644 --- a/src/sort.c +++ b/src/sort.c @@ -504,11 +504,12 @@ void sortCommand(redisClient *c) { } if (outputlen) { setKey(c->db,storekey,sobj); - notifyKeyspaceEvent("sortstore",storekey,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"sortstore",storekey, + c->db->id); server.dirty += outputlen; } else if (dbDelete(c->db,storekey)) { signalModifiedKey(c->db,storekey); - notifyKeyspaceEvent("del",storekey,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",storekey,c->db->id); server.dirty++; } decrRefCount(sobj); diff --git a/src/t_hash.c b/src/t_hash.c index e395ee26..959c9ca8 100644 --- a/src/t_hash.c +++ b/src/t_hash.c @@ -474,7 +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); + notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id); server.dirty++; } @@ -490,7 +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); + notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id); server.dirty++; } } @@ -512,7 +512,7 @@ void hmsetCommand(redisClient *c) { } addReply(c, shared.ok); signalModifiedKey(c->db,c->argv[1]); - notifyKeyspaceEvent("hset",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id); server.dirty++; } @@ -546,7 +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); + notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hincrby",c->argv[1],c->db->id); server.dirty++; } @@ -573,7 +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); + notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id); server.dirty++; /* Always replicate HINCRBYFLOAT as an HSET command with the final value @@ -671,8 +671,10 @@ void hdelCommand(redisClient *c) { } 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); + notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hdel",c->argv[1],c->db->id); + if (keyremoved) + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"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 ea5f4718..0413dc69 100644 --- a/src/t_list.c +++ b/src/t_list.c @@ -320,7 +320,7 @@ void pushGenericCommand(redisClient *c, int where) { char *event = (where == REDIS_HEAD) ? "lpush" : "rpush"; signalModifiedKey(c->db,c->argv[1]); - notifyKeyspaceEvent(event,c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id); } server.dirty += pushed; } @@ -367,7 +367,8 @@ 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); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"linsert", + c->argv[1],c->db->id); server.dirty++; } else { /* Notify client of a failed insert */ @@ -379,7 +380,7 @@ void pushxGenericCommand(redisClient *c, robj *refval, robj *val, int where) { listTypePush(subject,val,where); signalModifiedKey(c->db,c->argv[1]); - notifyKeyspaceEvent(event,c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id); server.dirty++; } @@ -474,7 +475,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); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"lset",c->argv[1],c->db->id); server.dirty++; } } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { @@ -487,7 +488,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); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"lset",c->argv[1],c->db->id); server.dirty++; } } else { @@ -507,9 +508,10 @@ void popGenericCommand(redisClient *c, int where) { addReplyBulk(c,value); decrRefCount(value); - notifyKeyspaceEvent(event,c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id); if (listTypeLength(o) == 0) { - notifyKeyspaceEvent("del",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del", + c->argv[1],c->db->id); dbDelete(c->db,c->argv[1]); } signalModifiedKey(c->db,c->argv[1]); @@ -632,10 +634,10 @@ void ltrimCommand(redisClient *c) { redisPanic("Unknown list encoding"); } - notifyKeyspaceEvent("ltrim",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"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); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id); } signalModifiedKey(c->db,c->argv[1]); server.dirty++; @@ -711,7 +713,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); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"lpush",dstkey,c->db->id); /* Always send the pushed value to the client. */ addReplyBulk(c,value); } @@ -741,10 +743,11 @@ void rpoplpushCommand(redisClient *c) { decrRefCount(value); /* Delete the source list when it is empty */ - notifyKeyspaceEvent("rpop",touchedkey,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"rpop",touchedkey,c->db->id); if (listTypeLength(sobj) == 0) { dbDelete(c->db,touchedkey); - notifyKeyspaceEvent("del",touchedkey,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del", + touchedkey,c->db->id); } signalModifiedKey(c->db,touchedkey); decrRefCount(touchedkey); @@ -1077,10 +1080,12 @@ void blockingPopGenericCommand(redisClient *c, int where) { addReplyBulk(c,c->argv[j]); addReplyBulk(c,value); decrRefCount(value); - notifyKeyspaceEvent(event,c->argv[j],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_LIST,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); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"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 70052a55..a22bbb67 100644 --- a/src/t_set.c +++ b/src/t_set.c @@ -268,7 +268,7 @@ void saddCommand(redisClient *c) { } if (added) { signalModifiedKey(c->db,c->argv[1]); - notifyKeyspaceEvent("sadd",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[1],c->db->id); } server.dirty += added; addReplyLongLong(c,added); @@ -293,8 +293,10 @@ void sremCommand(redisClient *c) { } 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); + notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[1],c->db->id); + if (keyremoved) + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1], + c->db->id); server.dirty += deleted; } addReplyLongLong(c,deleted); @@ -328,12 +330,12 @@ void smoveCommand(redisClient *c) { addReply(c,shared.czero); return; } - notifyKeyspaceEvent("srem",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_SET,"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]); - notifyKeyspaceEvent("del",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id); } signalModifiedKey(c->db,c->argv[1]); signalModifiedKey(c->db,c->argv[2]); @@ -348,7 +350,7 @@ void smoveCommand(redisClient *c) { /* An extra key has changed when ele was successfully added to dstset */ if (setTypeAdd(dstset,ele)) { server.dirty++; - notifyKeyspaceEvent("sadd",c->argv[2],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[2],c->db->id); } addReply(c,shared.cone); } @@ -391,7 +393,7 @@ void spopCommand(redisClient *c) { incrRefCount(ele); setTypeRemove(set,ele); } - notifyKeyspaceEvent("spop",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_SET,"spop",c->argv[1],c->db->id); /* Replicate/AOF this command as an SREM operation */ aux = createStringObject("SREM",4); @@ -402,7 +404,7 @@ void spopCommand(redisClient *c) { addReplyBulk(c,ele); if (setTypeSize(set) == 0) { dbDelete(c->db,c->argv[1]); - notifyKeyspaceEvent("del",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id); } signalModifiedKey(c->db,c->argv[1]); server.dirty++; @@ -708,7 +710,8 @@ 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); + notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sinterstore", + dstkey,c->db->id); } else { decrRefCount(dstset); addReply(c,shared.czero); @@ -874,7 +877,7 @@ void sunionDiffGenericCommand(redisClient *c, robj **setkeys, int setnum, robj * if (setTypeSize(dstset) > 0) { dbAdd(c->db,dstkey,dstset); addReplyLongLong(c,setTypeSize(dstset)); - notifyKeyspaceEvent( + notifyKeyspaceEvent(REDIS_NOTIFY_SET, op == REDIS_OP_UNION ? "sunionstore" : "sdiffstore", dstkey,c->db->id); } else { diff --git a/src/t_string.c b/src/t_string.c index ed784046..1d25e5ad 100644 --- a/src/t_string.c +++ b/src/t_string.c @@ -62,8 +62,9 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir setKey(c->db,key,val); server.dirty++; if (expire) setExpire(c->db,key,mstime()+milliseconds); - notifyKeyspaceEvent("set",key,c->db->id); - if (expire) notifyKeyspaceEvent("expire",key,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",key,c->db->id); + if (expire) notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC, + "expire",key,c->db->id); addReply(c, nx ? shared.cone : shared.ok); } @@ -110,7 +111,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); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",c->argv[1],c->db->id); server.dirty++; } @@ -172,7 +173,8 @@ 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); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING, + "setrange",c->argv[1],c->db->id); server.dirty++; } addReplyLongLong(c,sdslen(o->ptr)); @@ -257,7 +259,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],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",c->argv[j],c->db->id); } server.dirty += (c->argc-1)/2; addReply(c, nx ? shared.cone : shared.ok); @@ -292,7 +294,7 @@ void incrDecrCommand(redisClient *c, long long incr) { else dbAdd(c->db,c->argv[1],new); signalModifiedKey(c->db,c->argv[1]); - notifyKeyspaceEvent("incrby",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"incrby",c->argv[1],c->db->id); server.dirty++; addReply(c,shared.colon); addReply(c,new); @@ -342,7 +344,7 @@ void incrbyfloatCommand(redisClient *c) { else dbAdd(c->db,c->argv[1],new); signalModifiedKey(c->db,c->argv[1]); - notifyKeyspaceEvent("incrbyfloat",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"incrbyfloat",c->argv[1],c->db->id); server.dirty++; addReplyBulk(c,new); @@ -390,7 +392,7 @@ void appendCommand(redisClient *c) { totlen = sdslen(o->ptr); } signalModifiedKey(c->db,c->argv[1]); - notifyKeyspaceEvent("append",c->argv[1],c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"append",c->argv[1],c->db->id); server.dirty++; addReplyLongLong(c,totlen); } diff --git a/src/t_zset.c b/src/t_zset.c index 4a9383c1..b458f88a 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -968,7 +968,8 @@ cleanup: zfree(scores); if (added || updated) { signalModifiedKey(c->db,key); - notifyKeyspaceEvent(incr ? "zincr" : "zadd", key, c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_ZSET, + incr ? "zincr" : "zadd", key, c->db->id); } } @@ -1029,8 +1030,9 @@ void zremCommand(redisClient *c) { } if (deleted) { - notifyKeyspaceEvent("zrem",key,c->db->id); - if (keyremoved) notifyKeyspaceEvent("del",key,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,"zrem",key,c->db->id); + if (keyremoved) + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id); signalModifiedKey(c->db,key); server.dirty += deleted; } @@ -1073,8 +1075,9 @@ void zremrangebyscoreCommand(redisClient *c) { if (deleted) { signalModifiedKey(c->db,key); - notifyKeyspaceEvent("zrembyscore",key,c->db->id); - if (keyremoved) notifyKeyspaceEvent("del",key,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,"zrembyscore",key,c->db->id); + if (keyremoved) + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id); } server.dirty += deleted; addReplyLongLong(c,deleted); @@ -1132,8 +1135,9 @@ void zremrangebyrankCommand(redisClient *c) { if (deleted) { signalModifiedKey(c->db,key); - notifyKeyspaceEvent("zrembyrank",key,c->db->id); - if (keyremoved) notifyKeyspaceEvent("del",key,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,"zrembyrank",key,c->db->id); + if (keyremoved) + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",key,c->db->id); } server.dirty += deleted; addReplyLongLong(c,deleted); @@ -1676,7 +1680,7 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) { if (dbDelete(c->db,dstkey)) { signalModifiedKey(c->db,dstkey); - notifyKeyspaceEvent("del",dstkey,c->db->id); + notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",dstkey,c->db->id); touched = 1; server.dirty++; } @@ -1689,7 +1693,7 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) { dbAdd(c->db,dstkey,dstobj); addReplyLongLong(c,zsetLength(dstobj)); if (!touched) signalModifiedKey(c->db,dstkey); - notifyKeyspaceEvent( + notifyKeyspaceEvent(REDIS_NOTIFY_ZSET, (op == REDIS_OP_UNION) ? "zunionstore" : "zinterstore", dstkey,c->db->id); server.dirty++; diff --git a/tests/assets/default.conf b/tests/assets/default.conf index f264bed3..8d68e74f 100644 --- a/tests/assets/default.conf +++ b/tests/assets/default.conf @@ -13,7 +13,7 @@ # units are case insensitive so 1GB 1Gb 1gB are all the same. # Enable keyspace events notification for testing so that we cover more code. -notify-keyspace-events yes +notify-keyspace-events A # By default Redis does not run as a daemon. Use 'yes' if you need it. # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.