aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-02-21 12:26:45 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-02-21 12:40:09 +0300
commit9837843e3772a15c4fd22e3e1bab1e9097d6c44d (patch)
treecb7e24231afa892ffa03e82b28b5d8d2688f29d5
parenta1553506adf1e8b9ad08854f902771b6208142db (diff)
downloadydb-9837843e3772a15c4fd22e3e1bab1e9097d6c44d.tar.gz
Intermediate changes
-rw-r--r--yt/yt/library/profiling/sensor.cpp27
-rw-r--r--yt/yt/library/profiling/sensor.h5
-rw-r--r--yt/yt/library/profiling/sensors_owner/README.md95
-rw-r--r--yt/yt/library/profiling/sensors_owner/sensors_owner-inl.h102
-rw-r--r--yt/yt/library/profiling/sensors_owner/sensors_owner.cpp181
-rw-r--r--yt/yt/library/profiling/sensors_owner/sensors_owner.h118
-rw-r--r--yt/yt/library/profiling/sensors_owner/sensors_owner_traits.h100
-rw-r--r--yt/yt/library/profiling/sensors_owner/unittests/sensors_owner_ut.cpp178
-rw-r--r--yt/yt/library/profiling/sensors_owner/unittests/ya.make13
-rw-r--r--yt/yt/library/profiling/sensors_owner/ya.make16
-rw-r--r--yt/yt/library/profiling/testing.cpp6
-rw-r--r--yt/yt/library/profiling/testing.h1
-rw-r--r--yt/yt/library/profiling/ya.make1
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