summaryrefslogtreecommitdiff
path: root/src/multi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/multi.c')
-rw-r--r--src/multi.c44
1 files changed, 32 insertions, 12 deletions
diff --git a/src/multi.c b/src/multi.c
index 5249c58bf..d3a91e866 100644
--- a/src/multi.c
+++ b/src/multi.c
@@ -264,17 +264,38 @@ void execCommand(client *c) {
* Also every client contains a list of WATCHed keys so that's possible to
* un-watch such keys when the client is freed or when UNWATCH is called. */
-/* In the client->watched_keys list we need to use watchedKey structures
- * as in order to identify a key in Redis we need both the key name and the
- * DB. This struct is also referenced from db->watched_keys dict, where the
- * values are lists of watchedKey pointers. */
+/* The watchedKey struct is included in two lists: the client->watched_keys list,
+ * and db->watched_keys dict (each value in that dict is a list of watchedKey structs).
+ * The list in the client struct is a plain list, where each node's value is a pointer to a watchedKey.
+ * The list in the db db->watched_keys is different, the listnode member that's embedded in this struct
+ * is the node in the dict. And the value inside that listnode is a pointer to the that list, and we can use
+ * struct member offset math to get from the listnode to the watchedKey struct.
+ * This is done to avoid the need for listSearchKey and dictFind when we remove from the list. */
typedef struct watchedKey {
+ listNode node;
robj *key;
redisDb *db;
client *client;
unsigned expired:1; /* Flag that we're watching an already expired key. */
} watchedKey;
+/* Attach a watchedKey to the list of clients watching that key. */
+static inline void watchedKeyLinkToClients(list *clients, watchedKey *wk) {
+ wk->node.value = clients; /* Point the value back to the list */
+ listLinkNodeTail(clients, &wk->node); /* Link the embedded node */
+}
+
+/* Get the list of clients watching that key. */
+static inline list *watchedKeyGetClients(watchedKey *wk) {
+ return listNodeValue(&wk->node); /* embedded node->value points back to the list */
+}
+
+/* Get the node with wk->client in the list of clients watching that key. Actually it
+ * is just the embedded node. */
+static inline listNode *watchedKeyGetClientNode(watchedKey *wk) {
+ return &wk->node;
+}
+
/* Watch for the specified key */
void watchForKey(client *c, robj *key) {
list *clients = NULL;
@@ -303,8 +324,8 @@ void watchForKey(client *c, robj *key) {
wk->db = c->db;
wk->expired = keyIsExpired(c->db, key);
incrRefCount(key);
- listAddNodeTail(c->watched_keys,wk);
- listAddNodeTail(clients,wk);
+ listAddNodeTail(c->watched_keys, wk);
+ watchedKeyLinkToClients(clients, wk);
}
/* Unwatch all the keys watched by this client. To clean the EXEC dirty
@@ -319,12 +340,11 @@ void unwatchAllKeys(client *c) {
list *clients;
watchedKey *wk;
- /* Lookup the watched key -> clients list and remove the client's wk
- * from the list */
+ /* Remove the client's wk from the list of clients watching the key. */
wk = listNodeValue(ln);
- clients = dictFetchValue(wk->db->watched_keys, wk->key);
+ clients = watchedKeyGetClients(wk);
serverAssertWithInfo(c,NULL,clients != NULL);
- listDelNode(clients,listSearchKey(clients,wk));
+ listUnlinkNode(clients, watchedKeyGetClientNode(wk));
/* Kill the entry at all if this was the only client */
if (listLength(clients) == 0)
dictDelete(wk->db->watched_keys, wk->key);
@@ -367,7 +387,7 @@ void touchWatchedKey(redisDb *db, robj *key) {
/* Check if we are already watching for this key */
listRewind(clients,&li);
while((ln = listNext(&li))) {
- watchedKey *wk = listNodeValue(ln);
+ watchedKey *wk = member2struct(watchedKey, node, ln);
client *c = wk->client;
if (wk->expired) {
@@ -420,7 +440,7 @@ void touchAllWatchedKeysInDb(redisDb *emptied, redisDb *replaced_with) {
if (!clients) continue;
listRewind(clients,&li);
while((ln = listNext(&li))) {
- watchedKey *wk = listNodeValue(ln);
+ watchedKey *wk = member2struct(watchedKey, node, ln);
if (wk->expired) {
if (!replaced_with || !dictFind(replaced_with->dict, key->ptr)) {
/* Expired key now deleted. No logical change. Clear the