Skip to content

Commit e6a9a6b

Browse files
committed
Fix for issue Snapchat#187 we need to properly handle the case where a key with a subkey expirey itself expires during load
1 parent d6be935 commit e6a9a6b

File tree

4 files changed

+41
-8
lines changed

4 files changed

+41
-8
lines changed

src/rdb.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2163,6 +2163,7 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
21632163
uint64_t mvcc_tstamp = OBJ_MVCC_INVALID;
21642164
robj *subexpireKey = nullptr;
21652165
sds key = nullptr;
2166+
bool fLastKeyExpired = false;
21662167

21672168
rdb->update_cksum = rdbLoadProgressCallback;
21682169
rdb->max_processing_chunk = g_pserver->loading_process_events_interval_bytes;
@@ -2295,11 +2296,22 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
22952296
static_assert(sizeof(unsigned long long) == sizeof(uint64_t), "Ensure long long is 64-bits");
22962297
mvcc_tstamp = strtoull(szFromObj(auxval), nullptr, 10);
22972298
} else if (!strcasecmp(szFromObj(auxkey), "keydb-subexpire-key")) {
2299+
if (subexpireKey != nullptr) {
2300+
serverLog(LL_WARNING, "Corrupt subexpire entry in RDB skipping. key: %s subkey: %s", key != nullptr ? key : "(null)", subexpireKey != nullptr ? szFromObj(subexpireKey) : "(null)");
2301+
decrRefCount(subexpireKey);
2302+
subexpireKey = nullptr;
2303+
}
22982304
subexpireKey = auxval;
22992305
incrRefCount(subexpireKey);
23002306
} else if (!strcasecmp(szFromObj(auxkey), "keydb-subexpire-when")) {
23012307
if (key == nullptr || subexpireKey == nullptr) {
2302-
serverLog(LL_WARNING, "Corrupt subexpire entry in RDB skipping. key: %s subkey: %s", key != nullptr ? key : "(null)", subexpireKey != nullptr ? szFromObj(subexpireKey) : "(null)");
2308+
if (!fLastKeyExpired) { // This is not an error if we just expired the key associated with this subexpire
2309+
serverLog(LL_WARNING, "Corrupt subexpire entry in RDB skipping. key: %s subkey: %s", key != nullptr ? key : "(null)", subexpireKey != nullptr ? szFromObj(subexpireKey) : "(null)");
2310+
}
2311+
if (subexpireKey) {
2312+
decrRefCount(subexpireKey);
2313+
subexpireKey = nullptr;
2314+
}
23032315
}
23042316
else {
23052317
redisObject keyobj;
@@ -2404,13 +2416,17 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
24042416
rsi->mi->staleKeyMap->operator[](db - g_pserver->db).push_back(objKeyDup);
24052417
decrRefCount(objKeyDup);
24062418
}
2419+
serverLog(LL_WARNING, "Loaded expired key");
2420+
fLastKeyExpired = true;
24072421
sdsfree(key);
24082422
key = nullptr;
24092423
decrRefCount(val);
24102424
val = nullptr;
24112425
} else {
24122426
/* Add the new object in the hash table */
24132427
int fInserted = dbMerge(db, &keyobj, val, (rsi && rsi->fForceSetKey) || (rdbflags & RDBFLAGS_ALLOW_DUP)); // Note: dbMerge will incrRef
2428+
serverLog(LL_WARNING, "Loaded: %s", key);
2429+
fLastKeyExpired = false;
24142430

24152431
if (fInserted)
24162432
{

tests/integration/replication.tcl

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,3 @@
1-
proc log_file_matches {log pattern} {
2-
set fp [open $log r]
3-
set content [read $fp]
4-
close $fp
5-
string match $pattern $content
6-
}
7-
81
start_server {tags {"repl"} overrides {hz 100}} {
92
set slave [srv 0 client]
103
set slave_host [srv 0 host]

tests/support/util.tcl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
proc log_file_matches {log pattern} {
2+
set fp [open $log r]
3+
set content [read $fp]
4+
close $fp
5+
string match $pattern $content
6+
}
7+
18
proc randstring {min max {type binary}} {
29
set len [expr {$min+int(rand()*($max-$min+1))}]
310
set output {}

tests/unit/expire.tcl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,23 @@ start_server {tags {"expire"}} {
272272
r expiremember testkey foo 10000
273273
r save
274274
r debug reload
275+
if {[log_file_matches [srv 0 stdout] "*Corrupt subexpire*"]} {
276+
fail "Server reported corrupt subexpire"
277+
}
275278
assert [expr [r ttl testkey foo] > 0]
276279
}
280+
281+
test {Load subkey for an expired key works} {
282+
# Note test inherits keys from previous tests, we want more traffic in the RDB
283+
r multi
284+
r sadd testset val1
285+
r expiremember testset val1 300
286+
r pexpire testset 1
287+
r debug reload
288+
r exec
289+
set logerr [log_file_matches [srv 0 stdout] "*Corrupt subexpire*"]
290+
if {$logerr} {
291+
fail "Server reported corrupt subexpire"
292+
}
293+
}
277294
}

0 commit comments

Comments
 (0)