Skip to content

Commit 40e7703

Browse files
cmgracefacebook-github-bot
authored andcommitted
Add CPU saturation-pct min/max ODS metrics for allotment monitoring (#558)
Summary: Adds min.60/max.60 aggregations for cpu.saturation-pct, extracted from D104745222 for a cleaner diff stack. New ODS keys (gated with TargetedOdsFilter defaultPublish=false): - dyno.allotment.cpu.saturation-pct.min.60 - dyno.allotment.cpu.saturation-pct.max.60 These use the same /proc/stat PROC_STAT data source as the existing saturation-pct avg and percentile metrics, with the readCgroupStat_ fallback honored. Values are normalized to percentage of the allotment cpuSaturationLimit. Adds getMinCPUCoresUsage() and getMaxCPUCoresUsage() to CPUTimeMonitor using raw() + std::min/max_element since MetricSeries lacks built-in min/max. Design doc: https://docs.google.com/document/d/16ea49QzoL60PTtFQ0DXss4Bmq7aCnSGh4MuG9phMmO0/edit Differential Revision: D105619438
1 parent 7cc651e commit 40e7703

3 files changed

Lines changed: 111 additions & 0 deletions

File tree

dynolog/src/CPUTimeMonitor.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,50 @@ std::optional<std::pair<double, double>> CPUTimeMonitor::getCpuBreakdownMinMax(
334334
return std::make_pair(*minIt, *maxIt);
335335
}
336336

337+
std::optional<std::pair<double, double>> CPUTimeMonitor::getMinMaxCPUCoresUsage(
338+
Granularity gran,
339+
uint64_t seconds_ago,
340+
const std::optional<std::string>& targetId,
341+
DataSource dataSource) {
342+
TimePoint now = std::chrono::steady_clock::now();
343+
std::shared_lock lock(dataLock_);
344+
345+
const auto* frame = getMetricFrame(gran, dataSource);
346+
if (frame == nullptr) {
347+
return std::nullopt;
348+
}
349+
auto slice = frame->slice(now - std::chrono::seconds(seconds_ago), now);
350+
if (slice == std::nullopt) {
351+
return std::nullopt;
352+
}
353+
std::string key = targetId.value_or("host");
354+
auto series = slice->series<double>(key);
355+
if (series == std::nullopt || series->size() == 0) {
356+
return std::nullopt;
357+
}
358+
auto data = series->raw();
359+
auto [minIt, maxIt] = std::minmax_element(data.begin(), data.end());
360+
return std::make_pair(*minIt, *maxIt);
361+
}
362+
363+
std::optional<double> CPUTimeMonitor::getMinCPUCoresUsage(
364+
Granularity gran,
365+
uint64_t seconds_ago,
366+
const std::optional<std::string>& targetId,
367+
DataSource dataSource) {
368+
auto result = getMinMaxCPUCoresUsage(gran, seconds_ago, targetId, dataSource);
369+
return result ? std::optional(result->first) : std::nullopt;
370+
}
371+
372+
std::optional<double> CPUTimeMonitor::getMaxCPUCoresUsage(
373+
Granularity gran,
374+
uint64_t seconds_ago,
375+
const std::optional<std::string>& targetId,
376+
DataSource dataSource) {
377+
auto result = getMinMaxCPUCoresUsage(gran, seconds_ago, targetId, dataSource);
378+
return result ? std::optional(result->second) : std::nullopt;
379+
}
380+
337381
void CPUTimeMonitor::tick(TMask mask) {
338382
TimePoint tickTime = std::chrono::steady_clock::now();
339383
bool readPerCore = false;

dynolog/src/CPUTimeMonitor.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,24 @@ class CPUTimeMonitor : MonitorBase<Ticker<60000, 1000, 10, 3>> {
124124
CpuBreakdown breakdown,
125125
const std::optional<std::string>& targetId = std::nullopt);
126126

127+
std::optional<double> getMinCPUCoresUsage(
128+
Granularity gran,
129+
uint64_t seconds_ago,
130+
const std::optional<std::string>& targetId = std::nullopt,
131+
DataSource dataSource = DataSource::PROC_STAT);
132+
133+
std::optional<double> getMaxCPUCoresUsage(
134+
Granularity gran,
135+
uint64_t seconds_ago,
136+
const std::optional<std::string>& targetId = std::nullopt,
137+
DataSource dataSource = DataSource::PROC_STAT);
138+
139+
std::optional<std::pair<double, double>> getMinMaxCPUCoresUsage(
140+
Granularity gran,
141+
uint64_t seconds_ago,
142+
const std::optional<std::string>& targetId = std::nullopt,
143+
DataSource dataSource = DataSource::PROC_STAT);
144+
127145
private:
128146
std::optional<double> getCpuBreakdownMinOrMax(
129147
Granularity gran,

dynolog/tests/CPUTimeMonitorTest.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,55 @@ TEST_F(CPUTimeMonitorTest, testBreakdownMinMaxCombined) {
11241124
monitor->deRegisterTarget("combined_test");
11251125
}
11261126

1127+
TEST_F(CPUTimeMonitorTest, testMinMaxCPUCoresUsage) {
1128+
monitor->registerTarget("cores_test", {0, 1, 2, 3});
1129+
monitor->tick(major_tick_60s);
1130+
monitor->tick(major_tick_60s);
1131+
1132+
for (const auto& gran :
1133+
{CPUTimeMonitor::Granularity::MINUTE,
1134+
CPUTimeMonitor::Granularity::SECOND}) {
1135+
auto minVal = monitor->getMinCPUCoresUsage(
1136+
gran, 60, "cores_test", CPUTimeMonitor::DataSource::PROC_STAT);
1137+
auto maxVal = monitor->getMaxCPUCoresUsage(
1138+
gran, 60, "cores_test", CPUTimeMonitor::DataSource::PROC_STAT);
1139+
auto avgVal = monitor->getAvgCPUCoresUsage(
1140+
gran, 60, "cores_test", CPUTimeMonitor::DataSource::PROC_STAT);
1141+
1142+
ASSERT_TRUE(minVal.has_value());
1143+
ASSERT_TRUE(maxVal.has_value());
1144+
ASSERT_TRUE(avgVal.has_value());
1145+
EXPECT_LE(minVal.value(), avgVal.value());
1146+
EXPECT_LE(avgVal.value(), maxVal.value());
1147+
}
1148+
1149+
monitor->deRegisterTarget("cores_test");
1150+
}
1151+
1152+
TEST(CPUTimeMonitorFallbackTest, testMinMaxCgroupStatFallback) {
1153+
// readCgroupStat=false: CGROUP_STAT requests fall back to PROC_STAT data
1154+
auto ticker = std::make_shared<CPUTimeMonitor::TTicker>();
1155+
auto mon = std::make_shared<CPUTimeMonitor>(
1156+
ticker, false, coreCount, getenv("TESTROOT"), true);
1157+
mon->tick(major_tick_60s);
1158+
mon->tick(major_tick_60s);
1159+
1160+
auto minProc = mon->getMinCPUCoresUsage(
1161+
CPUTimeMonitor::Granularity::MINUTE,
1162+
60,
1163+
std::nullopt,
1164+
CPUTimeMonitor::DataSource::PROC_STAT);
1165+
auto minCgroup = mon->getMinCPUCoresUsage(
1166+
CPUTimeMonitor::Granularity::MINUTE,
1167+
60,
1168+
std::nullopt,
1169+
CPUTimeMonitor::DataSource::CGROUP_STAT);
1170+
1171+
ASSERT_TRUE(minProc.has_value());
1172+
ASSERT_TRUE(minCgroup.has_value());
1173+
EXPECT_DOUBLE_EQ(minProc.value(), minCgroup.value());
1174+
}
1175+
11271176
TEST_F(CPUTimeMonitorTest, testBreakdownMinMaxNulloptAfterDeregister) {
11281177
monitor->registerTarget("dereg_test", {0, 1});
11291178
monitor->tick(major_tick_60s);

0 commit comments

Comments
 (0)