Skip to content

Commit 61ed4e8

Browse files
qlimenoqueromange
andauthored
feat: prometheus metrics per database (#5770)
* feat: prometheus metrics per database --------- Co-authored-by: Roman Gershman <[email protected]>
1 parent c2d270e commit 61ed4e8

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

src/server/db_slice.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,7 @@ auto DbSlice::FindInternal(const Context& cntx, string_view key, optional<unsign
580580

581581
if (!IsValid(res.it)) {
582582
events_.misses += miss_weight;
583+
db.stats.events.misses += miss_weight;
583584
return OpStatus::KEY_NOTFOUND;
584585
}
585586

@@ -589,13 +590,15 @@ auto DbSlice::FindInternal(const Context& cntx, string_view key, optional<unsign
589590

590591
if (req_obj_type.has_value() && res.it->second.ObjType() != req_obj_type.value()) {
591592
events_.misses += miss_weight;
593+
db.stats.events.misses += miss_weight;
592594
return OpStatus::WRONG_TYPE;
593595
}
594596

595597
if (res.it->second.HasExpire()) { // check expiry state
596598
res = ExpireIfNeeded(cntx, res.it);
597599
if (!IsValid(res.it)) {
598600
events_.misses += miss_weight;
601+
db.stats.events.misses += miss_weight;
599602
return OpStatus::KEY_NOTFOUND;
600603
}
601604
}
@@ -612,6 +615,7 @@ auto DbSlice::FindInternal(const Context& cntx, string_view key, optional<unsign
612615
break;
613616
case UpdateStatsMode::kReadStats:
614617
events_.hits++;
618+
db.stats.events.hits++;
615619
if (db.slots_stats) {
616620
db.slots_stats[KeySlot(key)].total_reads++;
617621
}
@@ -778,6 +782,7 @@ OpResult<DbSlice::ItAndUpdater> DbSlice::AddOrFindInternal(const Context& cntx,
778782
events_.garbage_collected = db.prime.garbage_collected();
779783
events_.stash_unloaded = db.prime.stash_unloaded();
780784
events_.evicted_keys += evp.evicted();
785+
db.stats.events.evicted_keys += evp.evicted();
781786
events_.garbage_checked += evp.checked();
782787
if (db.slots_stats) {
783788
SlotId sid = KeySlot(key);
@@ -1264,6 +1269,7 @@ DbSlice::PrimeItAndExp DbSlice::ExpireIfNeeded(const Context& cntx, PrimeIterato
12641269
ExpIterator(expire_it, StringOrView::FromView(key)), db.get());
12651270

12661271
++events_.expired_keys;
1272+
db->stats.events.expired_keys++;
12671273

12681274
return {PrimeIterator{}, ExpireIterator{}};
12691275
}
@@ -1493,6 +1499,7 @@ pair<uint64_t, size_t> DbSlice::FreeMemWithEvictionStepAtomic(DbIndex db_ind,
14931499
SendQueuedInvalidationMessagesAsync();
14941500
auto time_finish = absl::GetCurrentTimeNanos();
14951501
events_.evicted_keys += evicted_items;
1502+
db_arr_[db_ind]->stats.events.evicted_keys += evicted_items;
14961503
DVLOG(2) << "Eviction time (us): " << (time_finish - time_start) / 1000;
14971504
return pair<uint64_t, size_t>{evicted_items, evicted_bytes};
14981505
}
@@ -1582,6 +1589,11 @@ void DbSlice::ResetUpdateEvents() {
15821589

15831590
void DbSlice::ResetEvents() {
15841591
events_ = {};
1592+
for (auto& db : db_arr_) {
1593+
if (db) {
1594+
db->stats.events = {};
1595+
}
1596+
}
15851597
}
15861598

15871599
void DbSlice::SetNotifyKeyspaceEvents(std::string_view notify_keyspace_events) {

src/server/multi_test.cc

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,60 @@ TEST_F(MultiTest, HitMissStats) {
150150
EXPECT_THAT(metrics.events.misses, 1);
151151
}
152152

153+
TEST_F(MultiTest, PerDbHitMissStats) {
154+
Run({"SELECT", "0"});
155+
ASSERT_EQ(Run({"SET", "key1", "val1"}), "OK");
156+
ASSERT_EQ(Run({"GET", "key1"}), "val1");
157+
ASSERT_THAT(Run({"GET", "nonexistent1"}), ArgType(RespExpr::NIL));
158+
159+
Run({"SELECT", "1"});
160+
ASSERT_EQ(Run({"SET", "key2", "val2"}), "OK");
161+
ASSERT_EQ(Run({"GET", "key2"}), "val2");
162+
ASSERT_THAT(Run({"GET", "nonexistent2"}), ArgType(RespExpr::NIL));
163+
164+
auto metrics = GetMetrics();
165+
166+
EXPECT_GE(metrics.db_stats.size(), 2u);
167+
EXPECT_EQ(metrics.db_stats[0].events.hits, 1u);
168+
EXPECT_EQ(metrics.db_stats[0].events.misses, 1u);
169+
EXPECT_EQ(metrics.db_stats[1].events.hits, 1u);
170+
EXPECT_EQ(metrics.db_stats[1].events.misses, 1u);
171+
172+
EXPECT_EQ(metrics.events.hits, 2u);
173+
EXPECT_EQ(metrics.events.misses, 2u);
174+
}
175+
176+
TEST_F(MultiTest, PerDbHitMissStatsReset) {
177+
Run({"SELECT", "0"});
178+
Run({"SET", "key1", "val1"});
179+
Run({"GET", "key1"});
180+
Run({"GET", "key2"});
181+
182+
auto before = GetMetrics();
183+
ASSERT_GT(before.db_stats[0].events.hits, 0u);
184+
ASSERT_GT(before.db_stats[0].events.misses, 0u);
185+
186+
EXPECT_EQ("OK", Run({"CONFIG", "RESETSTAT"}));
187+
188+
auto after = GetMetrics();
189+
EXPECT_EQ(after.db_stats[0].events.hits, 0u);
190+
EXPECT_EQ(after.db_stats[0].events.misses, 0u);
191+
}
192+
193+
TEST_F(MultiTest, PerDbHitMissInfoOutput) {
194+
Run({"SELECT", "0"});
195+
Run({"SET", "testkey", "testval"});
196+
Run({"GET", "testkey"});
197+
Run({"GET", "missing"});
198+
199+
auto info_resp = Run({"INFO", "keyspace"});
200+
ASSERT_TRUE(info_resp.type == RespExpr::STRING);
201+
string info_str = info_resp.GetString();
202+
EXPECT_THAT(info_str, HasSubstr("hits=1"));
203+
EXPECT_THAT(info_str, HasSubstr("misses=1"));
204+
EXPECT_THAT(info_str, HasSubstr("hit_ratio=50.00"));
205+
}
206+
153207
TEST_F(MultiTest, MultiEmpty) {
154208
RespExpr resp = Run({"multi"});
155209
ASSERT_EQ(resp, "OK");

src/server/server_family.cc

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,22 @@ void PrintPrometheusMetrics(uint64_t uptime, const Metrics& m, DflyCmd* dfly_cmd
17241724
&resp->body());
17251725
AppendMetricWithoutLabels("evicted_keys_total", "", m.events.evicted_keys, MetricType::COUNTER,
17261726
&resp->body());
1727+
// Per-DB expired/evicted totals
1728+
{
1729+
string perdb_str;
1730+
AppendMetricHeader("expired_keys_total", "", MetricType::COUNTER, &perdb_str);
1731+
AppendMetricHeader("evicted_keys_total", "", MetricType::COUNTER, &perdb_str);
1732+
for (size_t i = 0; i < m.db_stats.size(); ++i) {
1733+
const auto& s = m.db_stats[i];
1734+
if (s.events.expired_keys > 0)
1735+
AppendMetricValue("expired_keys_total", s.events.expired_keys, {"db"}, {StrCat("db", i)},
1736+
&perdb_str);
1737+
if (s.events.evicted_keys > 0)
1738+
AppendMetricValue("evicted_keys_total", s.events.evicted_keys, {"db"}, {StrCat("db", i)},
1739+
&perdb_str);
1740+
}
1741+
absl::StrAppend(&resp->body(), perdb_str);
1742+
}
17271743
// Memory stats
17281744
if (legacy) {
17291745
AppendMetricWithoutLabels("memory_fiberstack_vms_bytes",
@@ -1915,6 +1931,11 @@ void PrintPrometheusMetrics(uint64_t uptime, const Metrics& m, DflyCmd* dfly_cmd
19151931

19161932
AppendMetricValue("db_keys_expiring", m.db_stats[i].expire_count, {"db"}, {StrCat("db", i)},
19171933
&db_key_expire_metrics);
1934+
1935+
AppendMetricValue("keyspace_hits_total", m.db_stats[i].events.hits, {"db"}, {StrCat("db", i)},
1936+
&resp->body());
1937+
AppendMetricValue("keyspace_misses_total", m.db_stats[i].events.misses, {"db"},
1938+
{StrCat("db", i)}, &resp->body());
19181939
}
19191940

19201941
absl::StrAppend(&resp->body(), db_key_metrics, db_key_expire_metrics, db_capacity_metrics,
@@ -2644,7 +2665,8 @@ void ServerFamily::ResetStat(Namespace* ns) {
26442665
registry->ResetCallStats(index);
26452666
EngineShard* shard = EngineShard::tlocal();
26462667
if (shard) {
2647-
ns->GetDbSlice(shard->shard_id()).ResetEvents();
2668+
auto& db_slice = ns->GetDbSlice(shard->shard_id());
2669+
db_slice.ResetEvents();
26482670
}
26492671
facade::ResetStats();
26502672
ServerState::tlocal()->exec_freq_count.clear();
@@ -3244,7 +3266,12 @@ string ServerFamily::FormatInfoMetrics(const Metrics& m, std::string_view sectio
32443266
const auto& stats = m.db_stats[i];
32453267
bool show = (i == 0) || (stats.key_count > 0);
32463268
if (show) {
3269+
size_t total = stats.events.hits + stats.events.misses;
3270+
double hit_ratio =
3271+
(total > 0) ? static_cast<double>(stats.events.hits) / (total)*100.0 : 0.0;
32473272
string val = StrCat("keys=", stats.key_count, ",expires=", stats.expire_count,
3273+
",hits=", stats.events.hits, ",misses=", stats.events.misses,
3274+
",hit_ratio=", absl::StrFormat("%.2f", hit_ratio),
32483275
",avg_ttl=-1"); // TODO
32493276
append(StrCat("db", i), val);
32503277
}

src/server/table.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,16 @@ void DbTableStats::AddTypeMemoryUsage(unsigned type, int64_t delta) {
3030

3131
DbTableStats& DbTableStats::operator+=(const DbTableStats& o) {
3232
constexpr size_t kDbSz = sizeof(DbTableStats) - sizeof(memory_usage_by_type);
33-
static_assert(kDbSz == 32);
33+
static_assert(kDbSz == 64);
3434

3535
ADD(inline_keys);
3636
ADD(obj_memory_usage);
3737
ADD(tiered_entries);
3838
ADD(tiered_used_bytes);
39+
ADD(events.hits);
40+
ADD(events.misses);
41+
ADD(events.expired_keys);
42+
ADD(events.evicted_keys);
3943

4044
for (size_t i = 0; i < o.memory_usage_by_type.size(); ++i) {
4145
memory_usage_by_type[i] += o.memory_usage_by_type[i];

src/server/table.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ struct DbTableStats {
7070
size_t tiered_entries = 0;
7171
size_t tiered_used_bytes = 0;
7272

73+
struct {
74+
// Per-database hits/misses on keys
75+
size_t hits = 0;
76+
size_t misses = 0;
77+
78+
// Per-database expired/evicted keys
79+
size_t expired_keys = 0;
80+
size_t evicted_keys = 0;
81+
} events;
82+
7383
std::array<size_t, OBJ_TYPE_MAX> memory_usage_by_type = {};
7484

7585
// Mostly used internally, exposed for tiered storage.

0 commit comments

Comments
 (0)