summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2018-06-14 18:08:05 +0200
committerantirez <antirez@gmail.com>2018-06-29 13:29:33 +0200
commitab145a9f15ffa8e899a406e6404b15198a80dbd0 (patch)
treeef3a4080bc90fb148fe5b82bfc9c6179e13da1ed
parent2fa43ece12e25ffe4ac19e6259686c146068c580 (diff)
downloadredis-ab145a9f15ffa8e899a406e6404b15198a80dbd0.tar.gz
Fix infinite loop in dbRandomKey().
Thanks to @kevinmcgehee for signaling the issue and reasoning about the consequences and potential fixes. Issue #5015.
-rw-r--r--src/db.c13
1 files changed, 13 insertions, 0 deletions
diff --git a/src/db.c b/src/db.c
index 21840d0c9..a8322d228 100644
--- a/src/db.c
+++ b/src/db.c
@@ -224,6 +224,8 @@ int dbExists(redisDb *db, robj *key) {
* The function makes sure to return keys not already expired. */
robj *dbRandomKey(redisDb *db) {
dictEntry *de;
+ int maxtries = 100;
+ int allvolatile = dictSize(db->dict) == dictSize(db->expires);
while(1) {
sds key;
@@ -235,6 +237,17 @@ robj *dbRandomKey(redisDb *db) {
key = dictGetKey(de);
keyobj = createStringObject(key,sdslen(key));
if (dictFind(db->expires,key)) {
+ if (allvolatile && server.masterhost && --maxtries == 0) {
+ /* If the DB is composed only of keys with an expire set,
+ * it could happen that all the keys are already logically
+ * expired in the slave, so the function cannot stop because
+ * expireIfNeeded() is false, nor it can stop because
+ * dictGetRandomKey() returns NULL (there are keys to return).
+ * To prevent the infinite loop we do some tries, but if there
+ * are the conditions for an infinite loop, eventually we
+ * return a key name that may be already expired. */
+ return keyobj;
+ }
if (expireIfNeeded(db,keyobj)) {
decrRefCount(keyobj);
continue; /* search for another key. This expired. */