diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2024-02-21 12:26:45 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2024-02-21 12:40:09 +0300 |
commit | 9837843e3772a15c4fd22e3e1bab1e9097d6c44d (patch) | |
tree | cb7e24231afa892ffa03e82b28b5d8d2688f29d5 | |
parent | a1553506adf1e8b9ad08854f902771b6208142db (diff) | |
download | ydb-9837843e3772a15c4fd22e3e1bab1e9097d6c44d.tar.gz |
Intermediate changes
-rw-r--r-- | yt/yt/library/profiling/sensor.cpp | 27 | ||||
-rw-r--r-- | yt/yt/library/profiling/sensor.h | 5 | ||||
-rw-r--r-- | yt/yt/library/profiling/sensors_owner/README.md | 95 | ||||
-rw-r--r-- | yt/yt/library/profiling/sensors_owner/sensors_owner-inl.h | 102 | ||||
-rw-r--r-- | yt/yt/library/profiling/sensors_owner/sensors_owner.cpp | 181 | ||||
-rw-r--r-- | yt/yt/library/profiling/sensors_owner/sensors_owner.h | 118 | ||||
-rw-r--r-- | yt/yt/library/profiling/sensors_owner/sensors_owner_traits.h | 100 | ||||
-rw-r--r-- | yt/yt/library/profiling/sensors_owner/unittests/sensors_owner_ut.cpp | 178 | ||||
-rw-r--r-- | yt/yt/library/profiling/sensors_owner/unittests/ya.make | 13 | ||||
-rw-r--r-- | yt/yt/library/profiling/sensors_owner/ya.make | 16 | ||||
-rw-r--r-- | yt/yt/library/profiling/testing.cpp | 6 | ||||
-rw-r--r-- | yt/yt/library/profiling/testing.h | 1 | ||||
-rw-r--r-- | yt/yt/library/profiling/ya.make | 1 |
13 files changed, 812 insertions, 31 deletions
diff --git a/yt/yt/library/profiling/sensor.cpp b/yt/yt/library/profiling/sensor.cpp index 138a8ae959..d4299ac8af 100644 --- a/yt/yt/library/profiling/sensor.cpp +++ b/yt/yt/library/profiling/sensor.cpp @@ -234,33 +234,6 @@ void TRateHistogram::Remove(double value, int count) const noexcept Histogram_->Remove(value, count); } -void TRateHistogram::Reset() noexcept -{ - if (!Histogram_) { - return; - } - - Histogram_->Reset(); -} - -THistogramSnapshot TRateHistogram::GetSnapshot() const -{ - if (!Histogram_) { - return {}; - } - - return Histogram_->GetSnapshot(false); -} - -void TRateHistogram::LoadSnapshot(THistogramSnapshot snapshot) -{ - if (!Histogram_) { - return; - } - - Histogram_->LoadSnapshot(snapshot); -} - TRateHistogram::operator bool() const { return Histogram_.operator bool(); diff --git a/yt/yt/library/profiling/sensor.h b/yt/yt/library/profiling/sensor.h index 4d80da1d8d..b1d5662298 100644 --- a/yt/yt/library/profiling/sensor.h +++ b/yt/yt/library/profiling/sensor.h @@ -162,15 +162,12 @@ class TRateHistogram public: void Add(double value, int count = 1) const noexcept; void Remove(double value, int count = 1) const noexcept; - void Reset() noexcept; - - THistogramSnapshot GetSnapshot() const; - void LoadSnapshot(THistogramSnapshot snapshot); explicit operator bool() const; private: friend class TProfiler; + friend struct TTesting; IHistogramImplPtr Histogram_; }; diff --git a/yt/yt/library/profiling/sensors_owner/README.md b/yt/yt/library/profiling/sensors_owner/README.md new file mode 100644 index 0000000000..48e9ed83d9 --- /dev/null +++ b/yt/yt/library/profiling/sensors_owner/README.md @@ -0,0 +1,95 @@ +# Sensors owner + +Это персистентный слой для NYT::NProfiling. TSensorsOwner содержит внутри себя TProfiler и в дополнение +владеет множеством объектов метрик с точки зрения времени их жизни. +TSensorsOwner может владеть другими TSensorsOwner. Имеет апи для получения "дочерних" объектов с метриками. + +## Примеры использования + +* Простейший пример использования: +```cpp +sensorsOwner.Inc(".my_simple_counter", 1); +``` +Когда в конкретном месте нужно проинкрементить всего один счетчик. +Объект счетчика в этом случае создатся один раз и будет храниться внутри sensorsOwner. +Не рекомендуется для более сложных случаев. + +* Инкремент метрик в функции: +```cpp +void DoSmth(/*... , */ const TSensorsOwner& sensorsOwner) +{ + // В функции можно прям по месту объявлять структуру с метриками и пользоваться. + struct TSensors + { + NYT::NProfiling::TProfiler Profiler; + NYT::NProfiling::TCounter TotalCount = Profiler.Counter(".count"); + NYT::NProfiling::TCounter FailedCount = Profiler.Counter(".failed_count"); + }; + // Тут одна и та же ссылка на объект метрик при условии, что в функцию передается один и тот же sensorsOwner. + // Метод `.Get` достаточно эффективен, но всё же лучше не вызывать лишний раз. + const auto& sensors = sensorsOwner.Get<TSensors>(); + + //... + bool failed = false; + //... + + sensors.TotalCount.Increment(1); + if (failed) { + sensors.FailedCount.Increment(1); + } +} +``` + +* Когда очень хочется конструировать дочерние метрики не только от профайлера и ключа: +```cpp +struct THistogramSensors +{ + NYT::NProfiling::TProfiler Profiler; + int Key; + std::vector<TDuration> Buckets; + NYT::NProfiling::TEventTimer Histogram = Profiler.WithTag("tag", ToString(Key)).TimeHistogram(".another_counter", Buckets); +}; + +owner.Get<THistogramSensors>(/*Key*/ 132, /*Buckets*/ std::vector<TDuration>{5s, 10min}).Histogram.Record(6s); +``` + +* Можно и явно написать конструктор для структурки с метриками: +```cpp +struct TChildSensors +{ + NYT::NProfiling::TCounter Counter; + + TChildSensors(const NYT::NProfiling::TProfiler& p) + : Counter(p.Counter(".my_counter_2")) + { } +}; +``` + +* Если структурку с метриками хочется куда-то дальше передавать и не беспокоиться о времени жизни: +```cpp +struct TSharedSensors final +{ + TProfiler Profiler; + TCounter Counter = Profiler.Counter(".under_ptr_counter"); +}; +using TSharedSensorsPtr = NYT::TIntrusivePtr<TSharedSensors>; + +owner.Get<TSharedSensorsPtr>()->Counter.Increment(1); +``` + +* TSensorsOwner мимикрирует под TProfiler в ряде моментов: +```cpp +auto subOwner = owner.WithPrefix("prefix.").WithTags(NYT::NProfiling::TTagSet().WithTag({"key", "value2"})); +``` + +## Когда использовать? + +* При реализации логики на функциях и отсутствии необходимости иметь объекты метрик +(счетчиков и гистограмм как правило) вне функции. + +* В случаях, когда время жизни метрик должно превышать время жизни основного использующего эти метрики класса. +Например, если при возникновении ошибки, вы хотите репортить метрику и разрушать класс, +то вам важно, чтобы объект метрики ошибки не умер сразу - иначе апдейт метрики скорее всего не успеет отрепортиться мониторингу. + +* Когда вы просто не хотите, чтобы объекты метрик когда-либо разрушались. +В этом случае можно подвешивать все к GetRootSensorsOwner(). diff --git a/yt/yt/library/profiling/sensors_owner/sensors_owner-inl.h b/yt/yt/library/profiling/sensors_owner/sensors_owner-inl.h new file mode 100644 index 0000000000..61b6f02b86 --- /dev/null +++ b/yt/yt/library/profiling/sensors_owner/sensors_owner-inl.h @@ -0,0 +1,102 @@ +#ifndef ALLOW_INCLUDE_SENSORS_OWNER_INL_H + #error "Direct inclusion of this file is not allowed, must be included from sensors_owner.h only! Include sensors_owner.h" +#endif + +namespace NYT::NProfiling { + +//////////////////////////////////////////////////////////////////////////////// + +namespace NSensorsOwnerPrivate { + +//////////////////////////////////////////////////////////////////////////////// + +struct TTagSetKey +{ + TTagSet Tags; + + operator ui64() const; + bool operator==(const TTagSetKey& key) const; +}; + +template <typename TMap> +struct TOwnedMapWrapper +{ + mutable TMap Map; + + TOwnedMapWrapper(const TProfiler&) + { } +}; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NSensorsOwnerPrivate + +template <typename TChild, typename TFindKey, typename... TExtraConstructionArgs> +const TChild& TSensorsOwner::Get(const TFindKey& key, const TExtraConstructionArgs&... extraArgs) const +{ + constexpr bool childHasKey = NSensorsOwnerPrivate::TChildTraits<TChild>::HasKey; + + auto childConstructor = [&]<typename... TArgs>(TArgs&&... args) { + return NSensorsOwnerPrivate::TChildTraits<TChild>::Create(State_->Profiler, std::forward<TArgs>(args)..., extraArgs...); + }; + + if constexpr (std::is_same_v<TFindKey, std::monostate>) { + static_assert(!childHasKey); + + struct TWrapper : public TRefCounted + { + TChild Child; + + TWrapper(decltype(childConstructor)& childConstructor_) + : Child(childConstructor_()) + { } + }; + + auto* wrapperPtr = State_->Children + .FindOrInsert( + std::type_index(typeid(TChild)), + [&] { + return New<TWrapper>(childConstructor); + }) + .first->Get(); + return static_cast<TWrapper*>(wrapperPtr)->Child; + } else { + using TChildKey = typename NSensorsOwnerPrivate::TChildTraits<TChild>::TKey; + using TMap = NConcurrency::TSyncMap<TChildKey, TChild>; + + static_assert(childHasKey); + static_assert(!std::is_same_v<TChildKey, TTagSet>, "Use GetWithTags() method"); + + auto& childMap = Get<NSensorsOwnerPrivate::TOwnedMapWrapper<TMap>>().Map; + + return *childMap + .FindOrInsert( + key, + [&] { + return childConstructor(TChildKey{key}); + }) + .first; + } +} + +template <typename TChild> + requires(!NSensorsOwnerPrivate::TChildTraits<TChild>::HasKey) +const TChild& TSensorsOwner::GetWithTags(const TTagSet& tags) const +{ + struct TSensors + { + using TKey = NSensorsOwnerPrivate::TTagSetKey; + + TChild Child; + + TSensors(const TProfiler& profiler, const NSensorsOwnerPrivate::TTagSetKey& key) + : Child{profiler.WithTags(key.Tags)} + { } + }; + + return Get<TSensors>(NSensorsOwnerPrivate::TTagSetKey{tags}).Child; +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NProfiling diff --git a/yt/yt/library/profiling/sensors_owner/sensors_owner.cpp b/yt/yt/library/profiling/sensors_owner/sensors_owner.cpp new file mode 100644 index 0000000000..19a5c238a7 --- /dev/null +++ b/yt/yt/library/profiling/sensors_owner/sensors_owner.cpp @@ -0,0 +1,181 @@ +#include "sensors_owner.h" + +#include <util/digest/sequence.h> + +namespace NYT::NProfiling { + +//////////////////////////////////////////////////////////////////////////////// + +namespace NSensorsOwnerPrivate { + +//////////////////////////////////////////////////////////////////////////////// + +TTagSetKey::operator ui64() const +{ + return TRangeHash<>{}(Tags.Tags()); +} + +bool TTagSetKey::operator==(const TTagSetKey& key) const +{ + return Tags.Tags() == key.Tags.Tags(); +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NSensorsOwnerPrivate + +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +template <typename TSensor, typename... TArgs> +struct TSensorWrapper +{ + template <TSensor (TProfiler::*Getter)(const TString&, TArgs...) const> + struct TImpl + { + using TKey = TString; + + TImpl(const TProfiler& profiler, const TString& key, TArgs... args) + : Sensor((profiler.*Getter)(key, std::move(args)...)) + { } + + TSensor Sensor; + }; +}; + +using TCounterWrapper = TSensorWrapper<TCounter>::template TImpl<&TProfiler::Counter>; +using TGaugeWrapper = TSensorWrapper<TGauge>::template TImpl<&TProfiler::Gauge>; + +template <typename... Args> +using TTimeHistogramWrapper = typename TSensorWrapper<TEventTimer, Args...>::template TImpl<&TProfiler::TimeHistogram>; +template <typename... Args> +using TGaugeHistogramWrapper = typename TSensorWrapper<TGaugeHistogram, Args...>::template TImpl<&TProfiler::GaugeHistogram>; +template <typename... Args> +using TRateHistogramWrapper = typename TSensorWrapper<TRateHistogram, Args...>::template TImpl<&TProfiler::RateHistogram>; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace + +TSensorsOwner::TSensorsOwner() + : State_(GetDefaultState()) +{ } + +TSensorsOwner::TSensorsOwner(const TProfiler& profiler) + : State_(New<TState>(profiler)) +{ } + +const TProfiler& TSensorsOwner::GetProfiler() const +{ + return State_->Profiler; +} + +TSensorsOwner::TState::TState(const TProfiler& profiler) + : Profiler(profiler) +{ } + +const TSensorsOwner& TSensorsOwner::WithTags(const TTagSet& tags) const +{ + struct TChild + { + TSensorsOwner SensorsOwner; + + TChild(const TProfiler& profiler) + : SensorsOwner(profiler) + { } + }; + + return GetWithTags<TChild>(tags).SensorsOwner; +} + +const TSensorsOwner& TSensorsOwner::WithTag(const TString& name, const TString& value) const +{ + return WithTags(TTagSet().WithTag({name, value})); +} + +const TSensorsOwner& TSensorsOwner::WithRequiredTag(const TString& name, const TString& value) const +{ + return WithTags(TTagSet().WithRequiredTag({name, value})); +} + +const TSensorsOwner& TSensorsOwner::WithExcludedTag(const TString& name, const TString& value) const +{ + return WithTags(TTagSet().WithExcludedTag({name, value})); +} + +const TSensorsOwner& TSensorsOwner::WithAlternativeTag(const TString& name, const TString& value, int alternativeTo) const +{ + return WithTags(TTagSet().WithAlternativeTag({name, value}, alternativeTo)); +} + +const TSensorsOwner& TSensorsOwner::WithPrefix(const TString& prefix) const +{ + struct TChild + { + using TKey = TString; + + TSensorsOwner SensorsOwner; + + TChild(const TProfiler& profiler, const TString& prefix) + : SensorsOwner(profiler.WithPrefix(prefix)) + { } + }; + + return Get<TChild>(prefix).SensorsOwner; +} + +const TCounter& TSensorsOwner::GetCounter(TStringBuf name) const +{ + return Get<TCounterWrapper>(name).Sensor; +} + +const TGauge& TSensorsOwner::GetGauge(TStringBuf name) const +{ + return Get<TGaugeWrapper>(name).Sensor; +} + +const TEventTimer& TSensorsOwner::GetTimeHistogram(TStringBuf name, std::vector<TDuration> bounds) const +{ + return Get<TTimeHistogramWrapper<std::vector<TDuration>>>(name, std::move(bounds)).Sensor; +} + +const TEventTimer& TSensorsOwner::GetTimeHistogram(TStringBuf name, TDuration min, TDuration max) const +{ + return Get<TTimeHistogramWrapper<TDuration, TDuration>>(name, min, max).Sensor; +} + +const TGaugeHistogram& TSensorsOwner::GetGaugeHistogram(TStringBuf name, std::vector<double> buckets) const +{ + return Get<TGaugeHistogramWrapper<std::vector<double>>>(name, std::move(buckets)).Sensor; +} + +const TRateHistogram& TSensorsOwner::GetRateHistogram(TStringBuf name, std::vector<double> buckets) const +{ + return Get<TRateHistogramWrapper<std::vector<double>>>(name, std::move(buckets)).Sensor; +} + +void TSensorsOwner::Inc(TStringBuf name, i64 delta) const +{ + GetCounter(name).Increment(delta); +} + +TIntrusivePtr<TSensorsOwner::TState> TSensorsOwner::GetDefaultState() +{ + static auto state = New<TState>(TProfiler()); + return state; +} + +const TSensorsOwner& GetRootSensorsOwner() +{ + struct TLocalType + { + TSensorsOwner SensorsOwner{TProfiler("", "")}; + }; + + return Singleton<TLocalType>()->SensorsOwner; +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NProfiling diff --git a/yt/yt/library/profiling/sensors_owner/sensors_owner.h b/yt/yt/library/profiling/sensors_owner/sensors_owner.h new file mode 100644 index 0000000000..d6b5467b0d --- /dev/null +++ b/yt/yt/library/profiling/sensors_owner/sensors_owner.h @@ -0,0 +1,118 @@ +#pragma once + +#include "sensors_owner_traits.h" + +#include <yt/yt/library/profiling/sensor.h> +#include <yt/yt/library/profiling/tag.h> + +#include <yt/yt/library/syncmap/map.h> + +namespace NYT::NProfiling { + +//////////////////////////////////////////////////////////////////////////////// + +//! Class that can own metrics of different types. +/*! + * What does 'own' means? + * YT profiler metric is reported only while the corresponding metric object is alive. + * So if you increment YT counter and destroy its object, you lost this increment. + * This class helps with storing metrics in long-living storage. + * + * You can find examples in unittests. + */ +class TSensorsOwner +{ +public: + //! Returns no-op sensors owner, should be singleton to avoid memory leaks. + TSensorsOwner(); + explicit TSensorsOwner(const TProfiler& profiler); + + //! Gets owned struct of type TChild + /*! + * If std::is_same<TFindKey, std::monostate> + * TChild is constructed as TChild{Profiler, extraArgs...]}. + * Result of &Get<TChild>() is always the same with fixed *this, and TChild. + * TChild must not contain nested type TKey or member Key. + * Else + * TChild is constructed as TChild{Profiler, TChildKey{key}, extraArgs...]}. + * Result of &Get<TChild>(key) is always the same with fixed *this, key and TChild. + * TChild must contain nested type TKey or member Key, TChildKey is determined by them. + */ + template <typename TChild, typename TFindKey = std::monostate, typename... TExtraConstructionArgs> + const TChild& Get(const TFindKey& key = {}, const TExtraConstructionArgs&... extraArgs) const; + + //! Gets owned struct of type TChild. + /*! + * Result of &GetWithTags<TChild>(tags) is always the same with fixed *this, TChild and tags.Tags(). + * TChild is constructed as TChild{Profiler.WithTags(tags)}. + */ + template <typename TChild> + requires(!NSensorsOwnerPrivate::TChildTraits<TChild>::HasKey) + const TChild& GetWithTags(const TTagSet& tags) const; + + //! Gets owned TSensorsOwner with profiler=Profiler.WithTags(...). + //! Result of WithTags(tags) is always the same with fixed *this and tags.Tags(). + const TSensorsOwner& WithTags(const TTagSet& tags) const; + const TSensorsOwner& WithTag(const TString& name, const TString& value) const; + const TSensorsOwner& WithRequiredTag(const TString& name, const TString& value) const; + const TSensorsOwner& WithExcludedTag(const TString& name, const TString& value) const; + const TSensorsOwner& WithAlternativeTag(const TString& name, const TString& value, int alternativeTo) const; + + //! Gets owned TSensorsOwner with profiler=Profiler.WithPrefix(...). + //! Result of WithPrefix(prefix) is always the same with fixed *this and prefix. + const TSensorsOwner& WithPrefix(const TString& prefix) const; + + const TProfiler& GetProfiler() const; + + /*! + * Note that it is generally better to have a structure storing all the sensors + * you need and access it through the Get method. Avoid using methods below + * unless you only need a single sensor and lookup by your key is not + * cheaper than by TString, or you don't care about performance. + */ + + //! Gets owned counter with given metric suffix. + const TCounter& GetCounter(TStringBuf name) const; + + //! ~ .Counter(str).Increment(delta) + void Inc(TStringBuf name, i64 delta) const; + + //! Gets owned gauge with given metric suffix. + const TGauge& GetGauge(TStringBuf name) const; + + //! Gets owned TimeHistogram with given metric suffix using bounds as a constructor argument. + const TEventTimer& GetTimeHistogram(TStringBuf name, std::vector<TDuration> bounds) const; + + //! Gets owned TimeHistogram with given metric suffix using min/max as a constructor arguments. + const TEventTimer& GetTimeHistogram(TStringBuf name, TDuration min, TDuration max) const; + + //! Gets owned GaugeHistogram with given metric suffix using buckets as a constructor. + const TGaugeHistogram& GetGaugeHistogram(TStringBuf name, std::vector<double> buckets) const; + + //! Gets owned RateHistogram with given metric suffix using buckets as a constructor. + const TRateHistogram& GetRateHistogram(TStringBuf name, std::vector<double> buckets) const; + +private: + struct TState final + { + TProfiler Profiler; + NConcurrency::TSyncMap<std::type_index, TRefCountedPtr> Children; + + explicit TState(const TProfiler& profiler); + }; + + TIntrusivePtr<TState> State_; + + static TIntrusivePtr<TState> GetDefaultState(); +}; + +// Root sensors owner to create others from. Has empty prefix. +const TSensorsOwner& GetRootSensorsOwner(); + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NProfiling + +#define ALLOW_INCLUDE_SENSORS_OWNER_INL_H +#include "sensors_owner-inl.h" +#undef ALLOW_INCLUDE_SENSORS_OWNER_INL_H diff --git a/yt/yt/library/profiling/sensors_owner/sensors_owner_traits.h b/yt/yt/library/profiling/sensors_owner/sensors_owner_traits.h new file mode 100644 index 0000000000..504d94dee3 --- /dev/null +++ b/yt/yt/library/profiling/sensors_owner/sensors_owner_traits.h @@ -0,0 +1,100 @@ +#pragma once + +// Public API is in sensors_owner.h + +#include <library/cpp/yt/memory/new.h> + +#include <util/generic/ptr.h> + +namespace NYT::NProfiling::NSensorsOwnerPrivate { + +//////////////////////////////////////////////////////////////////////////////// + +template <class TChild> +struct TChildTypeTraits +{ + using TValue = TChild; + + template <typename... TArgs> + static TChild Create(TArgs&&... args) + { + return TChild{std::forward<TArgs>(args)...}; + } +}; + +template <class TValue_> +struct TChildTypeTraits<TIntrusivePtr<TValue_>> +{ + using TValue = TValue_; + + template <typename... TArgs> + static TIntrusivePtr<TValue> Create(TArgs&&... args) + { + return New<TValue>(std::forward<TArgs>(args)...); + } +}; + +template <typename TValue_, typename TPtr> +struct TChildPtrTypeTraits +{ + using TValue = TValue_; + + template <typename... TArgs> + static TPtr Create(TArgs&&... args) + { + return TPtr{new TValue{std::forward<TArgs>(args)...}}; + } +}; + +template <class TValue> +struct TChildTypeTraits<TAtomicSharedPtr<TValue>> + : public TChildPtrTypeTraits<TValue, TAtomicSharedPtr<TValue>> +{ }; + +template <class TValue> +struct TChildTypeTraits<std::shared_ptr<TValue>> + : public TChildPtrTypeTraits<TValue, std::shared_ptr<TValue>> +{ }; + +template <class TChild> +concept CHasKeyField = requires(TChild c) { + c.Key; +}; +template <class TChild> +concept CHasKeyAlias = requires { + typename TChild::TKey; +}; + +template <class TValue> +struct TKeyTraits +{ + static constexpr bool HasKey = false; +}; + +template <CHasKeyField TValue> +struct TKeyTraits<TValue> +{ + using TKey = std::decay_t<decltype(std::declval<TValue>().Key)>; + + static constexpr bool HasKey = true; +}; + +template <CHasKeyAlias TValue> +struct TKeyTraits<TValue> +{ + using TKey = typename TValue::TKey; + + static constexpr bool HasKey = true; +}; + +template <class TChild> +struct TChildTraits + : public TChildTypeTraits<TChild> + , public TKeyTraits<typename TChildTypeTraits<TChild>::TValue> +{ + static_assert(std::is_same_v<TChild, std::decay_t<TChild>>); +}; + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace NYT::NProfiling::NSensorsOwnerPrivate diff --git a/yt/yt/library/profiling/sensors_owner/unittests/sensors_owner_ut.cpp b/yt/yt/library/profiling/sensors_owner/unittests/sensors_owner_ut.cpp new file mode 100644 index 0000000000..a1f101bb84 --- /dev/null +++ b/yt/yt/library/profiling/sensors_owner/unittests/sensors_owner_ut.cpp @@ -0,0 +1,178 @@ +#include <yt/yt/library/profiling/sensors_owner/sensors_owner.h> + +#include <library/cpp/testing/gtest/gtest.h> + +using namespace std::literals; + +namespace NYT::NProfiling { +namespace { + +//////////////////////////////////////////////////////////////////////////////// + +TEST(TSensorsOwnerTest, Example) +{ + TProfiler profiler("", "bigrt.test"); + auto owner = TSensorsOwner(profiler); + + struct TSensors + { + TProfiler Profiler; + TCounter Counter = Profiler.Counter(".my_counter"); + TSensorsOwner OtherSensors{Profiler}; + }; + + struct TChildSensors + { + TCounter Counter; + + TChildSensors(const TProfiler& p) + : Counter(p.Counter(".my_counter_2")) + { } + }; + + struct TAnotherSensors + { + TProfiler Profiler; + int Key; + TCounter Counter = Profiler.WithTag("counter", ToString(Key)).Counter(".another_counter"); + }; + + struct TWithTagsSensors + { + TProfiler Profiler; + TCounter Counter = Profiler.Counter(".by_tags_counter"); + }; + + struct TSharedSensors final + { + TProfiler Profiler; + TCounter Counter = Profiler.Counter(".under_ptr_counter"); + }; + + using TSharedSensorsPtr = NYT::TIntrusivePtr<TSharedSensors>; + + owner.Inc(".my_simple_counter", 1); + owner.Get<TSensors>().OtherSensors.Get<TChildSensors>().Counter.Increment(1); + owner.Get<TSharedSensorsPtr>()->Counter.Increment(1); + owner.Get<TAnotherSensors>(42).Counter.Increment(1); + owner.WithPrefix(".prefix").Get<TAnotherSensors>(42).Counter.Increment(1); + owner.GetWithTags<TWithTagsSensors>(TTagSet().WithTag({"key", "value"})).Counter.Increment(1); + owner.WithTags(TTagSet().WithTag({"key", "value2"})).Get<TWithTagsSensors>().Counter.Increment(1); + + struct THistogramSensors + { + TProfiler Profiler; + int Key; + std::vector<TDuration> Buckets; + TEventTimer Histogram = Profiler.WithTag("tag", ToString(Key)).TimeHistogram(".another_counter", Buckets); + }; + + owner.Get<THistogramSensors>(/*Key*/ 132, /*Buckets*/ std::vector<TDuration>{5s, 10min}).Histogram.Record(6s); +} + +void DoSmth(/*... , */ const TSensorsOwner& sensorsOwner) +{ + // В функции можно прям по месту объявлять структуру с метриками и пользоваться. + struct TSensors + { + TProfiler Profiler; + TCounter TotalCount = Profiler.Counter(".count"); + TCounter FailedCount = Profiler.Counter(".failed_count"); + }; + + // Тут одна и та же ссылка на объект метрик при условии, что в функцию передается один и тот же sensorsOwner. + // Метод `.Get` достаточно эффективен, но всё же лучше не вызывать лишний раз. + const auto& sensors = sensorsOwner.Get<TSensors>(); + + //... + bool failed = false; + //... + + sensors.TotalCount.Increment(1); + if (failed) { + sensors.FailedCount.Increment(1); + } +} + +TEST(TSensorsOwnerTest, Simple) +{ + TProfiler profiler("", "bigrt.test"); + auto registryPtr = profiler.GetRegistry(); + auto owner = TSensorsOwner(profiler); + + DoSmth(owner); + + ASSERT_EQ(registryPtr, owner.GetProfiler().GetRegistry()); // Equal profilers. + + struct TChild1 + { + TProfiler Profiler; + int A = 1; + }; + + ASSERT_EQ(registryPtr, owner.Get<TChild1>().Profiler.GetRegistry()); + ASSERT_EQ(1, owner.Get<TChild1>().A); + ASSERT_EQ(owner.Get<TChild1>().Profiler.GetRegistry(), owner.Get<TChild1>().Profiler.GetRegistry()); + ASSERT_EQ(&owner.Get<TChild1>(), &owner.Get<TChild1>()); + + struct TChild2 + { + TProfiler Profiler; + int B = 2; + + TChild2(const TProfiler& p) + : Profiler(p) + { + } + }; + + ASSERT_EQ(registryPtr, owner.Get<TChild2>().Profiler.GetRegistry()); + ASSERT_EQ(2, owner.Get<TChild2>().B); + ASSERT_EQ(owner.Get<TChild2>().Profiler.GetRegistry(), owner.Get<TChild2>().Profiler.GetRegistry()); + + struct TSensorsByKey + { + TProfiler Profiler; + int Key; + TCounter Counter = Profiler.WithTag("key", ToString(Key)).Counter(".by_key_counter"); + }; + + ASSERT_EQ(42, (owner.Get<TSensorsByKey>(42).Key)); + ASSERT_EQ(43, (owner.Get<TSensorsByKey>(43).Key)); + + struct TWithTagsSensors + { + TProfiler Profiler; + TCounter Counter = Profiler.Counter(".by_tags_counter"); + }; + + ASSERT_EQ( + &owner.GetWithTags<TWithTagsSensors>(TTagSet().WithTag({"key", "value"})), + &owner.GetWithTags<TWithTagsSensors>(TTagSet().WithTag({"key", "value"}))); + + ASSERT_EQ( + &owner.WithTags(TTagSet().WithTag({"key", "value2"})).Get<TWithTagsSensors>(), + &owner.WithTags(TTagSet().WithTag({"key", "value2"})).Get<TWithTagsSensors>()); + + ASSERT_EQ( + &owner.WithPrefix(".prefix").Get<TChild1>(), + &owner.WithPrefix(".prefix").Get<TChild1>()); +} + +TEST(TSensorsOwnerTest, Copy) +{ + auto owner = TSensorsOwner(TProfiler("", "bigrt.test")); + auto owner2 = owner; + + struct TChild + { + TProfiler Profiler; + }; + + ASSERT_EQ(&owner.Get<TChild>(), &owner2.Get<TChild>()); // The same owner. +} + +//////////////////////////////////////////////////////////////////////////////// + +} // namespace +} // namespace NYT::NProfiling diff --git a/yt/yt/library/profiling/sensors_owner/unittests/ya.make b/yt/yt/library/profiling/sensors_owner/unittests/ya.make new file mode 100644 index 0000000000..ccb22b251d --- /dev/null +++ b/yt/yt/library/profiling/sensors_owner/unittests/ya.make @@ -0,0 +1,13 @@ +GTEST() + +INCLUDE(${ARCADIA_ROOT}/yt/ya_cpp.make.inc) + +SRCS( + sensors_owner_ut.cpp +) + +PEERDIR( + yt/yt/library/profiling/sensors_owner +) + +END() diff --git a/yt/yt/library/profiling/sensors_owner/ya.make b/yt/yt/library/profiling/sensors_owner/ya.make new file mode 100644 index 0000000000..e2a01ea0d0 --- /dev/null +++ b/yt/yt/library/profiling/sensors_owner/ya.make @@ -0,0 +1,16 @@ +LIBRARY() + +INCLUDE(${ARCADIA_ROOT}/yt/ya_cpp.make.inc) + +SRCS( + sensors_owner.cpp +) + +PEERDIR( + yt/yt/core + yt/yt/library/profiling +) + +END() + +RECURSE_FOR_TESTS(unittests) diff --git a/yt/yt/library/profiling/testing.cpp b/yt/yt/library/profiling/testing.cpp index 22276ac006..b8989328a4 100644 --- a/yt/yt/library/profiling/testing.cpp +++ b/yt/yt/library/profiling/testing.cpp @@ -32,6 +32,12 @@ TDuration TTesting::ReadTimeCounter(const TTimeCounter& counter) return counter.Counter_->GetValue(); } +THistogramSnapshot TTesting::ReadRateHistogram(const TRateHistogram& histogram) +{ + Y_ENSURE(histogram.Histogram_, "Histogram is not registered"); + return histogram.Histogram_->GetSnapshot(false); +} + const TSensorOptions& TTesting::ReadOptions(const TProfiler& profiler) { return profiler.Options_; } diff --git a/yt/yt/library/profiling/testing.h b/yt/yt/library/profiling/testing.h index 116706a951..62fac31591 100644 --- a/yt/yt/library/profiling/testing.h +++ b/yt/yt/library/profiling/testing.h @@ -12,6 +12,7 @@ struct TTesting static TDuration ReadTimeGauge(const TTimeGauge& gauge); static i64 ReadCounter(const TCounter& counter); static TDuration ReadTimeCounter(const TTimeCounter& counter); + static THistogramSnapshot ReadRateHistogram(const TRateHistogram& histogram); static const TSensorOptions& ReadOptions(const TProfiler& profiler); }; diff --git a/yt/yt/library/profiling/ya.make b/yt/yt/library/profiling/ya.make index 6e2e626564..c59ab7d643 100644 --- a/yt/yt/library/profiling/ya.make +++ b/yt/yt/library/profiling/ya.make @@ -22,6 +22,7 @@ PEERDIR( END() RECURSE( + sensors_owner solomon unittests example |