metric_value.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. #pragma once
  2. #include "histogram_collector.h"
  3. #include "metric_value_type.h"
  4. #include "summary_collector.h"
  5. #include "log_histogram_snapshot.h"
  6. #include <util/datetime/base.h>
  7. #include <util/generic/algorithm.h>
  8. #include <util/generic/vector.h>
  9. #include <util/generic/cast.h>
  10. #include <util/generic/ymath.h>
  11. namespace NMonitoring {
  12. namespace NPrivate {
  13. template <typename T>
  14. T FromFloatSafe(double d) {
  15. static_assert(std::is_integral<T>::value, "this function only converts floats to integers");
  16. Y_ENSURE(::IsValidFloat(d) && d >= Min<T>() && d <= MaxFloor<T>(), "Cannot convert " << d << " to an integer value");
  17. return static_cast<T>(d);
  18. }
  19. inline auto POINT_KEY_FN = [](auto& p) {
  20. return p.GetTime();
  21. };
  22. } // namespace NPrivate
  23. template <typename T, typename Enable = void>
  24. struct TValueType;
  25. template <>
  26. struct TValueType<double> {
  27. static constexpr auto Type = EMetricValueType::DOUBLE;
  28. };
  29. template <>
  30. struct TValueType<i64> {
  31. static constexpr auto Type = EMetricValueType::INT64;
  32. };
  33. template <>
  34. struct TValueType<ui64> {
  35. static constexpr auto Type = EMetricValueType::UINT64;
  36. };
  37. template <>
  38. struct TValueType<TLogHistogramSnapshot*> {
  39. static constexpr auto Type = EMetricValueType::LOGHISTOGRAM;
  40. };
  41. template <typename T>
  42. struct TValueType<T*, typename std::enable_if_t<std::is_base_of<IHistogramSnapshot, T>::value>> {
  43. static constexpr auto Type = EMetricValueType::HISTOGRAM;
  44. };
  45. template <typename T>
  46. struct TValueType<T*, typename std::enable_if_t<std::is_base_of<ISummaryDoubleSnapshot, T>::value>> {
  47. static constexpr auto Type = EMetricValueType::SUMMARY;
  48. };
  49. ///////////////////////////////////////////////////////////////////////////
  50. // TMetricValue
  51. ///////////////////////////////////////////////////////////////////////////
  52. // TMetricValue represents a generic value. It does not contain type
  53. // information about a value. This is done to minimize object footprint.
  54. // To read an actual value from the object the type must be checked
  55. // first or provided to AsXxxx(type) member-functions.
  56. // This class does not hold an ownership of an IHistogramSnapshot or
  57. // SummarySnapshot, so this must be done somewhere outside.
  58. class TMetricValue {
  59. public:
  60. TMetricValue() noexcept {
  61. Value_.Uint64 = 0;
  62. }
  63. explicit TMetricValue(double value) noexcept {
  64. Value_.Double = value;
  65. }
  66. explicit TMetricValue(i64 value) noexcept {
  67. Value_.Int64 = value;
  68. }
  69. explicit TMetricValue(ui64 value) noexcept {
  70. Value_.Uint64 = value;
  71. }
  72. explicit TMetricValue(IHistogramSnapshot* histogram) noexcept {
  73. Value_.Histogram = histogram;
  74. }
  75. explicit TMetricValue(ISummaryDoubleSnapshot* summary) noexcept {
  76. Value_.Summary = summary;
  77. }
  78. explicit TMetricValue(TLogHistogramSnapshot* logHist) noexcept {
  79. Value_.LogHistogram = logHist;
  80. }
  81. double AsDouble() const noexcept {
  82. return Value_.Double;
  83. }
  84. // will cast value into double, current value type is determined by
  85. // the given type argument
  86. double AsDouble(EMetricValueType type) const {
  87. switch (type) {
  88. case EMetricValueType::DOUBLE:
  89. return Value_.Double;
  90. case EMetricValueType::INT64:
  91. return static_cast<double>(Value_.Int64);
  92. case EMetricValueType::UINT64:
  93. return static_cast<double>(Value_.Uint64);
  94. case EMetricValueType::HISTOGRAM:
  95. ythrow yexception() << "histogram cannot be casted to Double";
  96. case EMetricValueType::SUMMARY:
  97. ythrow yexception() << "summary cannot be casted to Double";
  98. case EMetricValueType::LOGHISTOGRAM:
  99. ythrow yexception() << "loghistogram cannot be casted to Double";
  100. case EMetricValueType::UNKNOWN:
  101. ythrow yexception() << "unknown value type";
  102. }
  103. Y_ABORT(); // for GCC
  104. }
  105. ui64 AsUint64() const noexcept {
  106. return Value_.Uint64;
  107. }
  108. // will cast value into uint64, current value's type is determined by
  109. // the given type argument
  110. ui64 AsUint64(EMetricValueType type) const {
  111. switch (type) {
  112. case EMetricValueType::DOUBLE:
  113. return NPrivate::FromFloatSafe<ui64>(Value_.Double);
  114. case EMetricValueType::INT64:
  115. return SafeIntegerCast<ui64>(Value_.Int64);
  116. case EMetricValueType::UINT64:
  117. return Value_.Uint64;
  118. case EMetricValueType::HISTOGRAM:
  119. ythrow yexception() << "histogram cannot be casted to Uint64";
  120. case EMetricValueType::SUMMARY:
  121. ythrow yexception() << "summary cannot be casted to Uint64";
  122. case EMetricValueType::LOGHISTOGRAM:
  123. ythrow yexception() << "loghistogram cannot be casted to Uint64";
  124. case EMetricValueType::UNKNOWN:
  125. ythrow yexception() << "unknown value type";
  126. }
  127. Y_ABORT(); // for GCC
  128. }
  129. i64 AsInt64() const noexcept {
  130. return Value_.Int64;
  131. }
  132. // will cast value into int64, current value's type is determined by
  133. // the given type argument
  134. i64 AsInt64(EMetricValueType type) const {
  135. switch (type) {
  136. case EMetricValueType::DOUBLE:
  137. return NPrivate::FromFloatSafe<i64>(Value_.Double);
  138. case EMetricValueType::INT64:
  139. return Value_.Int64;
  140. case EMetricValueType::UINT64:
  141. return SafeIntegerCast<i64>(Value_.Uint64);
  142. case EMetricValueType::HISTOGRAM:
  143. ythrow yexception() << "histogram cannot be casted to Int64";
  144. case EMetricValueType::SUMMARY:
  145. ythrow yexception() << "summary cannot be casted to Int64";
  146. case EMetricValueType::LOGHISTOGRAM:
  147. ythrow yexception() << "loghistogram cannot be casted to Int64";
  148. case EMetricValueType::UNKNOWN:
  149. ythrow yexception() << "unknown value type";
  150. }
  151. Y_ABORT(); // for GCC
  152. }
  153. IHistogramSnapshot* AsHistogram() const noexcept {
  154. return Value_.Histogram;
  155. }
  156. IHistogramSnapshot* AsHistogram(EMetricValueType type) const {
  157. if (type != EMetricValueType::HISTOGRAM) {
  158. ythrow yexception() << type << " cannot be casted to Histogram";
  159. }
  160. return Value_.Histogram;
  161. }
  162. ISummaryDoubleSnapshot* AsSummaryDouble() const noexcept {
  163. return Value_.Summary;
  164. }
  165. ISummaryDoubleSnapshot* AsSummaryDouble(EMetricValueType type) const {
  166. if (type != EMetricValueType::SUMMARY) {
  167. ythrow yexception() << type << " cannot be casted to SummaryDouble";
  168. }
  169. return Value_.Summary;
  170. }
  171. TLogHistogramSnapshot* AsLogHistogram() const noexcept {
  172. return Value_.LogHistogram;
  173. }
  174. TLogHistogramSnapshot* AsLogHistogram(EMetricValueType type) const {
  175. if (type != EMetricValueType::LOGHISTOGRAM) {
  176. ythrow yexception() << type << " cannot be casted to LogHistogram";
  177. }
  178. return Value_.LogHistogram;
  179. }
  180. protected:
  181. union {
  182. double Double;
  183. i64 Int64;
  184. ui64 Uint64;
  185. IHistogramSnapshot* Histogram;
  186. ISummaryDoubleSnapshot* Summary;
  187. TLogHistogramSnapshot* LogHistogram;
  188. } Value_;
  189. };
  190. ///////////////////////////////////////////////////////////////////////////
  191. // TMetricValueWithType
  192. ///////////////////////////////////////////////////////////////////////////
  193. // Same as TMetricValue, but this type holds an ownership of
  194. // snapshots and contains value type information.
  195. class TMetricValueWithType: private TMetricValue, public TMoveOnly {
  196. public:
  197. using TBase = TMetricValue;
  198. template <typename T>
  199. explicit TMetricValueWithType(T value)
  200. : TBase(value)
  201. , ValueType_{TValueType<T>::Type}
  202. {
  203. Ref();
  204. }
  205. TMetricValueWithType(TMetricValueWithType&& other)
  206. : TBase(std::move(other))
  207. , ValueType_{other.ValueType_}
  208. {
  209. Ref();
  210. other.Clear();
  211. }
  212. TMetricValueWithType& operator=(TMetricValueWithType&& other) {
  213. TBase::operator=(other);
  214. ValueType_ = other.ValueType_;
  215. Ref();
  216. other.Clear();
  217. return *this;
  218. }
  219. ~TMetricValueWithType() {
  220. UnRef();
  221. }
  222. void Clear() {
  223. UnRef();
  224. ValueType_ = EMetricValueType::UNKNOWN;
  225. }
  226. EMetricValueType GetType() const noexcept {
  227. return ValueType_;
  228. }
  229. double AsDouble() const {
  230. return TBase::AsDouble(ValueType_);
  231. }
  232. ui64 AsUint64() const {
  233. return TBase::AsUint64(ValueType_);
  234. }
  235. i64 AsInt64() const {
  236. return TBase::AsInt64(ValueType_);
  237. }
  238. IHistogramSnapshot* AsHistogram() const {
  239. return TBase::AsHistogram(ValueType_);
  240. }
  241. ISummaryDoubleSnapshot* AsSummaryDouble() const {
  242. return TBase::AsSummaryDouble(ValueType_);
  243. }
  244. TLogHistogramSnapshot* AsLogHistogram() const {
  245. return TBase::AsLogHistogram(ValueType_);
  246. }
  247. private:
  248. void Ref() {
  249. if (ValueType_ == EMetricValueType::SUMMARY) {
  250. TBase::AsSummaryDouble()->Ref();
  251. } else if (ValueType_ == EMetricValueType::HISTOGRAM) {
  252. TBase::AsHistogram()->Ref();
  253. } else if (ValueType_ == EMetricValueType::LOGHISTOGRAM) {
  254. TBase::AsLogHistogram()->Ref();
  255. }
  256. }
  257. void UnRef() {
  258. if (ValueType_ == EMetricValueType::SUMMARY) {
  259. TBase::AsSummaryDouble()->UnRef();
  260. } else if (ValueType_ == EMetricValueType::HISTOGRAM) {
  261. TBase::AsHistogram()->UnRef();
  262. } else if (ValueType_ == EMetricValueType::LOGHISTOGRAM) {
  263. TBase::AsLogHistogram()->UnRef();
  264. }
  265. }
  266. private:
  267. EMetricValueType ValueType_ = EMetricValueType::UNKNOWN;
  268. };
  269. static_assert(sizeof(TMetricValue) == sizeof(ui64),
  270. "expected size of TMetricValue is one machine word");
  271. ///////////////////////////////////////////////////////////////////////////
  272. // TMetricTimeSeries
  273. ///////////////////////////////////////////////////////////////////////////
  274. class TMetricTimeSeries: private TMoveOnly {
  275. public:
  276. class TPoint {
  277. public:
  278. TPoint()
  279. : Time_(TInstant::Zero())
  280. {
  281. }
  282. template <typename T>
  283. TPoint(TInstant time, T value)
  284. : Time_(time)
  285. , Value_(value)
  286. {
  287. }
  288. TInstant GetTime() const noexcept {
  289. return Time_;
  290. }
  291. TMetricValue GetValue() const noexcept {
  292. return Value_;
  293. }
  294. void ClearValue() {
  295. Value_ = {};
  296. }
  297. private:
  298. TInstant Time_;
  299. TMetricValue Value_;
  300. };
  301. public:
  302. TMetricTimeSeries() = default;
  303. TMetricTimeSeries(TMetricTimeSeries&& rhs) noexcept
  304. : ValueType_(rhs.ValueType_)
  305. , Points_(std::move(rhs.Points_))
  306. {
  307. rhs.ValueType_ = EMetricValueType::UNKNOWN;
  308. }
  309. TMetricTimeSeries& operator=(TMetricTimeSeries&& rhs) noexcept {
  310. Clear();
  311. ValueType_ = rhs.ValueType_;
  312. rhs.ValueType_ = EMetricValueType::UNKNOWN;
  313. Points_ = std::move(rhs.Points_);
  314. return *this;
  315. }
  316. ~TMetricTimeSeries() {
  317. Clear();
  318. }
  319. template <typename T>
  320. void Add(TInstant time, T value) {
  321. Add(TPoint(time, value), TValueType<T>::Type);
  322. }
  323. void Add(TPoint point, EMetricValueType valueType) {
  324. if (Empty()) {
  325. ValueType_ = valueType;
  326. } else {
  327. CheckTypes(ValueType_, valueType);
  328. }
  329. Points_.push_back(point);
  330. if (ValueType_ == EMetricValueType::SUMMARY) {
  331. TPoint& p = Points_.back();
  332. p.GetValue().AsSummaryDouble()->Ref();
  333. } else if (ValueType_ == EMetricValueType::HISTOGRAM) {
  334. TPoint& p = Points_.back();
  335. p.GetValue().AsHistogram()->Ref();
  336. } else if (ValueType_ == EMetricValueType::LOGHISTOGRAM) {
  337. TPoint& p = Points_.back();
  338. p.GetValue().AsLogHistogram()->Ref();
  339. }
  340. }
  341. void CopyFrom(const TMetricTimeSeries& other) {
  342. if (Empty()) {
  343. ValueType_ = other.ValueType_;
  344. } else {
  345. CheckTypes(GetValueType(), other.GetValueType());
  346. }
  347. size_t prevSize = Points_.size();
  348. Copy(std::begin(other.Points_), std::end(other.Points_),
  349. std::back_inserter(Points_));
  350. if (ValueType_ == EMetricValueType::HISTOGRAM) {
  351. for (size_t i = prevSize; i < Points_.size(); i++) {
  352. TPoint& point = Points_[i];
  353. point.GetValue().AsHistogram()->Ref();
  354. }
  355. } else if (ValueType_ == EMetricValueType::SUMMARY) {
  356. for (size_t i = prevSize; i < Points_.size(); ++i) {
  357. TPoint& point = Points_[i];
  358. point.GetValue().AsSummaryDouble()->Ref();
  359. }
  360. } else if (ValueType_ == EMetricValueType::LOGHISTOGRAM) {
  361. for (size_t i = prevSize; i < Points_.size(); ++i) {
  362. TPoint& point = Points_[i];
  363. point.GetValue().AsLogHistogram()->Ref();
  364. }
  365. }
  366. }
  367. template <typename TConsumer>
  368. void ForEach(TConsumer c) const {
  369. for (const auto& point : Points_) {
  370. c(point.GetTime(), ValueType_, point.GetValue());
  371. }
  372. }
  373. bool Empty() const noexcept {
  374. return Points_.empty();
  375. }
  376. size_t Size() const noexcept {
  377. return Points_.size();
  378. }
  379. size_t Capacity() const noexcept {
  380. return Points_.capacity();
  381. }
  382. const TPoint& operator[](size_t index) const noexcept {
  383. return Points_[index];
  384. }
  385. void SortByTs();
  386. void Clear() noexcept;
  387. EMetricValueType GetValueType() const noexcept {
  388. return ValueType_;
  389. }
  390. private:
  391. static void CheckTypes(EMetricValueType t1, EMetricValueType t2) {
  392. Y_ENSURE(t1 == t2,
  393. "Series type mismatch: expected " << t1 <<
  394. ", but got " << t2);
  395. }
  396. private:
  397. EMetricValueType ValueType_ = EMetricValueType::UNKNOWN;
  398. TVector<TPoint> Points_;
  399. };
  400. template <EMetricValueType valueType, typename TPoint>
  401. static inline void SnapshotUnRef(TPoint& point) {
  402. if constexpr (valueType == EMetricValueType::HISTOGRAM) {
  403. if (auto* hist = point.GetValue().AsHistogram()) {
  404. hist->UnRef();
  405. }
  406. } else if constexpr (valueType == EMetricValueType::SUMMARY) {
  407. if (auto* summary = point.GetValue().AsSummaryDouble()) {
  408. summary->UnRef();
  409. }
  410. } else if constexpr (valueType == EMetricValueType::LOGHISTOGRAM) {
  411. if (auto* logHist = point.GetValue().AsLogHistogram()) {
  412. logHist->UnRef();
  413. }
  414. }
  415. }
  416. template <EMetricValueType valueType, typename TPoint>
  417. static void EraseDuplicates(TVector<TPoint>& points) {
  418. // we have to manually clean reference to a snapshot from point
  419. // while removing duplicates
  420. auto result = points.rbegin();
  421. for (auto it = result + 1; it != points.rend(); ++it) {
  422. if (result->GetTime() != it->GetTime() && ++result != it) {
  423. SnapshotUnRef<valueType>(*result);
  424. *result = *it; // (2) copy
  425. it->ClearValue(); // (3) clean pointer in the source
  426. }
  427. }
  428. // erase tail points
  429. for (auto it = result + 1; it != points.rend(); ++it) {
  430. SnapshotUnRef<valueType>(*it);
  431. }
  432. points.erase(points.begin(), (result + 1).base());
  433. }
  434. template <typename TPoint>
  435. void SortPointsByTs(EMetricValueType valueType, TVector<TPoint>& points) {
  436. if (points.size() < 2) {
  437. return;
  438. }
  439. if (valueType != EMetricValueType::HISTOGRAM && valueType != EMetricValueType::SUMMARY
  440. && valueType != EMetricValueType::LOGHISTOGRAM) {
  441. // Stable sort + saving only the last point inside a group of duplicates
  442. StableSortBy(points, NPrivate::POINT_KEY_FN);
  443. auto it = UniqueBy(points.rbegin(), points.rend(), NPrivate::POINT_KEY_FN);
  444. points.erase(points.begin(), it.base());
  445. } else {
  446. StableSortBy(points, NPrivate::POINT_KEY_FN);
  447. if (valueType == EMetricValueType::HISTOGRAM) {
  448. EraseDuplicates<EMetricValueType::HISTOGRAM>(points);
  449. } else if (valueType == EMetricValueType::LOGHISTOGRAM) {
  450. EraseDuplicates<EMetricValueType::LOGHISTOGRAM>(points);
  451. } else {
  452. EraseDuplicates<EMetricValueType::SUMMARY>(points);
  453. }
  454. }
  455. }
  456. }