From 80f9b812cac2efe974247b4268d46c05116c6a6e Mon Sep 17 00:00:00 2001 From: John Sully Date: Tue, 19 Nov 2019 15:47:06 -0500 Subject: [PATCH] Ensure iterating the keyspace still works during background save Former-commit-id: 23d7a8a293f253262801bc6ce88ad85e4e38f59a --- src/db.cpp | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/server.h | 2 ++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/db.cpp b/src/db.cpp index 125b23852..107ad2044 100644 --- a/src/db.cpp +++ b/src/db.cpp @@ -646,6 +646,61 @@ bool redisDbPersistentData::iterate(std::function fn) } } dictReleaseIterator(di); + + if (m_pdbSnapshot != nullptr) + { + fResult = m_pdbSnapshot->iterate([&](const char *key){ + // Before passing off to the user we need to make sure it's not already in the + // the current set, and not deleted + dictEntry *deCurrent = dictFind(m_pdict, key); + if (deCurrent != nullptr) + return true; + dictEntry *deTombstone = dictFind(m_pdictTombstone, key); + if (deTombstone != nullptr) + return true; + + // Alright it's a key in the use keyspace, lets ensure it and then pass it off + ensure(key); + deCurrent = dictFind(m_pdict, key); + return fn(key, (robj*)dictGetVal(deCurrent)); + }); + } + + return fResult; +} + +bool redisDbPersistentData::iterate(std::function fn) +{ + dictIterator *di = dictGetSafeIterator(m_pdict); + dictEntry *de = nullptr; + bool fResult = true; + while((de = dictNext(di)) != nullptr) + { + if (!fn((const char*)dictGetKey(de))) + { + fResult = false; + break; + } + } + dictReleaseIterator(di); + + if (m_pdbSnapshot != nullptr) + { + fResult = m_pdbSnapshot->iterate([&](const char *key){ + // Before passing off to the user we need to make sure it's not already in the + // the current set, and not deleted + dictEntry *deCurrent = dictFind(m_pdict, key); + if (deCurrent != nullptr) + return true; + dictEntry *deTombstone = dictFind(m_pdictTombstone, key); + if (deTombstone != nullptr) + return true; + + // Alright it's a key in the use keyspace + return fn(key); + }); + } + return fResult; } @@ -656,7 +711,7 @@ void keysCommand(client *c) { void *replylen = addReplyDeferredLen(c); allkeys = (pattern[0] == '*' && pattern[1] == '\0'); - c->db->iterate([&](const char *key, robj *)->bool { + c->db->iterate([&](const char *key)->bool { robj *keyobj; if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) { diff --git a/src/server.h b/src/server.h index d49098b51..26b5b9f4f 100644 --- a/src/server.h +++ b/src/server.h @@ -1193,7 +1193,9 @@ public: int removeSubkeyExpire(robj *key, robj *subkey); void clear(void(callback)(void*)); void emptyDbAsync(); + // Note: If you do not need the obj then use the objless iterator version. It's faster bool iterate(std::function fn); + bool iterate(std::function fn); void setExpire(robj *key, robj *subkey, long long when); void setExpire(expireEntry &&e); expireEntry *getExpire(robj_roptr key);