Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: stats and keyspace notifications about lazy expiration #1547

Open
wants to merge 32 commits into
base: unstable
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9dced28
feat: add key expiration misses stat
proost Jan 12, 2025
6f58024
chore: rollback header file
proost Jan 12, 2025
35a5622
feat: add notify lazy expired
proost Jan 17, 2025
bc21780
Replace dict with new hashtable: sorted set datatype (#1427)
SoftlyRaining Jan 8, 2025
980ab2a
Skip logreqres on tests for the HELLO command (#1528)
rueian Jan 8, 2025
8d5f151
valkey-cli auto-exit from subscribed mode (#1432)
Nikhil-Manglore Jan 8, 2025
1dd2cba
Improve Typos configuration (#1456)
szepeviktor Jan 8, 2025
820d33a
Accelerate hash table iterator with prefetching (#1501)
NadavGigi Jan 8, 2025
9323ecf
Remove legacy SERVER_TEST compiler flag from cmake. (#1530)
karthyuom Jan 9, 2025
ad9601d
Fix new cli subscribed mode test in cluster mode (#1533)
enjoy-binbin Jan 9, 2025
00d97a3
Free the passed in lua context instead of the global (#1536)
madolson Jan 9, 2025
6a58098
Update upload artifacts to v4 (#1539)
hpatro Jan 10, 2025
426bfed
Fix crash when freeing newly created node when nodeIp2String fail (#1…
enjoy-binbin Jan 10, 2025
0ae56e7
Fix module LatencyAddSample still work when latency-monitor-threshold…
enjoy-binbin Jan 11, 2025
c84d1ed
Do election in order based on failed primary rank to avoid voting con…
enjoy-binbin Jan 11, 2025
88abb0d
Mark the node as FAIL when the node is marked as NOADDR and broadcast…
enjoy-binbin Jan 11, 2025
e8e6f68
Add latency stats around cluster config file operations (#1534)
enjoy-binbin Jan 11, 2025
af563bb
Skip CLI tests with reply schema validation (#1545)
zuiderkwast Jan 12, 2025
0c04b84
Test coverage for ECHO for reply schema validation (#1549)
zuiderkwast Jan 13, 2025
9b42d9c
Replace dict with new hashtable: hash datatype (#1502)
SoftlyRaining Jan 13, 2025
06506cd
Escape unix socket group in unit tests (#1554)
secwall Jan 14, 2025
c33a7b1
Fix valgrind test (#1555)
naglera Jan 14, 2025
cf8af09
Introduce const_sds for const-content sds (#1553)
zuiderkwast Jan 14, 2025
32c57b5
add paused_actions for INFO Clients (#1519)
soloestoy Jan 14, 2025
040dbe4
Adding Missing filters to CLIENT LIST and Dedup Parsing (#1401)
sarthakaggarwal97 Jan 15, 2025
f752df6
Allow clang-format to be triggered in push events (#1565)
enjoy-binbin Jan 16, 2025
50d6d24
Incr expired_keys if the unix-time is already expired for EXPIREAT an…
RayaCoo Jan 16, 2025
d4a7de5
Extract the scripting engine code from the functions unit (#1312)
rjd15372 Jan 16, 2025
fda1e1b
Fix cluster info sent stats for message with light header (#1563)
hpatro Jan 16, 2025
6d6a9f7
Fix memory leak in forgotten node ping ext code path (#1574)
pieturin Jan 16, 2025
e9a9894
Update comments and log message in cluster_legacy.c (#1561)
pieturin Jan 17, 2025
e05be86
Merge branch 'unstable' of https://github.com/valkey-io/valkey into f…
proost Jan 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ void updateLFU(robj *val) {
robj *lookupKey(serverDb *db, robj *key, int flags) {
int dict_index = getKVStoreIndexForKey(key->ptr);
robj *val = dbFindWithDictIndex(db, key->ptr, dict_index);
bool is_expired = false;
if (val) {
/* Forcing deletion of expired keys on a replica makes the replica
* inconsistent with the primary. We forbid it on readonly replicas, but
Expand All @@ -117,6 +118,7 @@ robj *lookupKey(serverDb *db, robj *key, int flags) {
if (expireIfNeededWithDictIndex(db, key, val, expire_flags, dict_index) != KEY_VALID) {
/* The key is no longer valid. */
val = NULL;
is_expired = true;
}
}

Expand All @@ -141,7 +143,12 @@ robj *lookupKey(serverDb *db, robj *key, int flags) {
/* TODO: Use separate hits stats for WRITE */
} else {
if (!(flags & (LOOKUP_NONOTIFY | LOOKUP_WRITE))) notifyKeyspaceEvent(NOTIFY_KEY_MISS, "keymiss", key, db->id);
if (!(flags & (LOOKUP_NOSTATS | LOOKUP_WRITE))) server.stat_keyspace_misses++;
if (!(flags & (LOOKUP_NOSTATS | LOOKUP_WRITE))) {
server.stat_keyspace_misses++;
if (is_expired) {
server.stat_keyspace_expiration_misses++;
}
}
/* TODO: Use separate misses stats and notify event for WRITE */
}

Expand Down Expand Up @@ -1822,13 +1829,16 @@ long long getExpire(serverDb *db, robj *key) {
return getExpireWithDictIndex(db, key, dict_index);
}

void deleteExpiredKeyAndPropagateWithDictIndex(serverDb *db, robj *keyobj, int dict_index) {
void deleteExpiredKeyAndPropagateWithDictIndex(serverDb *db, robj *keyobj, int dict_index, bool lazy) {
mstime_t expire_latency;
latencyStartMonitor(expire_latency);
dbGenericDeleteWithDictIndex(db, keyobj, server.lazyfree_lazy_expire, DB_FLAG_KEY_EXPIRED, dict_index);
latencyEndMonitor(expire_latency);
latencyAddSampleIfNeeded("expire-del", expire_latency);
notifyKeyspaceEvent(NOTIFY_EXPIRED, "expired", keyobj, db->id);
if (lazy) {
notifyKeyspaceEvent(NOTIFY_LAZY_EXPIRED, "lazyexpired", keyobj, db->id);
}
signalModifiedKey(NULL, db, keyobj);
propagateDeletion(db, keyobj, server.lazyfree_lazy_expire);
server.stat_expiredkeys++;
Expand All @@ -1837,7 +1847,7 @@ void deleteExpiredKeyAndPropagateWithDictIndex(serverDb *db, robj *keyobj, int d
/* Delete the specified expired key and propagate expire. */
void deleteExpiredKeyAndPropagate(serverDb *db, robj *keyobj) {
int dict_index = getKVStoreIndexForKey(keyobj->ptr);
deleteExpiredKeyAndPropagateWithDictIndex(db, keyobj, dict_index);
deleteExpiredKeyAndPropagateWithDictIndex(db, keyobj, dict_index, false);
}

/* Delete the specified expired key from overwriting and propagate the DEL or UNLINK. */
Expand Down Expand Up @@ -1998,7 +2008,7 @@ static keyStatus expireIfNeededWithDictIndex(serverDb *db, robj *key, robj *val,
key = createStringObject(key->ptr, sdslen(key->ptr));
}
/* Delete the key */
deleteExpiredKeyAndPropagateWithDictIndex(db, key, dict_index);
deleteExpiredKeyAndPropagateWithDictIndex(db, key, dict_index, true);
if (static_key) {
decrRefCount(key);
}
Expand Down
2 changes: 2 additions & 0 deletions src/notify.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ int keyspaceEventsStringToFlags(char *classes) {
case 'm': flags |= NOTIFY_KEY_MISS; break;
case 'd': flags |= NOTIFY_MODULE; break;
case 'n': flags |= NOTIFY_NEW; break;
case 'X': flags |= NOTIFY_LAZY_EXPIRED; break;
default: return -1;
}
}
Expand Down Expand Up @@ -91,6 +92,7 @@ sds keyspaceEventsFlagsToString(int flags) {
if (flags & NOTIFY_KEYSPACE) res = sdscatlen(res, "K", 1);
if (flags & NOTIFY_KEYEVENT) res = sdscatlen(res, "E", 1);
if (flags & NOTIFY_KEY_MISS) res = sdscatlen(res, "m", 1);
if (flags & NOTIFY_LAZY_EXPIRED) res = sdscatlen(res, "X", 1);
return res;
}

Expand Down
2 changes: 2 additions & 0 deletions src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -2649,6 +2649,7 @@ void resetServerStats(void) {
server.stat_total_eviction_exceeded_time = 0;
server.stat_last_eviction_exceeded_time = 0;
server.stat_keyspace_misses = 0;
server.stat_keyspace_expiration_misses = 0;
server.stat_keyspace_hits = 0;
server.stat_active_defrag_hits = 0;
server.stat_active_defrag_misses = 0;
Expand Down Expand Up @@ -5930,6 +5931,7 @@ sds genValkeyInfoString(dict *section_dict, int all_sections, int everything) {
"current_eviction_exceeded_time:%lld\r\n", current_eviction_exceeded_time / 1000,
"keyspace_hits:%lld\r\n", server.stat_keyspace_hits,
"keyspace_misses:%lld\r\n", server.stat_keyspace_misses,
"keyspace_expiration_misses:%lld\r\n", server.stat_keyspace_expiration_misses,
"pubsub_channels:%llu\r\n", kvstoreSize(server.pubsub_channels),
"pubsub_patterns:%lu\r\n", dictSize(server.pubsub_patterns),
"pubsubshard_channels:%llu\r\n", kvstoreSize(server.pubsubshard_channels),
Expand Down
2 changes: 2 additions & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,7 @@ typedef enum {
#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_NEW (1 << 14) /* n, new key notification */
#define NOTIFY_LAZY_EXPIRED (1 << 15) /* X, lazy expire 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 */
Expand Down Expand Up @@ -1670,6 +1671,7 @@ struct valkeyServer {
monotime stat_last_eviction_exceeded_time; /* Timestamp of current eviction start, unit us */
long long stat_keyspace_hits; /* Number of successful lookups of keys */
long long stat_keyspace_misses; /* Number of failed lookups of keys */
long long stat_keyspace_expiration_misses; /* Number of failed lookups of keys due to expiration */
long long stat_active_defrag_hits; /* number of allocations moved */
long long stat_active_defrag_misses; /* number of allocations scanned but not moved */
long long stat_active_defrag_key_hits; /* number of keys with moved allocations */
Expand Down
31 changes: 31 additions & 0 deletions tests/unit/info.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,37 @@ start_server {tags {"info" "external:skip" "debug_defrag:skip"}} {
$r2 close
wait_for_watched_clients_count 0
}

test {stats: keyspace misses} {
# disable active expire cycle
r debug set-active-expire 0

# clear stats before test
r config resetstat

# test keyspace misses
r set k1 "v1" PX 1

# before expiration, k1 is in db
set before [r info stats]

# wait for key to expire
after 100

# after expiration, k1 is not in db
r get k1
r get k2
set after [r info stats]

# check keyspace misses
assert_equal [getInfoProperty $before keyspace_misses] 0
assert_equal [getInfoProperty $before keyspace_expiration_misses] 0
assert_equal [getInfoProperty $after keyspace_misses] 2
assert_equal [getInfoProperty $after keyspace_expiration_misses] 1

# rollback active expire cycle
r debug set-active-expire 1
}
}
}

Expand Down
42 changes: 42 additions & 0 deletions tests/unit/pubsub.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,48 @@ start_server {tags {"pubsub network"}} {
$rd1 close
}

test "Keyspace notifications: lazy expired events" {
# disable active expire
r debug set-active-expire 0
r config set notify-keyspace-events KEX
r del foo
set rd1 [valkey_deferring_client]
assert_equal {1} [psubscribe $rd1 *]
r set foo bar PX 1
wait_for_condition 50 100 {
[r exists foo] == 0
} else {
fail "Key does not lazy expire?!"
}
assert_equal "pmessage * __keyspace@${db}__:foo lazyexpired" [$rd1 read]
assert_equal "pmessage * __keyevent@${db}__:lazyexpired foo" [$rd1 read]
$rd1 close
# enable active expire
r debug set-active-expire 1
}

test "Keyspace notifications: expired and lazy expired events" {
# disable active expire
r debug set-active-expire 0
r config set notify-keyspace-events KEXx
r del foo
set rd1 [valkey_deferring_client]
assert_equal {1} [psubscribe $rd1 *]
r set foo bar PX 1
wait_for_condition 50 100 {
[r exists foo] == 0
} else {
fail "Key does not lazy expire?!"
}
assert_equal "pmessage * __keyspace@${db}__:foo expired" [$rd1 read]
assert_equal "pmessage * __keyevent@${db}__:expired foo" [$rd1 read]
assert_equal "pmessage * __keyspace@${db}__:foo lazyexpired" [$rd1 read]
assert_equal "pmessage * __keyevent@${db}__:lazyexpired foo" [$rd1 read]
$rd1 close
# enable active expire
r debug set-active-expire 1
}

test "Keyspace notifications: evicted events" {
r config set notify-keyspace-events Ee
r config set maxmemory-policy allkeys-lru
Expand Down