Skip to content

Commit 4b3c93d

Browse files
cmgracefacebook-github-bot
authored andcommitted
Add CPU saturation-pct min/max ODS metrics for allotment monitoring
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 a98353a commit 4b3c93d

3 files changed

Lines changed: 119 additions & 0 deletions

File tree

dynolog/src/CPUTimeMonitor.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,69 @@ std::optional<double> CPUTimeMonitor::getCpuBreakdownMax(
306306
return getCpuBreakdownMinOrMax(gran, seconds_ago, breakdown, targetId, false);
307307
}
308308

309+
// Min/max for overall CPU cores usage (saturation). Honors the same
310+
// readCgroupStat_ fallback as getAvg/Quantile: if cgroup stat collection is
311+
// disabled, a CGROUP_STAT request falls back to PROC_STAT data.
312+
std::optional<double> CPUTimeMonitor::getMinCPUCoresUsage(
313+
Granularity gran,
314+
uint64_t seconds_ago,
315+
const std::optional<std::string>& targetId,
316+
DataSource dataSource) {
317+
TimePoint now = std::chrono::steady_clock::now();
318+
std::shared_lock lock(dataLock_);
319+
320+
auto levelOpt = granularityToLevel(gran);
321+
if (!levelOpt.has_value()) {
322+
return std::nullopt;
323+
}
324+
int level = *levelOpt;
325+
326+
auto& frame = (readCgroupStat_ && dataSource == DataSource::CGROUP_STAT)
327+
? cgroupUsageMetricFrames_[level]
328+
: procUsageMetricFrames_[level];
329+
auto slice = frame.slice(now - std::chrono::seconds(seconds_ago), now);
330+
if (slice == std::nullopt) {
331+
return std::nullopt;
332+
}
333+
std::string key = targetId.value_or("host");
334+
auto series = slice->series<double>(key);
335+
if (series == std::nullopt || series->size() == 0) {
336+
return std::nullopt;
337+
}
338+
auto data = series->raw();
339+
return *std::min_element(data.begin(), data.end());
340+
}
341+
342+
std::optional<double> CPUTimeMonitor::getMaxCPUCoresUsage(
343+
Granularity gran,
344+
uint64_t seconds_ago,
345+
const std::optional<std::string>& targetId,
346+
DataSource dataSource) {
347+
TimePoint now = std::chrono::steady_clock::now();
348+
std::shared_lock lock(dataLock_);
349+
350+
auto levelOpt = granularityToLevel(gran);
351+
if (!levelOpt.has_value()) {
352+
return std::nullopt;
353+
}
354+
int level = *levelOpt;
355+
356+
auto& frame = (readCgroupStat_ && dataSource == DataSource::CGROUP_STAT)
357+
? cgroupUsageMetricFrames_[level]
358+
: procUsageMetricFrames_[level];
359+
auto slice = frame.slice(now - std::chrono::seconds(seconds_ago), now);
360+
if (slice == std::nullopt) {
361+
return std::nullopt;
362+
}
363+
std::string key = targetId.value_or("host");
364+
auto series = slice->series<double>(key);
365+
if (series == std::nullopt || series->size() == 0) {
366+
return std::nullopt;
367+
}
368+
auto data = series->raw();
369+
return *std::max_element(data.begin(), data.end());
370+
}
371+
309372
void CPUTimeMonitor::tick(TMask mask) {
310373
TimePoint tickTime = std::chrono::steady_clock::now();
311374
bool readPerCore = false;

dynolog/src/CPUTimeMonitor.h

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

120+
std::optional<double> getMinCPUCoresUsage(
121+
Granularity gran,
122+
uint64_t seconds_ago,
123+
const std::optional<std::string>& targetId = std::nullopt,
124+
DataSource dataSource = DataSource::PROC_STAT);
125+
126+
std::optional<double> getMaxCPUCoresUsage(
127+
Granularity gran,
128+
uint64_t seconds_ago,
129+
const std::optional<std::string>& targetId = std::nullopt,
130+
DataSource dataSource = DataSource::PROC_STAT);
131+
120132
private:
121133
std::optional<double> getCpuBreakdownMinOrMax(
122134
Granularity gran,

dynolog/tests/CPUTimeMonitorTest.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,50 @@ TEST_F(CPUTimeMonitorTest, testBreakdownMinMax) {
10951095
monitor->deRegisterTarget("minmax_test");
10961096
}
10971097

1098+
TEST_F(CPUTimeMonitorTest, testMinMaxCPUCoresUsage) {
1099+
monitor->registerTarget("cores_test", {0, 1, 2, 3});
1100+
monitor->tick(major_tick_60s);
1101+
monitor->tick(major_tick_60s);
1102+
1103+
for (const auto& gran :
1104+
{CPUTimeMonitor::Granularity::MINUTE,
1105+
CPUTimeMonitor::Granularity::SECOND}) {
1106+
auto minVal = monitor->getMinCPUCoresUsage(
1107+
gran, 60, "cores_test", CPUTimeMonitor::DataSource::PROC_STAT);
1108+
auto maxVal = monitor->getMaxCPUCoresUsage(
1109+
gran, 60, "cores_test", CPUTimeMonitor::DataSource::PROC_STAT);
1110+
auto avgVal = monitor->getAvgCPUCoresUsage(
1111+
gran, 60, "cores_test", CPUTimeMonitor::DataSource::PROC_STAT);
1112+
1113+
ASSERT_TRUE(minVal.has_value());
1114+
ASSERT_TRUE(maxVal.has_value());
1115+
ASSERT_TRUE(avgVal.has_value());
1116+
EXPECT_LE(minVal.value(), avgVal.value());
1117+
EXPECT_LE(avgVal.value(), maxVal.value());
1118+
}
1119+
1120+
monitor->deRegisterTarget("cores_test");
1121+
}
1122+
1123+
TEST_F(CPUTimeMonitorTest, testMinMaxCgroupStatFallback) {
1124+
monitor->tick(major_tick_60s);
1125+
monitor->tick(major_tick_60s);
1126+
1127+
auto minProc = monitor->getMinCPUCoresUsage(
1128+
CPUTimeMonitor::Granularity::MINUTE,
1129+
60,
1130+
std::nullopt,
1131+
CPUTimeMonitor::DataSource::PROC_STAT);
1132+
auto minCgroup = monitor->getMinCPUCoresUsage(
1133+
CPUTimeMonitor::Granularity::MINUTE,
1134+
60,
1135+
std::nullopt,
1136+
CPUTimeMonitor::DataSource::CGROUP_STAT);
1137+
1138+
ASSERT_TRUE(minProc.has_value());
1139+
ASSERT_TRUE(minCgroup.has_value());
1140+
}
1141+
10981142
TEST_F(CPUTimeMonitorTest, testBreakdownMinMaxNulloptAfterDeregister) {
10991143
monitor->registerTarget("dereg_test", {0, 1});
11001144
monitor->tick(major_tick_60s);

0 commit comments

Comments
 (0)