feat: add keyspace_mutations metric (#2329)

* feat: add keyspace_mutations metric

Currently we expose hits/misses for read only commands only (compatible with redis).
`keyyspace_mutations` complement this providing number of key operations for write commands.
It's interesting because now we can learn the number of key_ops vs API ops, where
key_ops = misses + hits + mutations

Signed-off-by: Roman Gershman <roman@dragonflydb.io>

* chore: address fixes

Signed-off-by: Roman Gershman <roman@dragonflydb.io>

---------

Signed-off-by: Roman Gershman <roman@dragonflydb.io>
This commit is contained in:
Roman Gershman 2023-12-24 10:21:36 +02:00 committed by GitHub
parent 4562fad737
commit f90317a795
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 39 additions and 17 deletions

View file

@ -263,7 +263,7 @@ DbStats& DbStats::operator+=(const DbStats& o) {
}
SliceEvents& SliceEvents::operator+=(const SliceEvents& o) {
static_assert(sizeof(SliceEvents) == 88, "You should update this function with new fields");
static_assert(sizeof(SliceEvents) == 96, "You should update this function with new fields");
ADD(evicted_keys);
ADD(hard_evictions);
@ -274,6 +274,7 @@ SliceEvents& SliceEvents::operator+=(const SliceEvents& o) {
ADD(garbage_checked);
ADD(hits);
ADD(misses);
ADD(mutations);
ADD(insertion_rejections);
ADD(update);
@ -399,7 +400,7 @@ DbSlice::AddOrFindResult& DbSlice::AddOrFindResult::operator=(ItAndUpdater&& o)
}
DbSlice::ItAndUpdater DbSlice::FindMutable(const Context& cntx, string_view key) {
auto [it, exp_it] = FindInternal(cntx, key, FindInternalMode::kDontUpdateCacheStats);
auto [it, exp_it] = FindInternal(cntx, key, FindInternalMode::kUpdateMutableStats);
if (IsValid(it)) {
PreUpdate(cntx.db_index, it);
@ -413,7 +414,7 @@ DbSlice::ItAndUpdater DbSlice::FindMutable(const Context& cntx, string_view key)
OpResult<DbSlice::ItAndUpdater> DbSlice::FindMutable(const Context& cntx, string_view key,
unsigned req_obj_type) {
// Don't use FindMutable() so that we don't call PreUpdate()
auto [it, exp_it] = FindInternal(cntx, key, FindInternalMode::kDontUpdateCacheStats);
auto [it, exp_it] = FindInternal(cntx, key, FindInternalMode::kUpdateMutableStats);
if (!IsValid(it))
return OpStatus::KEY_NOTFOUND;
@ -428,7 +429,7 @@ OpResult<DbSlice::ItAndUpdater> DbSlice::FindMutable(const Context& cntx, string
}
DbSlice::ItAndExpConst DbSlice::FindReadOnly(const Context& cntx, std::string_view key) {
auto res = FindInternal(cntx, key, FindInternalMode::kUpdateCacheStats);
auto res = FindInternal(cntx, key, FindInternalMode::kUpdateReadStats);
return {res.it, res.exp_it};
}
@ -457,8 +458,14 @@ DbSlice::ItAndExp DbSlice::FindInternal(const Context& cntx, std::string_view ke
res.it = db.prime.Find(key);
FiberAtomicGuard fg;
if (!IsValid(res.it)) {
if (mode == FindInternalMode::kUpdateCacheStats)
events_.misses++;
switch (mode) {
case FindInternalMode::kUpdateMutableStats:
events_.mutations++;
break;
case FindInternalMode::kUpdateReadStats:
events_.misses++;
break;
}
return res;
}
@ -483,14 +490,17 @@ DbSlice::ItAndExp DbSlice::FindInternal(const Context& cntx, std::string_view ke
db.top_keys.Touch(key);
if (mode == FindInternalMode::kUpdateCacheStats) {
events_.hits++;
if (ClusterConfig::IsEnabled()) {
db.slots_stats[ClusterConfig::KeySlot(key)].total_reads += 1;
}
switch (mode) {
case FindInternalMode::kUpdateMutableStats:
events_.mutations++;
break;
case FindInternalMode::kUpdateReadStats:
events_.hits++;
if (ClusterConfig::IsEnabled()) {
db.slots_stats[ClusterConfig::KeySlot(key)].total_reads++;
}
break;
}
return res;
}
@ -517,7 +527,7 @@ DbSlice::AddOrFindResult DbSlice::AddOrFind(const Context& cntx, string_view key
DbTable& db = *db_arr_[cntx.db_index];
auto res = FindInternal(cntx, key, FindInternalMode::kDontUpdateCacheStats);
auto res = FindInternal(cntx, key, FindInternalMode::kUpdateMutableStats);
if (IsValid(res.it)) {
PreUpdate(cntx.db_index, res.it);

View file

@ -48,6 +48,7 @@ struct SliceEvents {
// hits/misses on keys
size_t hits = 0;
size_t misses = 0;
size_t mutations = 0;
// how many insertions were rejected due to OOM.
size_t insertion_rejections = 0;
@ -422,8 +423,8 @@ class DbSlice {
size_t EvictObjects(size_t memory_to_free, PrimeIterator it, DbTable* table);
enum class FindInternalMode {
kUpdateCacheStats,
kDontUpdateCacheStats,
kUpdateReadStats,
kUpdateMutableStats,
};
ItAndExp FindInternal(const Context& cntx, std::string_view key, FindInternalMode mode);

View file

@ -817,6 +817,8 @@ void PrintPrometheusMetrics(const Metrics& m, StringResponse* resp) {
&resp->body());
AppendMetricWithoutLabels("keyspace_misses_total", "", m.events.misses, MetricType::COUNTER,
&resp->body());
AppendMetricWithoutLabels("keyspace_mutations_total", "", m.events.mutations, MetricType::COUNTER,
&resp->body());
// Net metrics
AppendMetricWithoutLabels("net_input_bytes_total", "", conn_stats.io_read_bytes,
@ -1666,6 +1668,7 @@ void ServerFamily::Info(CmdArgList args, ConnectionContext* cntx) {
append("delete_ttl_sec", m.delete_ttl_per_sec);
append("keyspace_hits", m.events.hits);
append("keyspace_misses", m.events.misses);
append("keyspace_mutations", m.events.mutations);
append("total_reads_processed", m.conn_stats.io_read_cnt);
append("total_writes_processed", m.conn_stats.io_write_cnt);
append("defrag_attempt_total", m.shard_stats.defrag_attempt_total);

View file

@ -46,10 +46,14 @@ TEST_F(StringFamilyTest, SetGet) {
EXPECT_EQ(Run({"get", "key1"}), "1");
EXPECT_EQ(Run({"set", "key", "2"}), "OK");
EXPECT_EQ(Run({"get", "key"}), "2");
EXPECT_THAT(Run({"get", "key3"}), ArgType(RespExpr::NIL));
auto metrics = GetMetrics();
auto tc = metrics.coordinator_stats.tx_type_cnt;
EXPECT_EQ(6, tc[ServerState::QUICK] + tc[ServerState::INLINE]);
EXPECT_EQ(7, tc[ServerState::QUICK] + tc[ServerState::INLINE]);
EXPECT_EQ(3, metrics.events.hits);
EXPECT_EQ(1, metrics.events.misses);
EXPECT_EQ(3, metrics.events.mutations);
}
TEST_F(StringFamilyTest, Incr) {
@ -67,6 +71,10 @@ TEST_F(StringFamilyTest, Incr) {
ASSERT_THAT(Run({"incrby", "ne", "0"}), IntArg(0));
ASSERT_THAT(Run({"decrby", "a", "-9223372036854775808"}), ErrArg("overflow"));
auto metrics = GetMetrics();
EXPECT_EQ(10, metrics.events.mutations);
EXPECT_EQ(0, metrics.events.misses);
EXPECT_EQ(0, metrics.events.hits);
}
TEST_F(StringFamilyTest, Append) {