Skip to content

Commit b3143b7

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 72a43fb commit b3143b7

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
@@ -306,6 +306,50 @@ std::optional<double> CPUTimeMonitor::getCpuBreakdownMax(
306306
return result ? std::optional(result->second) : std::nullopt;
307307
}
308308

309+
std::optional<std::pair<double, double>> CPUTimeMonitor::getMinMaxCPUCoresUsage(
310+
Granularity gran,
311+
uint64_t seconds_ago,
312+
const std::optional<std::string>& targetId,
313+
DataSource dataSource) {
314+
TimePoint now = std::chrono::steady_clock::now();
315+
std::shared_lock lock(dataLock_);
316+
317+
const auto* frame = getMetricFrame(gran, dataSource);
318+
if (frame == nullptr) {
319+
return std::nullopt;
320+
}
321+
auto slice = frame->slice(now - std::chrono::seconds(seconds_ago), now);
322+
if (slice == std::nullopt) {
323+
return std::nullopt;
324+
}
325+
std::string key = targetId.value_or("host");
326+
auto series = slice->series<double>(key);
327+
if (series == std::nullopt || series->size() == 0) {
328+
return std::nullopt;
329+
}
330+
auto data = series->raw();
331+
auto [minIt, maxIt] = std::minmax_element(data.begin(), data.end());
332+
return std::make_pair(*minIt, *maxIt);
333+
}
334+
335+
std::optional<double> CPUTimeMonitor::getMinCPUCoresUsage(
336+
Granularity gran,
337+
uint64_t seconds_ago,
338+
const std::optional<std::string>& targetId,
339+
DataSource dataSource) {
340+
auto result = getMinMaxCPUCoresUsage(gran, seconds_ago, targetId, dataSource);
341+
return result ? std::optional(result->first) : std::nullopt;
342+
}
343+
344+
std::optional<double> CPUTimeMonitor::getMaxCPUCoresUsage(
345+
Granularity gran,
346+
uint64_t seconds_ago,
347+
const std::optional<std::string>& targetId,
348+
DataSource dataSource) {
349+
auto result = getMinMaxCPUCoresUsage(gran, seconds_ago, targetId, dataSource);
350+
return result ? std::optional(result->second) : std::nullopt;
351+
}
352+
309353
void CPUTimeMonitor::tick(TMask mask) {
310354
TimePoint tickTime = std::chrono::steady_clock::now();
311355
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
enum class Statistic { AVG, QUANTILE };
129147

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)