Unverified Commit bb7891f0 authored by Luke Palmer's avatar Luke Palmer Committed by GitHub
Browse files

Keyspace event for new keys (#10512)

Add an optional keyspace event when new keys are added to the db.

This is useful for applications where clients need to be aware of the redis keyspace.
Such an application can SCAN once at startup and then listen for "new" events (plus
others associated with DEL, RENAME, etc).
parent e875ff89
...@@ -1827,6 +1827,7 @@ latency-monitor-threshold 0 ...@@ -1827,6 +1827,7 @@ latency-monitor-threshold 0
# z Sorted set commands # z Sorted set commands
# x Expired events (events generated every time a key expires) # x Expired events (events generated every time a key expires)
# e Evicted events (events generated when a key is evicted for maxmemory) # e Evicted events (events generated when a key is evicted for maxmemory)
# n New key events (Note: not included in the 'A' class)
# t Stream commands # t Stream commands
# d Module key type events # d Module key type events
# m Key-miss events (Note: It is not included in the 'A' class) # m Key-miss events (Note: It is not included in the 'A' class)
......
...@@ -2691,7 +2691,7 @@ static int setConfigNotifyKeyspaceEventsOption(standardConfig *config, sds *argv ...@@ -2691,7 +2691,7 @@ static int setConfigNotifyKeyspaceEventsOption(standardConfig *config, sds *argv
} }
int flags = keyspaceEventsStringToFlags(argv[0]); int flags = keyspaceEventsStringToFlags(argv[0]);
if (flags == -1) { if (flags == -1) {
*err = "Invalid event class character. Use 'Ag$lshzxeKEtmd'."; *err = "Invalid event class character. Use 'Ag$lshzxeKEtmdn'.";
return 0; return 0;
} }
server.notify_keyspace_events = flags; server.notify_keyspace_events = flags;
......
...@@ -182,6 +182,7 @@ void dbAdd(redisDb *db, robj *key, robj *val) { ...@@ -182,6 +182,7 @@ void dbAdd(redisDb *db, robj *key, robj *val) {
dictSetVal(db->dict, de, val); dictSetVal(db->dict, de, val);
signalKeyAsReady(db, key, val->type); signalKeyAsReady(db, key, val->type);
if (server.cluster_enabled) slotToKeyAddEntry(de, db); if (server.cluster_enabled) slotToKeyAddEntry(de, db);
notifyKeyspaceEvent(NOTIFY_NEW,"new",key,db->id);
} }
/* This is a special version of dbAdd() that is used only when loading /* This is a special version of dbAdd() that is used only when loading
......
...@@ -57,6 +57,7 @@ int keyspaceEventsStringToFlags(char *classes) { ...@@ -57,6 +57,7 @@ int keyspaceEventsStringToFlags(char *classes) {
case 't': flags |= NOTIFY_STREAM; break; case 't': flags |= NOTIFY_STREAM; break;
case 'm': flags |= NOTIFY_KEY_MISS; break; case 'm': flags |= NOTIFY_KEY_MISS; break;
case 'd': flags |= NOTIFY_MODULE; break; case 'd': flags |= NOTIFY_MODULE; break;
case 'n': flags |= NOTIFY_NEW; break;
default: return -1; default: return -1;
} }
} }
...@@ -84,6 +85,7 @@ sds keyspaceEventsFlagsToString(int flags) { ...@@ -84,6 +85,7 @@ sds keyspaceEventsFlagsToString(int flags) {
if (flags & NOTIFY_EVICTED) res = sdscatlen(res,"e",1); if (flags & NOTIFY_EVICTED) res = sdscatlen(res,"e",1);
if (flags & NOTIFY_STREAM) res = sdscatlen(res,"t",1); if (flags & NOTIFY_STREAM) res = sdscatlen(res,"t",1);
if (flags & NOTIFY_MODULE) res = sdscatlen(res,"d",1); if (flags & NOTIFY_MODULE) res = sdscatlen(res,"d",1);
if (flags & NOTIFY_NEW) res = sdscatlen(res,"n",1);
} }
if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1); if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res,"K",1);
if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1); if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res,"E",1);
......
...@@ -603,6 +603,7 @@ typedef enum { ...@@ -603,6 +603,7 @@ typedef enum {
#define NOTIFY_KEY_MISS (1<<11) /* m (Note: This one is excluded from NOTIFY_ALL on purpose) */ #define NOTIFY_KEY_MISS (1<<11) /* m (Note: This one is excluded from NOTIFY_ALL on purpose) */
#define NOTIFY_LOADED (1<<12) /* module only key space notification, indicate a key loaded from rdb */ #define NOTIFY_LOADED (1<<12) /* module only key space notification, indicate a key loaded from rdb */
#define NOTIFY_MODULE (1<<13) /* d, module key space notification */ #define NOTIFY_MODULE (1<<13) /* d, module key space notification */
#define NOTIFY_NEW (1<<14) /* n, new key notification */
#define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM | NOTIFY_MODULE) /* A flag */ #define NOTIFY_ALL (NOTIFY_GENERIC | NOTIFY_STRING | NOTIFY_LIST | NOTIFY_SET | NOTIFY_HASH | NOTIFY_ZSET | NOTIFY_EXPIRED | NOTIFY_EVICTED | NOTIFY_STREAM | NOTIFY_MODULE) /* A flag */
/* Using the following macro you can run code inside serverCron() with the /* Using the following macro you can run code inside serverCron() with the
......
...@@ -390,4 +390,17 @@ start_server {tags {"pubsub network"}} { ...@@ -390,4 +390,17 @@ start_server {tags {"pubsub network"}} {
r config set notify-keyspace-events EA r config set notify-keyspace-events EA
assert_equal {AE} [lindex [r config get notify-keyspace-events] 1] assert_equal {AE} [lindex [r config get notify-keyspace-events] 1]
} }
test "Keyspace notifications: new key test" {
r config set notify-keyspace-events En
set rd1 [redis_deferring_client]
assert_equal {1} [psubscribe $rd1 *]
r set foo bar
# second set of foo should not cause a 'new' event
r set foo baz
r set bar bar
assert_equal "pmessage * __keyevent@${db}__:new foo" [$rd1 read]
assert_equal "pmessage * __keyevent@${db}__:new bar" [$rd1 read]
$rd1 close
}
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment