Skip to content

Commit

Permalink
Implement consolidation functions for rrd
Browse files Browse the repository at this point in the history
These are the ones supported by rrdfetch.

CMK-18929

Change-Id: I547673771c7c2099dd9b76c45e6cb82eedb40e4d
  • Loading branch information
Synss committed Jan 13, 2025
1 parent 702542a commit db3fce7
Show file tree
Hide file tree
Showing 3 changed files with 277 additions and 0 deletions.
66 changes: 66 additions & 0 deletions packages/livestatus/include/livestatus/RRDConsolidate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
// This file is part of Checkmk (https://checkmk.com). It is subject to the
// terms and conditions defined in the file COPYING, which is part of this
// source code package.

#include <cstddef>
#include <limits>
#include <memory>
#include <vector>

/// The four consolidation functions from
/// https://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html
struct CF {
virtual ~CF() = default;
[[nodiscard]] virtual double init() = 0;
virtual void handle(double value) = 0;
};

class MaxCF : public CF {
public:
MaxCF() = default;
~MaxCF() override = default;
[[nodiscard]] double init() override;
void handle(double value) override;

private:
double counter_ = std::numeric_limits<double>::quiet_NaN();
};

class MinCF : public CF {
public:
MinCF() = default;
~MinCF() override = default;
[[nodiscard]] double init() override;
void handle(double value) override;

private:
double counter_ = std::numeric_limits<double>::quiet_NaN();
};

class AvgCF : public CF {
public:
AvgCF() = default;
~AvgCF() override = default;
[[nodiscard]] double init() override;
void handle(double value) override;

private:
std::size_t nelem = 0;
double counter_ = std::numeric_limits<double>::quiet_NaN();
};

class LastCF : public CF {
public:
LastCF() = default;
~LastCF() override = default;
[[nodiscard]] double init() override;
void handle(double value) override;

private:
double counter_ = std::numeric_limits<double>::quiet_NaN();
};

std::vector<double> rrd_consolidate(const std::unique_ptr<CF> &f,
const std::vector<double> &input,
std::size_t act_step, std::size_t target);
82 changes: 82 additions & 0 deletions packages/livestatus/src/RRDConsolidate.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
// This file is part of Checkmk (https://checkmk.com). It is subject to the
// terms and conditions defined in the file COPYING, which is part of this
// source code package.

#include "livestatus/RRDConsolidate.h"

#include <algorithm>
#include <cmath>
#include <iterator>
#include <limits>

namespace {
constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
}

double MaxCF::init() {
const double out = counter_;
counter_ = NaN;
return out;
}

void MaxCF::handle(double value) {
if (std::isnan(value)) {
return;
}
counter_ = std::isnan(counter_) ? value : std::max(counter_, value);
}

double MinCF::init() {
const double out = counter_;
counter_ = NaN;
return out;
}

void MinCF::handle(double value) {
if (std::isnan(value)) {
return;
}
counter_ = std::isnan(counter_) ? value : std::min(counter_, value);
}

double AvgCF::init() {
const double out =
nelem == 0 ? counter_ : counter_ / static_cast<double>(nelem);
counter_ = NaN;
nelem = 0;
return out;
}

void AvgCF::handle(double value) {
if (std::isnan(value)) {
return;
}
counter_ = std::isnan(counter_) ? value : counter_ + value;
nelem += 1;
}

double LastCF::init() {
const double out = counter_;
counter_ = NaN;
return out;
}

void LastCF::handle(double value) { counter_ = value; }

std::vector<double> rrd_consolidate(const std::unique_ptr<CF> &cf,
const std::vector<double> &input,
std::size_t act_step, std::size_t target) {
if (act_step >= target) {
return input;
}
const std::size_t factor = target / act_step;
auto out = std::vector<double>{};
for (auto iter = input.begin(); iter != input.end(); iter++) {
cf->handle(*iter);
if (std::distance(input.begin(), iter) % factor == factor - 1) {
out.emplace_back(cf->init());
}
}
return out;
}
129 changes: 129 additions & 0 deletions packages/livestatus/test/test_RRDConsolidate.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (C) 2019 Checkmk GmbH - License: GNU General Public License v2
// This file is part of Checkmk (https://checkmk.com). It is subject to the
// terms and conditions defined in the file COPYING, which is part of this
// source code package.

#include <cmath>
#include <limits>
#include <memory>
#include <numeric>
#include <string>
#include <vector>

#include "gtest/gtest.h"
#include "livestatus/RRDConsolidate.h"

namespace {
constexpr auto NaN = std::numeric_limits<double>::quiet_NaN();
} // namespace

TEST(TestRRDConsolidate, Constant) {
const auto value = 2.0;
const auto input = std::vector<double>(200, value);
EXPECT_EQ(input, rrd_consolidate(std::make_unique<MinCF>(), input, 10, 10));
EXPECT_EQ(input, rrd_consolidate(std::make_unique<MaxCF>(), input, 10, 10));
EXPECT_EQ(input, rrd_consolidate(std::make_unique<AvgCF>(), input, 10, 10));
EXPECT_EQ(input,
rrd_consolidate(std::make_unique<LastCF>(), input, 10, 10));

{
const auto min_ =
rrd_consolidate(std::make_unique<MinCF>(), input, 10, 20);
ASSERT_EQ(input.size() / 2, min_.size());
EXPECT_EQ(std::vector<double>(min_.size(), value), min_);
}
{
const auto max_ =
rrd_consolidate(std::make_unique<MaxCF>(), input, 10, 20);
ASSERT_EQ(input.size() / 2, max_.size());
EXPECT_EQ(std::vector<double>(max_.size(), value), max_);
}
{
const auto avg_ =
rrd_consolidate(std::make_unique<MinCF>(), input, 10, 20);
ASSERT_EQ(input.size() / 2, avg_.size());
EXPECT_EQ(std::vector<double>(avg_.size(), value), avg_);
}
{
const auto last_ =
rrd_consolidate(std::make_unique<LastCF>(), input, 10, 20);
ASSERT_EQ(input.size() / 2, last_.size());
EXPECT_EQ(std::vector<double>(last_.size(), value), last_);
}
}

TEST(TestRRDConsolidate, NaN) {
const auto value = NaN;
const auto input = std::vector<double>(20, value);

{
const auto min_ =
rrd_consolidate(std::make_unique<MinCF>(), input, 10, 20);
ASSERT_EQ(input.size() / 2, min_.size());
EXPECT_TRUE(std::accumulate(
input.begin(), input.end(), true,
[](auto &&lhs, auto &&rhs) { return lhs && std::isnan(rhs); }));
}
{
const auto max_ =
rrd_consolidate(std::make_unique<MaxCF>(), input, 10, 20);
ASSERT_EQ(input.size() / 2, max_.size());
EXPECT_TRUE(std::accumulate(
input.begin(), input.end(), true,
[](auto &&lhs, auto &&rhs) { return lhs && std::isnan(rhs); }));
}
{
const auto avg_ =
rrd_consolidate(std::make_unique<MinCF>(), input, 10, 20);
ASSERT_EQ(input.size() / 2, avg_.size());
EXPECT_TRUE(std::accumulate(
input.begin(), input.end(), true,
[](auto &&lhs, auto &&rhs) { return lhs && std::isnan(rhs); }));
}
{
const auto last_ =
rrd_consolidate(std::make_unique<MinCF>(), input, 10, 20);
ASSERT_EQ(input.size() / 2, last_.size());
EXPECT_TRUE(std::accumulate(
input.begin(), input.end(), true,
[](auto &&lhs, auto &&rhs) { return lhs && std::isnan(rhs); }));
}
}

TEST(TestRRDConsolidate, SimpleCases) {
const auto input = std::vector<double>{1, 2, 1, 2, 1, 2, 1, 2};
EXPECT_EQ(std::vector<double>(4, 1.0),
rrd_consolidate(std::make_unique<MinCF>(), input, 10, 20));
EXPECT_EQ(std::vector<double>(4, 2.0),
rrd_consolidate(std::make_unique<MaxCF>(), input, 10, 20));
EXPECT_EQ(std::vector<double>(4, 1.5),
rrd_consolidate(std::make_unique<AvgCF>(), input, 10, 20));
EXPECT_EQ(std::vector<double>(4, 2.0),
rrd_consolidate(std::make_unique<LastCF>(), input, 10, 20));
}

TEST(TestRRDConsolidate, ComplexCases) {
const auto input = std::vector<double>{1, NaN, 1, 2};
{
const auto min_ = std::vector<double>{1, 1};
EXPECT_EQ(min_,
rrd_consolidate(std::make_unique<MinCF>(), input, 10, 20));
}
{
const auto max_ = std::vector<double>{1, 2};
EXPECT_EQ(max_,
rrd_consolidate(std::make_unique<MaxCF>(), input, 10, 20));
}
{
const auto avg_ = std::vector<double>{1, 1.5};
EXPECT_EQ(avg_,
rrd_consolidate(std::make_unique<AvgCF>(), input, 10, 20));
}
{
const auto last_ =
rrd_consolidate(std::make_unique<LastCF>(), input, 10, 20);
EXPECT_EQ(2, last_.size());
EXPECT_TRUE(std::isnan(last_[0]));
EXPECT_DOUBLE_EQ(2.0, last_[1]);
}
}

0 comments on commit db3fce7

Please sign in to comment.