aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/monlib/metrics/metric_value.h
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/monlib/metrics/metric_value.h
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/monlib/metrics/metric_value.h')
-rw-r--r--library/cpp/monlib/metrics/metric_value.h542
1 files changed, 542 insertions, 0 deletions
diff --git a/library/cpp/monlib/metrics/metric_value.h b/library/cpp/monlib/metrics/metric_value.h
new file mode 100644
index 0000000000..607fcc8602
--- /dev/null
+++ b/library/cpp/monlib/metrics/metric_value.h
@@ -0,0 +1,542 @@
+#pragma once
+
+#include "histogram_collector.h"
+#include "metric_value_type.h"
+#include "summary_collector.h"
+#include "log_histogram_snapshot.h"
+
+#include <util/datetime/base.h>
+#include <util/generic/algorithm.h>
+#include <util/generic/vector.h>
+#include <util/generic/cast.h>
+#include <util/generic/ymath.h>
+
+namespace NMonitoring {
+ namespace NPrivate {
+ template <typename T>
+ T FromFloatSafe(double d) {
+ static_assert(std::is_integral<T>::value, "this function only converts floats to integers");
+ Y_ENSURE(::IsValidFloat(d) && d >= Min<T>() && d <= MaxFloor<T>(), "Cannot convert " << d << " to an integer value");
+ return static_cast<T>(d);
+ }
+
+ inline auto POINT_KEY_FN = [](auto& p) {
+ return p.GetTime();
+ };
+ } // namespace NPrivate
+
+ template <typename T, typename Enable = void>
+ struct TValueType;
+
+ template <>
+ struct TValueType<double> {
+ static constexpr auto Type = EMetricValueType::DOUBLE;
+ };
+
+ template <>
+ struct TValueType<i64> {
+ static constexpr auto Type = EMetricValueType::INT64;
+ };
+
+ template <>
+ struct TValueType<ui64> {
+ static constexpr auto Type = EMetricValueType::UINT64;
+ };
+
+ template <>
+ struct TValueType<TLogHistogramSnapshot*> {
+ static constexpr auto Type = EMetricValueType::LOGHISTOGRAM;
+ };
+
+ template <typename T>
+ struct TValueType<T*, typename std::enable_if_t<std::is_base_of<IHistogramSnapshot, T>::value>> {
+ static constexpr auto Type = EMetricValueType::HISTOGRAM;
+ };
+
+ template <typename T>
+ struct TValueType<T*, typename std::enable_if_t<std::is_base_of<ISummaryDoubleSnapshot, T>::value>> {
+ static constexpr auto Type = EMetricValueType::SUMMARY;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+ // TMetricValue
+ ///////////////////////////////////////////////////////////////////////////
+ // TMetricValue represents a generic value. It does not contain type
+ // information about a value. This is done to minimize object footprint.
+ // To read an actual value from the object the type must be checked
+ // first or provided to AsXxxx(type) member-functions.
+ // This class does not hold an ownership of an IHistogramSnapshot or
+ // SummarySnapshot, so this must be done somewhere outside.
+ class TMetricValue {
+ public:
+ TMetricValue() noexcept {
+ Value_.Uint64 = 0;
+ }
+
+ explicit TMetricValue(double value) noexcept {
+ Value_.Double = value;
+ }
+
+ explicit TMetricValue(i64 value) noexcept {
+ Value_.Int64 = value;
+ }
+
+ explicit TMetricValue(ui64 value) noexcept {
+ Value_.Uint64 = value;
+ }
+
+ explicit TMetricValue(IHistogramSnapshot* histogram) noexcept {
+ Value_.Histogram = histogram;
+ }
+
+ explicit TMetricValue(ISummaryDoubleSnapshot* summary) noexcept {
+ Value_.Summary = summary;
+ }
+
+ explicit TMetricValue(TLogHistogramSnapshot* logHist) noexcept {
+ Value_.LogHistogram = logHist;
+ }
+
+ double AsDouble() const noexcept {
+ return Value_.Double;
+ }
+
+ // will cast value into double, current value type is determined by
+ // the given type argument
+ double AsDouble(EMetricValueType type) const {
+ switch (type) {
+ case EMetricValueType::DOUBLE:
+ return Value_.Double;
+ case EMetricValueType::INT64:
+ return static_cast<double>(Value_.Int64);
+ case EMetricValueType::UINT64:
+ return static_cast<double>(Value_.Uint64);
+ case EMetricValueType::HISTOGRAM:
+ ythrow yexception() << "histogram cannot be casted to Double";
+ case EMetricValueType::SUMMARY:
+ ythrow yexception() << "summary cannot be casted to Double";
+ case EMetricValueType::LOGHISTOGRAM:
+ ythrow yexception() << "loghistogram cannot be casted to Double";
+ case EMetricValueType::UNKNOWN:
+ ythrow yexception() << "unknown value type";
+ }
+ Y_FAIL(); // for GCC
+ }
+
+ ui64 AsUint64() const noexcept {
+ return Value_.Uint64;
+ }
+
+ // will cast value into uint64, current value's type is determined by
+ // the given type argument
+ ui64 AsUint64(EMetricValueType type) const {
+ switch (type) {
+ case EMetricValueType::DOUBLE:
+ return NPrivate::FromFloatSafe<ui64>(Value_.Double);
+ case EMetricValueType::INT64:
+ return SafeIntegerCast<ui64>(Value_.Int64);
+ case EMetricValueType::UINT64:
+ return Value_.Uint64;
+ case EMetricValueType::HISTOGRAM:
+ ythrow yexception() << "histogram cannot be casted to Uint64";
+ case EMetricValueType::SUMMARY:
+ ythrow yexception() << "summary cannot be casted to Uint64";
+ case EMetricValueType::LOGHISTOGRAM:
+ ythrow yexception() << "loghistogram cannot be casted to Uint64";
+ case EMetricValueType::UNKNOWN:
+ ythrow yexception() << "unknown value type";
+ }
+ Y_FAIL(); // for GCC
+ }
+
+ i64 AsInt64() const noexcept {
+ return Value_.Int64;
+ }
+
+ // will cast value into int64, current value's type is determined by
+ // the given type argument
+ i64 AsInt64(EMetricValueType type) const {
+ switch (type) {
+ case EMetricValueType::DOUBLE:
+ return NPrivate::FromFloatSafe<i64>(Value_.Double);
+ case EMetricValueType::INT64:
+ return Value_.Int64;
+ case EMetricValueType::UINT64:
+ return SafeIntegerCast<i64>(Value_.Uint64);
+ case EMetricValueType::HISTOGRAM:
+ ythrow yexception() << "histogram cannot be casted to Int64";
+ case EMetricValueType::SUMMARY:
+ ythrow yexception() << "summary cannot be casted to Int64";
+ case EMetricValueType::LOGHISTOGRAM:
+ ythrow yexception() << "loghistogram cannot be casted to Int64";
+ case EMetricValueType::UNKNOWN:
+ ythrow yexception() << "unknown value type";
+ }
+ Y_FAIL(); // for GCC
+ }
+
+ IHistogramSnapshot* AsHistogram() const noexcept {
+ return Value_.Histogram;
+ }
+
+ IHistogramSnapshot* AsHistogram(EMetricValueType type) const {
+ if (type != EMetricValueType::HISTOGRAM) {
+ ythrow yexception() << type << " cannot be casted to Histogram";
+ }
+
+ return Value_.Histogram;
+ }
+
+ ISummaryDoubleSnapshot* AsSummaryDouble() const noexcept {
+ return Value_.Summary;
+ }
+
+ ISummaryDoubleSnapshot* AsSummaryDouble(EMetricValueType type) const {
+ if (type != EMetricValueType::SUMMARY) {
+ ythrow yexception() << type << " cannot be casted to SummaryDouble";
+ }
+
+ return Value_.Summary;
+ }
+
+ TLogHistogramSnapshot* AsLogHistogram() const noexcept {
+ return Value_.LogHistogram;
+ }
+
+ TLogHistogramSnapshot* AsLogHistogram(EMetricValueType type) const {
+ if (type != EMetricValueType::LOGHISTOGRAM) {
+ ythrow yexception() << type << " cannot be casted to LogHistogram";
+ }
+
+ return Value_.LogHistogram;
+ }
+
+ protected:
+ union {
+ double Double;
+ i64 Int64;
+ ui64 Uint64;
+ IHistogramSnapshot* Histogram;
+ ISummaryDoubleSnapshot* Summary;
+ TLogHistogramSnapshot* LogHistogram;
+ } Value_;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////
+ // TMetricValueWithType
+ ///////////////////////////////////////////////////////////////////////////
+ // Same as TMetricValue, but this type holds an ownership of
+ // snapshots and contains value type information.
+ class TMetricValueWithType: private TMetricValue, public TMoveOnly {
+ public:
+ using TBase = TMetricValue;
+
+ template <typename T>
+ explicit TMetricValueWithType(T value)
+ : TBase(value)
+ , ValueType_{TValueType<T>::Type}
+ {
+ Ref();
+ }
+
+ TMetricValueWithType(TMetricValueWithType&& other)
+ : TBase(std::move(other))
+ , ValueType_{other.ValueType_}
+ {
+ Ref();
+ other.Clear();
+ }
+
+ TMetricValueWithType& operator=(TMetricValueWithType&& other) {
+ TBase::operator=(other);
+ ValueType_ = other.ValueType_;
+
+ Ref();
+ other.Clear();
+
+ return *this;
+ }
+
+ ~TMetricValueWithType() {
+ UnRef();
+ }
+
+ void Clear() {
+ UnRef();
+ ValueType_ = EMetricValueType::UNKNOWN;
+ }
+
+ EMetricValueType GetType() const noexcept {
+ return ValueType_;
+ }
+
+ double AsDouble() const {
+ return TBase::AsDouble(ValueType_);
+ }
+
+ ui64 AsUint64() const {
+ return TBase::AsUint64(ValueType_);
+ }
+
+ i64 AsInt64() const {
+ return TBase::AsInt64(ValueType_);
+ }
+
+ IHistogramSnapshot* AsHistogram() const {
+ return TBase::AsHistogram(ValueType_);
+ }
+
+ ISummaryDoubleSnapshot* AsSummaryDouble() const {
+ return TBase::AsSummaryDouble(ValueType_);
+ }
+
+ TLogHistogramSnapshot* AsLogHistogram() const {
+ return TBase::AsLogHistogram(ValueType_);
+ }
+
+ private:
+ void Ref() {
+ if (ValueType_ == EMetricValueType::SUMMARY) {
+ TBase::AsSummaryDouble()->Ref();
+ } else if (ValueType_ == EMetricValueType::HISTOGRAM) {
+ TBase::AsHistogram()->Ref();
+ } else if (ValueType_ == EMetricValueType::LOGHISTOGRAM) {
+ TBase::AsLogHistogram()->Ref();
+ }
+ }
+
+ void UnRef() {
+ if (ValueType_ == EMetricValueType::SUMMARY) {
+ TBase::AsSummaryDouble()->UnRef();
+ } else if (ValueType_ == EMetricValueType::HISTOGRAM) {
+ TBase::AsHistogram()->UnRef();
+ } else if (ValueType_ == EMetricValueType::LOGHISTOGRAM) {
+ TBase::AsLogHistogram()->UnRef();
+ }
+ }
+
+ private:
+ EMetricValueType ValueType_ = EMetricValueType::UNKNOWN;
+ };
+
+ static_assert(sizeof(TMetricValue) == sizeof(ui64),
+ "expected size of TMetricValue is one machine word");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // TMetricTimeSeries
+ ///////////////////////////////////////////////////////////////////////////
+ class TMetricTimeSeries: private TMoveOnly {
+ public:
+ class TPoint {
+ public:
+ TPoint()
+ : Time_(TInstant::Zero())
+ {
+ }
+
+ template <typename T>
+ TPoint(TInstant time, T value)
+ : Time_(time)
+ , Value_(value)
+ {
+ }
+
+ TInstant GetTime() const noexcept {
+ return Time_;
+ }
+
+ TMetricValue GetValue() const noexcept {
+ return Value_;
+ }
+
+ void ClearValue() {
+ Value_ = {};
+ }
+
+ private:
+ TInstant Time_;
+ TMetricValue Value_;
+ };
+
+ public:
+ TMetricTimeSeries() = default;
+
+ TMetricTimeSeries(TMetricTimeSeries&& rhs) noexcept
+ : ValueType_(rhs.ValueType_)
+ , Points_(std::move(rhs.Points_))
+ {
+ rhs.ValueType_ = EMetricValueType::UNKNOWN;
+ }
+
+ TMetricTimeSeries& operator=(TMetricTimeSeries&& rhs) noexcept {
+ Clear();
+
+ ValueType_ = rhs.ValueType_;
+ rhs.ValueType_ = EMetricValueType::UNKNOWN;
+
+ Points_ = std::move(rhs.Points_);
+ return *this;
+ }
+
+ ~TMetricTimeSeries() {
+ Clear();
+ }
+
+ template <typename T>
+ void Add(TInstant time, T value) {
+ Add(TPoint(time, value), TValueType<T>::Type);
+ }
+
+ void Add(TPoint point, EMetricValueType valueType) {
+ if (Empty()) {
+ ValueType_ = valueType;
+ } else {
+ CheckTypes(ValueType_, valueType);
+ }
+ Points_.push_back(point);
+
+ if (ValueType_ == EMetricValueType::SUMMARY) {
+ TPoint& p = Points_.back();
+ p.GetValue().AsSummaryDouble()->Ref();
+ } else if (ValueType_ == EMetricValueType::HISTOGRAM) {
+ TPoint& p = Points_.back();
+ p.GetValue().AsHistogram()->Ref();
+ } else if (ValueType_ == EMetricValueType::LOGHISTOGRAM) {
+ TPoint& p = Points_.back();
+ p.GetValue().AsLogHistogram()->Ref();
+ }
+ }
+
+ void CopyFrom(const TMetricTimeSeries& other) {
+ if (Empty()) {
+ ValueType_ = other.ValueType_;
+ } else {
+ CheckTypes(GetValueType(), other.GetValueType());
+ }
+
+ size_t prevSize = Points_.size();
+ Copy(std::begin(other.Points_), std::end(other.Points_),
+ std::back_inserter(Points_));
+
+ if (ValueType_ == EMetricValueType::HISTOGRAM) {
+ for (size_t i = prevSize; i < Points_.size(); i++) {
+ TPoint& point = Points_[i];
+ point.GetValue().AsHistogram()->Ref();
+ }
+ } else if (ValueType_ == EMetricValueType::SUMMARY) {
+ for (size_t i = prevSize; i < Points_.size(); ++i) {
+ TPoint& point = Points_[i];
+ point.GetValue().AsSummaryDouble()->Ref();
+ }
+ } else if (ValueType_ == EMetricValueType::LOGHISTOGRAM) {
+ for (size_t i = prevSize; i < Points_.size(); ++i) {
+ TPoint& point = Points_[i];
+ point.GetValue().AsLogHistogram()->Ref();
+ }
+ }
+ }
+
+ template <typename TConsumer>
+ void ForEach(TConsumer c) const {
+ for (const auto& point : Points_) {
+ c(point.GetTime(), ValueType_, point.GetValue());
+ }
+ }
+
+ bool Empty() const noexcept {
+ return Points_.empty();
+ }
+
+ size_t Size() const noexcept {
+ return Points_.size();
+ }
+
+ size_t Capacity() const noexcept {
+ return Points_.capacity();
+ }
+
+ const TPoint& operator[](size_t index) const noexcept {
+ return Points_[index];
+ }
+
+ void SortByTs();
+
+ void Clear() noexcept;
+
+ EMetricValueType GetValueType() const noexcept {
+ return ValueType_;
+ }
+
+ private:
+ static void CheckTypes(EMetricValueType t1, EMetricValueType t2) {
+ Y_ENSURE(t1 == t2,
+ "Series type mismatch: expected " << t1 <<
+ ", but got " << t2);
+ }
+
+ private:
+ EMetricValueType ValueType_ = EMetricValueType::UNKNOWN;
+ TVector<TPoint> Points_;
+ };
+
+ template <EMetricValueType valueType, typename TPoint>
+ static inline void SnapshotUnRef(TPoint& point) {
+ if constexpr (valueType == EMetricValueType::HISTOGRAM) {
+ if (auto* hist = point.GetValue().AsHistogram()) {
+ hist->UnRef();
+ }
+ } else if constexpr (valueType == EMetricValueType::SUMMARY) {
+ if (auto* summary = point.GetValue().AsSummaryDouble()) {
+ summary->UnRef();
+ }
+ } else if constexpr (valueType == EMetricValueType::LOGHISTOGRAM) {
+ if (auto* logHist = point.GetValue().AsLogHistogram()) {
+ logHist->UnRef();
+ }
+ }
+ }
+
+ template <EMetricValueType valueType, typename TPoint>
+ static void EraseDuplicates(TVector<TPoint>& points) {
+ // we have to manually clean reference to a snapshot from point
+ // while removing duplicates
+ auto result = points.rbegin();
+ for (auto it = result + 1; it != points.rend(); ++it) {
+ if (result->GetTime() != it->GetTime() && ++result != it) {
+ SnapshotUnRef<valueType>(*result);
+ *result = *it; // (2) copy
+ it->ClearValue(); // (3) clean pointer in the source
+ }
+ }
+
+ // erase tail points
+ for (auto it = result + 1; it != points.rend(); ++it) {
+ SnapshotUnRef<valueType>(*it);
+ }
+ points.erase(points.begin(), (result + 1).base());
+ }
+
+ template <typename TPoint>
+ void SortPointsByTs(EMetricValueType valueType, TVector<TPoint>& points) {
+ if (points.size() < 2) {
+ return;
+ }
+
+ if (valueType != EMetricValueType::HISTOGRAM && valueType != EMetricValueType::SUMMARY
+ && valueType != EMetricValueType::LOGHISTOGRAM) {
+ // Stable sort + saving only the last point inside a group of duplicates
+ StableSortBy(points, NPrivate::POINT_KEY_FN);
+ auto it = UniqueBy(points.rbegin(), points.rend(), NPrivate::POINT_KEY_FN);
+ points.erase(points.begin(), it.base());
+ } else {
+ StableSortBy(points, NPrivate::POINT_KEY_FN);
+ if (valueType == EMetricValueType::HISTOGRAM) {
+ EraseDuplicates<EMetricValueType::HISTOGRAM>(points);
+ } else if (valueType == EMetricValueType::LOGHISTOGRAM) {
+ EraseDuplicates<EMetricValueType::LOGHISTOGRAM>(points);
+ } else {
+ EraseDuplicates<EMetricValueType::SUMMARY>(points);
+ }
+ }
+ }
+}