123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- #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_ABORT(); // 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_ABORT(); // 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_ABORT(); // 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);
- }
- }
- }
- }
|