spack_v1_ut.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. #include "spack_v1.h"
  2. #include <library/cpp/monlib/encode/protobuf/protobuf.h>
  3. #include <library/cpp/monlib/metrics/labels.h>
  4. #include <library/cpp/monlib/metrics/metric.h>
  5. #include <library/cpp/testing/unittest/registar.h>
  6. #include <util/generic/buffer.h>
  7. #include <util/stream/buffer.h>
  8. #include <util/string/hex.h>
  9. #include <utility>
  10. using namespace NMonitoring;
  11. #define UNIT_ASSERT_BINARY_EQUALS(a, b) \
  12. do { \
  13. auto size = Y_ARRAY_SIZE(b); \
  14. if (Y_UNLIKELY(::memcmp(a, b, size) != 0)) { \
  15. auto as = HexEncode(a, size); \
  16. auto bs = HexEncode(b, size); \
  17. UNIT_FAIL_IMPL("equal assertion failed " #a " == " #b, \
  18. "\n actual: " << as << "\nexpected: " << bs); \
  19. } \
  20. } while (0)
  21. void AssertLabelEqual(const NProto::TLabel& l, TStringBuf name, TStringBuf value) {
  22. UNIT_ASSERT_STRINGS_EQUAL(l.GetName(), name);
  23. UNIT_ASSERT_STRINGS_EQUAL(l.GetValue(), value);
  24. }
  25. void AssertPointEqual(const NProto::TPoint& p, TInstant time, double value) {
  26. UNIT_ASSERT_VALUES_EQUAL(p.GetTime(), time.MilliSeconds());
  27. UNIT_ASSERT_EQUAL(p.GetValueCase(), NProto::TPoint::kFloat64);
  28. UNIT_ASSERT_DOUBLES_EQUAL(p.GetFloat64(), value, std::numeric_limits<double>::epsilon());
  29. }
  30. void AssertPointEqual(const NProto::TPoint& p, TInstant time, ui64 value) {
  31. UNIT_ASSERT_VALUES_EQUAL(p.GetTime(), time.MilliSeconds());
  32. UNIT_ASSERT_EQUAL(p.GetValueCase(), NProto::TPoint::kUint64);
  33. UNIT_ASSERT_VALUES_EQUAL(p.GetUint64(), value);
  34. }
  35. void AssertPointEqual(const NProto::TPoint& p, TInstant time, i64 value) {
  36. UNIT_ASSERT_VALUES_EQUAL(p.GetTime(), time.MilliSeconds());
  37. UNIT_ASSERT_EQUAL(p.GetValueCase(), NProto::TPoint::kInt64);
  38. UNIT_ASSERT_VALUES_EQUAL(p.GetInt64(), value);
  39. }
  40. Y_UNIT_TEST_SUITE(TSpackTest) {
  41. ui8 expectedHeader_v1_0[] = {
  42. 0x53, 0x50, // magic "SP" (fixed ui16)
  43. // minor, major
  44. 0x00, 0x01, // version (fixed ui16)
  45. 0x18, 0x00, // header size (fixed ui16)
  46. 0x00, // time precision (fixed ui8)
  47. 0x00, // compression algorithm (fixed ui8)
  48. 0x0d, 0x00, 0x00, 0x00, // label names size (fixed ui32)
  49. 0x40, 0x00, 0x00, 0x00, // labels values size (fixed ui32)
  50. 0x08, 0x00, 0x00, 0x00, // metric count (fixed ui32)
  51. 0x08, 0x00, 0x00, 0x00, // points count (fixed ui32)
  52. };
  53. ui8 expectedHeader[] = {
  54. 0x53, 0x50, // magic "SP" (fixed ui16)
  55. // minor, major
  56. 0x01, 0x01, // version (fixed ui16)
  57. 0x18, 0x00, // header size (fixed ui16)
  58. 0x00, // time precision (fixed ui8)
  59. 0x00, // compression algorithm (fixed ui8)
  60. 0x0d, 0x00, 0x00, 0x00, // label names size (fixed ui32)
  61. 0x40, 0x00, 0x00, 0x00, // labels values size (fixed ui32)
  62. 0x08, 0x00, 0x00, 0x00, // metric count (fixed ui32)
  63. 0x08, 0x00, 0x00, 0x00, // points count (fixed ui32)
  64. };
  65. ui8 expectedStringPools[] = {
  66. 0x6e, 0x61, 0x6d, 0x65, 0x00, // "name\0"
  67. 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x00, // "project\0"
  68. 0x73, 0x6f, 0x6c, 0x6f, 0x6d, 0x6f, 0x6e, 0x00, // "solomon\0"
  69. 0x71, 0x31, 0x00, // "q1\0"
  70. 0x71, 0x32, 0x00, // "q2\0"
  71. 0x71, 0x33, 0x00, // "q3\0"
  72. 0x61, 0x6e, 0x73, 0x77, 0x65, 0x72, 0x00, // "answer\0"
  73. 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, // "responseTimeMillis\0"
  74. 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x6c, 0x6c,
  75. 0x69, 0x73, 0x00,
  76. 0x62, 0x79, 0x74, 0x65, 0x73, 0x00, // "bytes\0"
  77. 0x74, 0x65, 0x6D, 0x70, 0x65, 0x72, 0x61, 0x74, // "temperature\0"
  78. 0x75, 0x72, 0x65, 0x00,
  79. 0x6d, 0x73, 0x00, // "ms\0"
  80. };
  81. ui8 expectedCommonTime[] = {
  82. 0x00, 0x2f, 0x68, 0x59, // common time in seconds (fixed ui32)
  83. };
  84. ui8 expectedCommonLabels[] = {
  85. 0x01, // common labels count (varint)
  86. 0x01, // label name index (varint)
  87. 0x00, // label value index (varint)
  88. };
  89. ui8 expectedMetric1[] = {
  90. 0x0C, // types (RATE | NONE) (fixed ui8)
  91. 0x00, // flags (fixed ui8)
  92. 0x01, // metric labels count (varint)
  93. 0x00, // label name index (varint)
  94. 0x01, // label value index (varint)
  95. };
  96. ui8 expectedMetric2[] = {
  97. 0x09, // types (COUNTER | ONE_WITHOUT_TS) (fixed ui8)
  98. 0x00, // flags (fixed ui8)
  99. 0x01, // metric labels count (varint)
  100. 0x00, // label name index (varint)
  101. 0x02, // label value index (varint)
  102. 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value (fixed ui64)
  103. };
  104. ui8 expectedMetric3[] = {
  105. 0x0a, // types (COUNTER | ONE_WITH_TS) (fixed ui8)
  106. 0x00, // flags (fixed ui8)
  107. 0x01, // metric labels count (varint)
  108. 0x00, // label name index (varint)
  109. 0x03, // label value index (varint)
  110. 0x0b, 0x63, 0xfe, 0x59, // time in seconds (fixed ui32)
  111. 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value (fixed ui64)
  112. };
  113. ui8 expectedMetric4[] = {
  114. 0x07, // types (GAUGE | MANY_WITH_TS) (fixed ui8)
  115. 0x00, // flags (fixed ui8)
  116. 0x01, // metric labels count (varint)
  117. 0x00, // label name index (varint)
  118. 0x04, // label value index (varint)
  119. 0x02, // points count (varint)
  120. 0x0b, 0x63, 0xfe, 0x59, // time in seconds (fixed ui32)
  121. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x40, // value (double IEEE754)
  122. 0x1a, 0x63, 0xfe, 0x59, // time in seconds (fixed ui32)
  123. 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x4d, 0x40 // value (double IEEE754)
  124. };
  125. ui8 expectedMetric5_v1_0[] = {
  126. 0x16, // types (HIST | ONE_WITH_TS) (fixed ui8)
  127. 0x00, // flags (fixed ui8)
  128. 0x01, // metric labels count (varint)
  129. 0x00, // label name index (varint)
  130. 0x05, // label value index (varint)
  131. 0x0b, 0x63, 0xfe, 0x59, // time in seconds (fixed ui32)
  132. 0x06, // histogram buckets count (varint)
  133. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // histogram bucket bounds (array of fixed ui64)
  134. 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  135. 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  136. 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  137. 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  138. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
  139. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // histogram bucket values
  140. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  141. 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  142. 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  143. 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  144. 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  145. };
  146. ui8 expectedMetric5[] = {
  147. 0x16, // types (HIST | ONE_WITH_TS) (fixed ui8)
  148. 0x00, // flags (fixed ui8)
  149. 0x01, // metric labels count (varint)
  150. 0x00, // label name index (varint)
  151. 0x05, // label value index (varint)
  152. 0x0b, 0x63, 0xfe, 0x59, // time in seconds (fixed ui32)
  153. 0x06, // histogram buckets count (varint)
  154. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, // histogram bucket bounds (array of doubles)
  155. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40,
  156. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x40,
  157. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40,
  158. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x40,
  159. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x7f,
  160. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // histogram bucket values
  161. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  162. 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  163. 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  164. 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  165. 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  166. };
  167. ui8 expectedMetric6[] = {
  168. 0x12, // types (IGAUGE | ONE_WITH_TS) (fixed ui8)
  169. 0x00, // flags (fixed ui8)
  170. 0x01, // metric labels count (varint)
  171. 0x00, // label name index (varint)
  172. 0x06, // label value index (varint)
  173. 0x0b, 0x63, 0xfe, 0x59, // time in seconds (fixed ui32)
  174. 0x39, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value (fixed i64)
  175. };
  176. ui8 expectedMetric7[] = {
  177. 0x1e, // types (DSUMMARY | ONE_WITH_TS) (fixed ui8)
  178. 0x00, // flags (fixed ui8)
  179. 0x01, // metric labels count (varint)
  180. 0x00, // label name index (varint)
  181. 0x07, // label value index (varint)
  182. 0x0b, 0x63, 0xfe, 0x59, // time in seconds (fixed ui32)
  183. 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // count (fixed ui64)
  184. 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x24, 0x40, // sum (fixed double)
  185. 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xdc, 0xbf, // min (fixed double)
  186. 0x64, 0x3b, 0xdf, 0x4f, 0x8d, 0x97, 0xde, 0x3f, // max (fixed double)
  187. 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0xd3, 0x3f, // last (fixed double)
  188. };
  189. ui8 expectedMetric8[] = {
  190. 0x26, // types (LOGHIST | ONE_WITH_TS) (fixed ui8)
  191. 0x00, // flags (fixed ui8)
  192. 0x01, // metric labels count (variant)
  193. 0x00, // label name index (variant)
  194. 0x08, // label value index (variant)
  195. 0x0b, 0x63, 0xfe, 0x59, // time in seconds (fixed ui32)
  196. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x3F, // base (fixed double)
  197. 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // zerosCount (fixed ui64)
  198. 0x00, // startPower (variant)
  199. 0x04, // buckets count (variant)
  200. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x3F,
  201. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x3F, // bucket values
  202. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x3F,
  203. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x3F,
  204. };
  205. const size_t expectedSize =
  206. Y_ARRAY_SIZE(expectedHeader) +
  207. Y_ARRAY_SIZE(expectedStringPools) +
  208. Y_ARRAY_SIZE(expectedCommonTime) +
  209. Y_ARRAY_SIZE(expectedCommonLabels) +
  210. Y_ARRAY_SIZE(expectedMetric1) +
  211. Y_ARRAY_SIZE(expectedMetric2) +
  212. Y_ARRAY_SIZE(expectedMetric3) +
  213. Y_ARRAY_SIZE(expectedMetric4) +
  214. Y_ARRAY_SIZE(expectedMetric5) +
  215. Y_ARRAY_SIZE(expectedMetric6) +
  216. Y_ARRAY_SIZE(expectedMetric7) +
  217. Y_ARRAY_SIZE(expectedMetric8);
  218. const TInstant now = TInstant::ParseIso8601Deprecated("2017-11-05T01:02:03Z");
  219. // {1: 1, 2: 1, 4: 2, 8: 4, 16: 8, inf: 83}
  220. IHistogramSnapshotPtr TestHistogram() {
  221. auto h = ExponentialHistogram(6, 2);
  222. for (i64 i = 1; i < 100; i++) {
  223. h->Collect(i);
  224. }
  225. return h->Snapshot();
  226. }
  227. TLogHistogramSnapshotPtr TestLogHistogram() {
  228. TVector buckets{0.5, 0.25, 0.25, 0.5};
  229. return MakeIntrusive<TLogHistogramSnapshot>(1.5, 1u, 0, std::move(buckets));
  230. }
  231. ISummaryDoubleSnapshotPtr TestSummaryDouble() {
  232. return MakeIntrusive<TSummaryDoubleSnapshot>(10.1, -0.45, 0.478, 0.3, 30u);
  233. }
  234. Y_UNIT_TEST(Encode) {
  235. TBuffer buffer;
  236. TBufferOutput out(buffer);
  237. auto e = EncoderSpackV1(
  238. &out, ETimePrecision::SECONDS, ECompression::IDENTITY);
  239. e->OnStreamBegin();
  240. { // common time
  241. e->OnCommonTime(TInstant::Seconds(1500000000));
  242. }
  243. { // common labels
  244. e->OnLabelsBegin();
  245. e->OnLabel("project", "solomon");
  246. e->OnLabelsEnd();
  247. }
  248. { // metric #1
  249. e->OnMetricBegin(EMetricType::RATE);
  250. {
  251. e->OnLabelsBegin();
  252. e->OnLabel("name", "q1");
  253. e->OnLabelsEnd();
  254. }
  255. e->OnMetricEnd();
  256. }
  257. { // metric #2
  258. e->OnMetricBegin(EMetricType::COUNTER);
  259. {
  260. e->OnLabelsBegin();
  261. e->OnLabel("name", "q2");
  262. e->OnLabelsEnd();
  263. }
  264. // Only the last value will be encoded
  265. e->OnUint64(TInstant::Zero(), 10);
  266. e->OnUint64(TInstant::Zero(), 13);
  267. e->OnUint64(TInstant::Zero(), 17);
  268. e->OnMetricEnd();
  269. }
  270. { // metric #3
  271. e->OnMetricBegin(EMetricType::COUNTER);
  272. {
  273. e->OnLabelsBegin();
  274. e->OnLabel("name", "q3");
  275. e->OnLabelsEnd();
  276. }
  277. e->OnUint64(now, 10);
  278. e->OnUint64(now, 13);
  279. e->OnUint64(now, 17);
  280. e->OnMetricEnd();
  281. }
  282. { // metric #4
  283. e->OnMetricBegin(EMetricType::GAUGE);
  284. {
  285. e->OnLabelsBegin();
  286. e->OnLabel("name", "answer");
  287. e->OnLabelsEnd();
  288. }
  289. e->OnDouble(now, 42);
  290. e->OnDouble(now + TDuration::Seconds(15), 59);
  291. e->OnMetricEnd();
  292. }
  293. { // metric #5
  294. e->OnMetricBegin(EMetricType::HIST);
  295. {
  296. e->OnLabelsBegin();
  297. e->OnLabel("name", "responseTimeMillis");
  298. e->OnLabelsEnd();
  299. }
  300. auto histogram = TestHistogram();
  301. e->OnHistogram(now, histogram);
  302. e->OnMetricEnd();
  303. }
  304. { // metric #6
  305. e->OnMetricBegin(EMetricType::IGAUGE);
  306. {
  307. e->OnLabelsBegin();
  308. e->OnLabel("name", "bytes");
  309. e->OnLabelsEnd();
  310. }
  311. e->OnInt64(now, 1337);
  312. e->OnMetricEnd();
  313. }
  314. { // metric 7
  315. e->OnMetricBegin(EMetricType::DSUMMARY);
  316. {
  317. e->OnLabelsBegin();
  318. e->OnLabel("name", "temperature");
  319. e->OnLabelsEnd();
  320. }
  321. e->OnSummaryDouble(now, TestSummaryDouble());
  322. e->OnMetricEnd();
  323. }
  324. { // metric 8
  325. e->OnMetricBegin(EMetricType::LOGHIST);
  326. {
  327. e->OnLabelsBegin();
  328. e->OnLabel("name", "ms");
  329. e->OnLabelsEnd();
  330. }
  331. e->OnLogHistogram(now, TestLogHistogram());
  332. e->OnMetricEnd();
  333. }
  334. e->OnStreamEnd();
  335. e->Close();
  336. // Cout << "encoded: " << HexEncode(buffer.Data(), buffer.Size()) << Endl;
  337. // Cout << "size: " << buffer.Size() << Endl;
  338. UNIT_ASSERT_VALUES_EQUAL(buffer.Size(), expectedSize);
  339. ui8* p = reinterpret_cast<ui8*>(buffer.Data());
  340. UNIT_ASSERT_BINARY_EQUALS(p, expectedHeader);
  341. p += Y_ARRAY_SIZE(expectedHeader);
  342. UNIT_ASSERT_BINARY_EQUALS(p, expectedStringPools);
  343. p += Y_ARRAY_SIZE(expectedStringPools);
  344. UNIT_ASSERT_BINARY_EQUALS(p, expectedCommonTime);
  345. p += Y_ARRAY_SIZE(expectedCommonTime);
  346. UNIT_ASSERT_BINARY_EQUALS(p, expectedCommonLabels);
  347. p += Y_ARRAY_SIZE(expectedCommonLabels);
  348. UNIT_ASSERT_BINARY_EQUALS(p, expectedMetric1);
  349. p += Y_ARRAY_SIZE(expectedMetric1);
  350. UNIT_ASSERT_BINARY_EQUALS(p, expectedMetric2);
  351. p += Y_ARRAY_SIZE(expectedMetric2);
  352. UNIT_ASSERT_BINARY_EQUALS(p, expectedMetric3);
  353. p += Y_ARRAY_SIZE(expectedMetric3);
  354. UNIT_ASSERT_BINARY_EQUALS(p, expectedMetric4);
  355. p += Y_ARRAY_SIZE(expectedMetric4);
  356. UNIT_ASSERT_BINARY_EQUALS(p, expectedMetric5);
  357. p += Y_ARRAY_SIZE(expectedMetric5);
  358. UNIT_ASSERT_BINARY_EQUALS(p, expectedMetric6);
  359. p += Y_ARRAY_SIZE(expectedMetric6);
  360. UNIT_ASSERT_BINARY_EQUALS(p, expectedMetric7);
  361. p += Y_ARRAY_SIZE(expectedMetric7);
  362. UNIT_ASSERT_BINARY_EQUALS(p, expectedMetric8);
  363. p += Y_ARRAY_SIZE(expectedMetric8);
  364. }
  365. NProto::TMultiSamplesList GetMergingMetricSamples(EMetricsMergingMode mergingMode) {
  366. TBuffer buffer;
  367. TBufferOutput out(buffer);
  368. auto e = EncoderSpackV1(
  369. &out,
  370. ETimePrecision::SECONDS,
  371. ECompression::IDENTITY,
  372. mergingMode
  373. );
  374. e->OnStreamBegin();
  375. for (size_t i = 0; i != 3; ++i) {
  376. e->OnMetricBegin(EMetricType::COUNTER);
  377. {
  378. e->OnLabelsBegin();
  379. e->OnLabel("name", "my_counter");
  380. e->OnLabelsEnd();
  381. }
  382. e->OnUint64(TInstant::Zero() + TDuration::Seconds(i), i + 1);
  383. e->OnMetricEnd();
  384. }
  385. e->OnStreamEnd();
  386. e->Close();
  387. NProto::TMultiSamplesList samples;
  388. IMetricEncoderPtr eProto = EncoderProtobuf(&samples);
  389. TBufferInput in(buffer);
  390. DecodeSpackV1(&in, eProto.Get());
  391. return samples;
  392. }
  393. Y_UNIT_TEST(SpackEncoderMergesMetrics) {
  394. {
  395. NProto::TMultiSamplesList samples = GetMergingMetricSamples(EMetricsMergingMode::DEFAULT);
  396. UNIT_ASSERT_EQUAL(samples.SamplesSize(), 3);
  397. UNIT_ASSERT_EQUAL(samples.GetSamples(0).GetPoints(0).GetUint64(), 1);
  398. UNIT_ASSERT_EQUAL(samples.GetSamples(1).GetPoints(0).GetUint64(), 2);
  399. UNIT_ASSERT_EQUAL(samples.GetSamples(2).GetPoints(0).GetUint64(), 3);
  400. }
  401. {
  402. NProto::TMultiSamplesList samples = GetMergingMetricSamples(EMetricsMergingMode::MERGE_METRICS);
  403. UNIT_ASSERT_EQUAL(samples.SamplesSize(), 1);
  404. auto sample0 = samples.GetSamples(0);
  405. UNIT_ASSERT_EQUAL(sample0.GetPoints(0).GetUint64(), 1);
  406. UNIT_ASSERT_EQUAL(sample0.GetPoints(1).GetUint64(), 2);
  407. UNIT_ASSERT_EQUAL(sample0.GetPoints(2).GetUint64(), 3);
  408. }
  409. }
  410. void DecodeDataToSamples(NProto::TMultiSamplesList & samples, ui16 version) {
  411. IMetricEncoderPtr e = EncoderProtobuf(&samples);
  412. TBuffer data(expectedSize);
  413. if (SV1_00 == version) { // v1.0
  414. data.Append(reinterpret_cast<char*>(expectedHeader_v1_0), Y_ARRAY_SIZE(expectedHeader_v1_0));
  415. } else {
  416. data.Append(reinterpret_cast<char*>(expectedHeader), Y_ARRAY_SIZE(expectedHeader));
  417. }
  418. data.Append(reinterpret_cast<char*>(expectedStringPools), Y_ARRAY_SIZE(expectedStringPools));
  419. data.Append(reinterpret_cast<char*>(expectedCommonTime), Y_ARRAY_SIZE(expectedCommonTime));
  420. data.Append(reinterpret_cast<char*>(expectedCommonLabels), Y_ARRAY_SIZE(expectedCommonLabels));
  421. data.Append(reinterpret_cast<char*>(expectedMetric1), Y_ARRAY_SIZE(expectedMetric1));
  422. data.Append(reinterpret_cast<char*>(expectedMetric2), Y_ARRAY_SIZE(expectedMetric2));
  423. data.Append(reinterpret_cast<char*>(expectedMetric3), Y_ARRAY_SIZE(expectedMetric3));
  424. data.Append(reinterpret_cast<char*>(expectedMetric4), Y_ARRAY_SIZE(expectedMetric4));
  425. if (SV1_00 == version) { // v1.0
  426. data.Append(reinterpret_cast<char*>(expectedMetric5_v1_0), Y_ARRAY_SIZE(expectedMetric5_v1_0));
  427. } else {
  428. data.Append(reinterpret_cast<char*>(expectedMetric5), Y_ARRAY_SIZE(expectedMetric5));
  429. }
  430. data.Append(reinterpret_cast<char*>(expectedMetric6), Y_ARRAY_SIZE(expectedMetric6));
  431. data.Append(reinterpret_cast<char*>(expectedMetric7), Y_ARRAY_SIZE(expectedMetric7));
  432. data.Append(reinterpret_cast<char*>(expectedMetric8), Y_ARRAY_SIZE(expectedMetric8));
  433. TBufferInput in(data);
  434. DecodeSpackV1(&in, e.Get());
  435. }
  436. void DecodeDataToSamples(NProto::TMultiSamplesList & samples) {
  437. TSpackHeader header;
  438. header.Version = SV1_01;
  439. DecodeDataToSamples(samples, header.Version);
  440. }
  441. Y_UNIT_TEST(Decode) {
  442. NProto::TMultiSamplesList samples;
  443. DecodeDataToSamples(samples);
  444. UNIT_ASSERT_VALUES_EQUAL(
  445. TInstant::MilliSeconds(samples.GetCommonTime()),
  446. TInstant::Seconds(1500000000));
  447. UNIT_ASSERT_VALUES_EQUAL(samples.CommonLabelsSize(), 1);
  448. AssertLabelEqual(samples.GetCommonLabels(0), "project", "solomon");
  449. UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 8);
  450. {
  451. const NProto::TMultiSample& s = samples.GetSamples(0);
  452. UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::RATE);
  453. UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1);
  454. AssertLabelEqual(s.GetLabels(0), "name", "q1");
  455. }
  456. {
  457. const NProto::TMultiSample& s = samples.GetSamples(1);
  458. UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::COUNTER);
  459. UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1);
  460. AssertLabelEqual(s.GetLabels(0), "name", "q2");
  461. UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1);
  462. AssertPointEqual(s.GetPoints(0), TInstant::Zero(), ui64(17));
  463. }
  464. {
  465. const NProto::TMultiSample& s = samples.GetSamples(2);
  466. UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::COUNTER);
  467. UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1);
  468. AssertLabelEqual(s.GetLabels(0), "name", "q3");
  469. UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1);
  470. AssertPointEqual(s.GetPoints(0), now, ui64(17));
  471. }
  472. {
  473. const NProto::TMultiSample& s = samples.GetSamples(3);
  474. UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::GAUGE);
  475. UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1);
  476. AssertLabelEqual(s.GetLabels(0), "name", "answer");
  477. UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 2);
  478. AssertPointEqual(s.GetPoints(0), now, double(42));
  479. AssertPointEqual(s.GetPoints(1), now + TDuration::Seconds(15), double(59));
  480. }
  481. {
  482. const NProto::TMultiSample& s = samples.GetSamples(4);
  483. UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::HISTOGRAM);
  484. UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1);
  485. AssertLabelEqual(s.GetLabels(0), "name", "responseTimeMillis");
  486. UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1);
  487. const NProto::TPoint& p = s.GetPoints(0);
  488. UNIT_ASSERT_VALUES_EQUAL(p.GetTime(), now.MilliSeconds());
  489. UNIT_ASSERT_EQUAL(p.GetValueCase(), NProto::TPoint::kHistogram);
  490. auto histogram = TestHistogram();
  491. const NProto::THistogram& pointHistogram = p.GetHistogram();
  492. UNIT_ASSERT_VALUES_EQUAL(pointHistogram.BoundsSize(), histogram->Count());
  493. UNIT_ASSERT_VALUES_EQUAL(pointHistogram.ValuesSize(), histogram->Count());
  494. for (size_t i = 0; i < pointHistogram.BoundsSize(); i++) {
  495. UNIT_ASSERT_DOUBLES_EQUAL(pointHistogram.GetBounds(i), histogram->UpperBound(i), Min<double>());
  496. UNIT_ASSERT_VALUES_EQUAL(pointHistogram.GetValues(i), histogram->Value(i));
  497. }
  498. }
  499. {
  500. const NProto::TMultiSample& s = samples.GetSamples(5);
  501. UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::IGAUGE);
  502. UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1);
  503. AssertLabelEqual(s.GetLabels(0), "name", "bytes");
  504. UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1);
  505. AssertPointEqual(s.GetPoints(0), now, i64(1337));
  506. }
  507. {
  508. const NProto::TMultiSample& s = samples.GetSamples(6);
  509. UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::DSUMMARY);
  510. UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1);
  511. AssertLabelEqual(s.GetLabels(0), "name", "temperature");
  512. UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1);
  513. const NProto::TPoint& p = s.GetPoints(0);
  514. UNIT_ASSERT_VALUES_EQUAL(p.GetTime(), now.MilliSeconds());
  515. UNIT_ASSERT_EQUAL(p.GetValueCase(), NProto::TPoint::kSummaryDouble);
  516. auto expected = TestSummaryDouble();
  517. auto actual = p.GetSummaryDouble();
  518. UNIT_ASSERT_VALUES_EQUAL(expected->GetSum(), actual.GetSum());
  519. UNIT_ASSERT_VALUES_EQUAL(expected->GetMin(), actual.GetMin());
  520. UNIT_ASSERT_VALUES_EQUAL(expected->GetMax(), actual.GetMax());
  521. UNIT_ASSERT_VALUES_EQUAL(expected->GetLast(), actual.GetLast());
  522. UNIT_ASSERT_VALUES_EQUAL(expected->GetCount(), actual.GetCount());
  523. }
  524. {
  525. const NProto::TMultiSample& s = samples.GetSamples(7);
  526. UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::LOGHISTOGRAM);
  527. UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1);
  528. AssertLabelEqual(s.GetLabels(0), "name", "ms");
  529. UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1);
  530. const NProto::TPoint& p = s.GetPoints(0);
  531. UNIT_ASSERT_VALUES_EQUAL(p.GetTime(), now.MilliSeconds());
  532. UNIT_ASSERT_EQUAL(p.GetValueCase(), NProto::TPoint::kLogHistogram);
  533. auto expected = TestLogHistogram();
  534. auto actual = p.GetLogHistogram();
  535. UNIT_ASSERT_VALUES_EQUAL(expected->ZerosCount(), actual.GetZerosCount());
  536. UNIT_ASSERT_VALUES_EQUAL(expected->Base(), actual.GetBase());
  537. UNIT_ASSERT_VALUES_EQUAL(expected->StartPower(), actual.GetStartPower());
  538. UNIT_ASSERT_VALUES_EQUAL(expected->Count(), actual.BucketsSize());
  539. for (size_t i = 0; i < expected->Count(); ++i) {
  540. UNIT_ASSERT_VALUES_EQUAL(expected->Bucket(i), actual.GetBuckets(i));
  541. }
  542. }
  543. }
  544. void TestCompression(ECompression alg) {
  545. TBuffer buffer;
  546. {
  547. TBufferOutput out(buffer);
  548. auto e = EncoderSpackV1(&out, ETimePrecision::MILLIS, alg);
  549. e->OnStreamBegin();
  550. {
  551. e->OnMetricBegin(EMetricType::GAUGE);
  552. {
  553. e->OnLabelsBegin();
  554. e->OnLabel("name", "answer");
  555. e->OnLabelsEnd();
  556. }
  557. e->OnDouble(now, 42);
  558. e->OnMetricEnd();
  559. }
  560. e->OnStreamEnd();
  561. e->Close();
  562. }
  563. auto* header = reinterpret_cast<const TSpackHeader*>(buffer.Data());
  564. UNIT_ASSERT_EQUAL(DecodeCompression(header->Compression), alg);
  565. NProto::TMultiSamplesList samples;
  566. {
  567. IMetricEncoderPtr e = EncoderProtobuf(&samples);
  568. TBufferInput in(buffer);
  569. DecodeSpackV1(&in, e.Get());
  570. }
  571. UNIT_ASSERT_VALUES_EQUAL(
  572. TInstant::MilliSeconds(samples.GetCommonTime()),
  573. TInstant::Zero());
  574. UNIT_ASSERT_VALUES_EQUAL(samples.CommonLabelsSize(), 0);
  575. UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 1);
  576. {
  577. const NProto::TMultiSample& s = samples.GetSamples(0);
  578. UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::GAUGE);
  579. UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1);
  580. AssertLabelEqual(s.GetLabels(0), "name", "answer");
  581. AssertPointEqual(s.GetPoints(0), now, 42.0);
  582. }
  583. }
  584. Y_UNIT_TEST(CompressionIdentity) {
  585. TestCompression(ECompression::IDENTITY);
  586. }
  587. Y_UNIT_TEST(CompressionZlib) {
  588. TestCompression(ECompression::ZLIB);
  589. }
  590. Y_UNIT_TEST(CompressionZstd) {
  591. TestCompression(ECompression::ZSTD);
  592. }
  593. Y_UNIT_TEST(CompressionLz4) {
  594. TestCompression(ECompression::LZ4);
  595. }
  596. Y_UNIT_TEST(Decode_v1_0_histograms) {
  597. // Check that histogram bounds decoded from different versions are the same
  598. NProto::TMultiSamplesList samples, samples_v1_0;
  599. DecodeDataToSamples(samples);
  600. DecodeDataToSamples(samples_v1_0, /*version = */ SV1_00);
  601. const NProto::THistogram& pointHistogram = samples.GetSamples(4).GetPoints(0).GetHistogram();
  602. const NProto::THistogram& pointHistogram_v1_0 = samples_v1_0.GetSamples(4).GetPoints(0).GetHistogram();
  603. for (size_t i = 0; i < pointHistogram.BoundsSize(); i++) {
  604. UNIT_ASSERT_DOUBLES_EQUAL(pointHistogram.GetBounds(i), pointHistogram_v1_0.GetBounds(i), Min<double>());
  605. }
  606. }
  607. Y_UNIT_TEST(SimpleV12) {
  608. ui8 expectedSerialized[] = {
  609. // header
  610. 0x53, 0x50, // magic "SP" (fixed ui16)
  611. // minor, major
  612. 0x02, 0x01, // version (fixed ui16)
  613. 0x18, 0x00, // header size (fixed ui16)
  614. 0x00, // time precision (fixed ui8)
  615. 0x00, // compression algorithm (fixed ui8)
  616. 0x0A, 0x00, 0x00, 0x00, // label names size (fixed ui32)
  617. 0x14, 0x00, 0x00, 0x00, // labels values size (fixed ui32)
  618. 0x01, 0x00, 0x00, 0x00, // metric count (fixed ui32)
  619. 0x01, 0x00, 0x00, 0x00, // points count (fixed ui32)
  620. // string pools
  621. 0x73, 0x00, // "s\0"
  622. 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x00, // "project\0"
  623. 0x73, 0x6f, 0x6c, 0x6f, 0x6d, 0x6f, 0x6e, 0x00, // "solomon\0"
  624. 0x74, 0x65, 0x6D, 0x70, 0x65, 0x72, 0x61, 0x74, // temperature
  625. 0x75, 0x72, 0x65, 0x00,
  626. // common time
  627. 0x00, 0x2f, 0x68, 0x59, // common time in seconds (fixed ui32)
  628. // common labels
  629. 0x00, // common labels count (varint)
  630. // metric
  631. 0x09, // types (COUNTER | ONE_WITHOUT_TS) (fixed ui8)
  632. 0x00, // flags (fixed ui8)
  633. 0x01, // name index (varint)
  634. 0x01, // metric labels count (varint)
  635. 0x01, // 'project' label name index (varint)
  636. 0x00, // 'project' label value index (varint)
  637. 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value (fixed ui64)
  638. };
  639. // encode
  640. {
  641. TBuffer actualSerialized;
  642. {
  643. TBufferOutput out(actualSerialized);
  644. auto e = EncoderSpackV12(
  645. &out,
  646. ETimePrecision::SECONDS,
  647. ECompression::IDENTITY,
  648. EMetricsMergingMode::DEFAULT,
  649. "s");
  650. e->OnStreamBegin();
  651. e->OnCommonTime(TInstant::Seconds(1500000000));
  652. {
  653. e->OnMetricBegin(EMetricType::COUNTER);
  654. {
  655. e->OnLabelsBegin();
  656. e->OnLabel("project", "solomon");
  657. e->OnLabel("s", "temperature");
  658. e->OnLabelsEnd();
  659. }
  660. // Only the last value will be encoded
  661. e->OnUint64(TInstant::Zero(), 10);
  662. e->OnUint64(TInstant::Zero(), 13);
  663. e->OnUint64(TInstant::Zero(), 17);
  664. e->OnMetricEnd();
  665. }
  666. e->OnStreamEnd();
  667. e->Close();
  668. }
  669. UNIT_ASSERT_VALUES_EQUAL(actualSerialized.Size(), Y_ARRAY_SIZE(expectedSerialized));
  670. UNIT_ASSERT_BINARY_EQUALS(actualSerialized.Data(), expectedSerialized);
  671. }
  672. // decode
  673. {
  674. NProto::TMultiSamplesList samples;
  675. {
  676. auto input = TMemoryInput(expectedSerialized, Y_ARRAY_SIZE(expectedSerialized));
  677. auto encoder = EncoderProtobuf(&samples);
  678. DecodeSpackV1(&input, encoder.Get(), "s");
  679. }
  680. UNIT_ASSERT_VALUES_EQUAL(TInstant::MilliSeconds(samples.GetCommonTime()),
  681. TInstant::Seconds(1500000000));
  682. UNIT_ASSERT_VALUES_EQUAL(samples.CommonLabelsSize(), 0);
  683. UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 1);
  684. {
  685. const auto& s = samples.GetSamples(0);
  686. UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::COUNTER);
  687. UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 2);
  688. AssertLabelEqual(s.GetLabels(0), "s", "temperature");
  689. AssertLabelEqual(s.GetLabels(1), "project", "solomon");
  690. UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1);
  691. AssertPointEqual(s.GetPoints(0), TInstant::Zero(), ui64(17));
  692. }
  693. }
  694. }
  695. Y_UNIT_TEST(V12MissingNameForOneMetric) {
  696. TBuffer b;
  697. TBufferOutput out(b);
  698. auto e = EncoderSpackV12(
  699. &out,
  700. ETimePrecision::SECONDS,
  701. ECompression::IDENTITY,
  702. EMetricsMergingMode::DEFAULT,
  703. "s");
  704. UNIT_ASSERT_EXCEPTION_CONTAINS(
  705. [&]() {
  706. e->OnStreamBegin();
  707. {
  708. e->OnMetricBegin(EMetricType::COUNTER);
  709. {
  710. e->OnLabelsBegin();
  711. e->OnLabel("s", "s1");
  712. e->OnLabelsEnd();
  713. }
  714. e->OnUint64(TInstant::Zero(), 1);
  715. e->OnMetricEnd();
  716. e->OnMetricBegin(EMetricType::COUNTER);
  717. {
  718. e->OnLabelsBegin();
  719. e->OnLabel("project", "solomon");
  720. e->OnLabel("m", "v");
  721. e->OnLabelsEnd();
  722. }
  723. e->OnUint64(TInstant::Zero(), 2);
  724. e->OnMetricEnd();
  725. }
  726. e->OnStreamEnd();
  727. e->Close();
  728. }(),
  729. yexception,
  730. "metric name label 's' not found, all metric labels '{m=v, project=solomon}'");
  731. }
  732. }