diff options
| author | kgershov <[email protected]> | 2026-04-06 16:46:20 +0300 |
|---|---|---|
| committer | kgershov <[email protected]> | 2026-04-06 17:08:32 +0300 |
| commit | ee9a12d94efbb3b7bb3e5395fa0b01f724c4d215 (patch) | |
| tree | 7468ea854b171ac0cc66f3b3c09bea56c0d81eda /library/cpp/monlib/encode | |
| parent | f86fc7fae5939cfb2fc83a842cd8043cffcbb9a3 (diff) | |
[library/cpp/monlib] add v3 spack encode/decode
commit_hash:91ce0e2023d29ef78fadeaded314ea236f316b8e
Diffstat (limited to 'library/cpp/monlib/encode')
| -rw-r--r-- | library/cpp/monlib/encode/buffered/string_pool.h | 7 | ||||
| -rw-r--r-- | library/cpp/monlib/encode/spack/spack_v1.h | 10 | ||||
| -rw-r--r-- | library/cpp/monlib/encode/spack/spack_v1_decoder.cpp | 69 | ||||
| -rw-r--r-- | library/cpp/monlib/encode/spack/spack_v1_encoder.cpp | 48 | ||||
| -rw-r--r-- | library/cpp/monlib/encode/spack/spack_v1_ut.cpp | 412 |
5 files changed, 520 insertions, 26 deletions
diff --git a/library/cpp/monlib/encode/buffered/string_pool.h b/library/cpp/monlib/encode/buffered/string_pool.h index b7ab01d2a11..4c41b82258d 100644 --- a/library/cpp/monlib/encode/buffered/string_pool.h +++ b/library/cpp/monlib/encode/buffered/string_pool.h @@ -78,6 +78,13 @@ namespace NMonitoring { InitIndex(data, size); } + TStringPool(const char* data, const TVector<std::pair<ui32, ui32>>& segments) { + Index_.reserve(segments.size()); + for (const auto& [offset, length] : segments) { + Index_.emplace_back(data + offset, length); + } + } + TStringBuf Get(ui32 i) const { return Index_.at(i); } diff --git a/library/cpp/monlib/encode/spack/spack_v1.h b/library/cpp/monlib/encode/spack/spack_v1.h index cf1c9417b90..ce516626e31 100644 --- a/library/cpp/monlib/encode/spack/spack_v1.h +++ b/library/cpp/monlib/encode/spack/spack_v1.h @@ -92,7 +92,8 @@ namespace NMonitoring { enum ESpackV1Version: ui16 { SV1_00 = 0x0100, SV1_01 = 0x0101, - SV1_02 = 0x0102 + SV1_02 = 0x0102, + SV1_03 = 0x0103 }; IMetricEncoderPtr EncoderSpackV1( @@ -110,6 +111,13 @@ namespace NMonitoring { TStringBuf metricNameLabel = "name" ); + IMetricEncoderPtr EncoderSpackV13( + IOutputStream* out, + ETimePrecision timePrecision, + ECompression compression, + EMetricsMergingMode mergingMode = EMetricsMergingMode::DEFAULT + ); + void DecodeSpackV1(IInputStream* in, IMetricConsumer* c, TStringBuf metricNameLabel = "name"); } diff --git a/library/cpp/monlib/encode/spack/spack_v1_decoder.cpp b/library/cpp/monlib/encode/spack/spack_v1_decoder.cpp index a236bb9a509..7d04aa0d555 100644 --- a/library/cpp/monlib/encode/spack/spack_v1_decoder.cpp +++ b/library/cpp/monlib/encode/spack/spack_v1_decoder.cpp @@ -62,22 +62,48 @@ namespace NMonitoring { TimePrecision_ = DecodeTimePrecision(Header_.TimePrecision); - const ui64 labelSizeTotal = ui64(Header_.LabelNamesSize) + Header_.LabelValuesSize; + // (2) read string pools + TVector<char> namesBuf; + TVector<char> valuesBuf; + + if (Header_.Version == SV1_03) { + auto namesResult = ReadLengthDelimitedStringPool(Header_.LabelNamesSize); + namesBuf = std::move(namesResult.first); + auto valuesResult = ReadLengthDelimitedStringPool(Header_.LabelValuesSize); + valuesBuf = std::move(valuesResult.first); + + TStringPool labelNames(namesBuf.data(), namesResult.second); + TStringPool labelValues(valuesBuf.data(), valuesResult.second); + + DecodeBody(labelNames, labelValues, c); + return; + } + const ui64 labelSizeTotal = ui64(Header_.LabelNamesSize) + Header_.LabelValuesSize; DECODE_ENSURE(labelSizeTotal <= LABEL_SIZE_LIMIT, "Label names & values size of " << HumanReadableSize(labelSizeTotal, SF_BYTES) << " exceeds the limit which is " << HumanReadableSize(LABEL_SIZE_LIMIT, SF_BYTES)); - // (2) read string pools - TVector<char> namesBuf(Header_.LabelNamesSize); + namesBuf.resize(Header_.LabelNamesSize); readBytes = In_->Load(namesBuf.data(), namesBuf.size()); DECODE_ENSURE(readBytes == Header_.LabelNamesSize, "not enough data to read label names pool"); - TStringPool labelNames(namesBuf.data(), namesBuf.size()); + { + TStringPool labelNames(namesBuf.data(), namesBuf.size()); + + valuesBuf.resize(Header_.LabelValuesSize); + readBytes = In_->Load(valuesBuf.data(), valuesBuf.size()); + DECODE_ENSURE(readBytes == Header_.LabelValuesSize, "not enough data to read label values pool"); + TStringPool labelValues(valuesBuf.data(), valuesBuf.size()); - TVector<char> valuesBuf(Header_.LabelValuesSize); - readBytes = In_->Load(valuesBuf.data(), valuesBuf.size()); - DECODE_ENSURE(readBytes == Header_.LabelValuesSize, "not enough data to read label values pool"); - TStringPool labelValues(valuesBuf.data(), valuesBuf.size()); + DecodeBody(labelNames, labelValues, c); + } + } + private: + void DecodeBody( + const TStringPool& labelNames, + const TStringPool& labelValues, + IMetricConsumer* c) + { // (3) read common time c->OnCommonTime(ReadTime()); @@ -93,7 +119,6 @@ namespace NMonitoring { c->OnStreamEnd(); } - private: void ReadMetrics( const TStringPool& labelNames, const TStringPool& labelValues, @@ -111,15 +136,15 @@ namespace NMonitoring { c->OnMemOnly(ReadFixed<ui8>() & 0x01); auto metricNameValueIndex = std::numeric_limits<ui32>::max(); - if (Header_.Version >= SV1_02) { + if (Header_.Version == SV1_02) { metricNameValueIndex = ReadVarint(); } // (5.3) labels ui32 labelsCount = ReadVarint(); - DECODE_ENSURE(Header_.Version >= SV1_02 || labelsCount > 0, "metric #" << i << " has no labels"); + DECODE_ENSURE(Header_.Version == SV1_02 || labelsCount > 0, "metric #" << i << " has no labels"); c->OnLabelsBegin(); - if (Header_.Version >= SV1_02) { + if (Header_.Version == SV1_02) { c->OnLabel(MetricNameLabel_, labelValues.Get(metricNameValueIndex)); } ReadLabels(labelNames, labelValues, labelsCount, c); @@ -273,6 +298,26 @@ namespace NMonitoring { return ReadVarUInt32(In_); } + std::pair<TVector<char>, TVector<std::pair<ui32, ui32>>> ReadLengthDelimitedStringPool(ui32 count) { + TVector<char> storage; + TVector<std::pair<ui32, ui32>> segments; + segments.reserve(count); + + for (ui32 i = 0; i < count; i++) { + ui32 len = ReadVarint(); + ui32 offset = static_cast<ui32>(storage.size()); + storage.resize(offset + len); + if (len > 0) { + size_t readBytes = In_->Load(storage.data() + offset, len); + DECODE_ENSURE(readBytes == len, "not enough data to read string #" << i + << " (expected " << len << " bytes, got " << readBytes << ")"); + } + segments.emplace_back(offset, len); + } + + return {std::move(storage), std::move(segments)}; + } + private: IInputStream* In_; TString MetricNameLabel_; diff --git a/library/cpp/monlib/encode/spack/spack_v1_encoder.cpp b/library/cpp/monlib/encode/spack/spack_v1_encoder.cpp index 70c5bba5512..ac9934b0593 100644 --- a/library/cpp/monlib/encode/spack/spack_v1_encoder.cpp +++ b/library/cpp/monlib/encode/spack/spack_v1_encoder.cpp @@ -31,7 +31,7 @@ namespace NMonitoring { , TimePrecision_(timePrecision) , Compression_(compression) , Version_(version) - , MetricName_(Version_ >= SV1_02 ? LabelNamesPool_.PutIfAbsent(metricNameLabel) : nullptr) + , MetricName_(Version_ == SV1_02 ? LabelNamesPool_.PutIfAbsent(metricNameLabel) : nullptr) { MetricsMergingMode_ = mergingMode; @@ -96,10 +96,15 @@ namespace NMonitoring { header.Version = Version_; header.TimePrecision = EncodeTimePrecision(TimePrecision_); header.Compression = EncodeCompression(Compression_); - header.LabelNamesSize = static_cast<ui32>( - LabelNamesPool_.BytesSize() + LabelNamesPool_.Count()); - header.LabelValuesSize = static_cast<ui32>( - LabelValuesPool_.BytesSize() + LabelValuesPool_.Count()); + if (Version_ == SV1_03) { + header.LabelNamesSize = static_cast<ui32>(LabelNamesPool_.Count()); + header.LabelValuesSize = static_cast<ui32>(LabelValuesPool_.Count()); + } else { + header.LabelNamesSize = static_cast<ui32>( + LabelNamesPool_.BytesSize() + LabelNamesPool_.Count()); + header.LabelValuesSize = static_cast<ui32>( + LabelValuesPool_.BytesSize() + LabelValuesPool_.Count()); + } header.MetricCount = Metrics_.size(); header.PointsCount = pointsCount; Out_->Write(&header, sizeof(header)); @@ -111,13 +116,21 @@ namespace NMonitoring { } // (2) write string pools - auto strPoolWrite = [this](TStringBuf str, ui32, ui32) { - Out_->Write(str); - Out_->Write('\0'); - }; - - LabelNamesPool_.ForEach(strPoolWrite); - LabelValuesPool_.ForEach(strPoolWrite); + if (Version_ == SV1_03) { + auto strPoolWrite = [this](TStringBuf str, ui32, ui32) { + WriteVarUInt32(Out_, static_cast<ui32>(str.size())); + Out_->Write(str); + }; + LabelNamesPool_.ForEach(strPoolWrite); + LabelValuesPool_.ForEach(strPoolWrite); + } else { + auto strPoolWrite = [this](TStringBuf str, ui32, ui32) { + Out_->Write(str); + Out_->Write('\0'); + }; + LabelNamesPool_.ForEach(strPoolWrite); + LabelValuesPool_.ForEach(strPoolWrite); + } // (3) write common time WriteTime(CommonTime_); @@ -137,7 +150,7 @@ namespace NMonitoring { Out_->Write(&flagsByte, sizeof(flagsByte)); // v1.2 format addition — metric name - if (Version_ >= SV1_02) { + if (Version_ == SV1_02) { const auto it = FindIf(metric.Labels, [&](const auto& l) { return l.Key == MetricName_; }); @@ -319,4 +332,13 @@ namespace NMonitoring { Y_ENSURE(!metricNameLabel.empty(), "metricNameLabel can't be empty"); return MakeHolder<TEncoderSpackV1>(out, timePrecision, compression, mergingMode, SV1_02, metricNameLabel); } + + IMetricEncoderPtr EncoderSpackV13( + IOutputStream* out, + ETimePrecision timePrecision, + ECompression compression, + EMetricsMergingMode mergingMode + ) { + return MakeHolder<TEncoderSpackV1>(out, timePrecision, compression, mergingMode, SV1_03, ""); + } } diff --git a/library/cpp/monlib/encode/spack/spack_v1_ut.cpp b/library/cpp/monlib/encode/spack/spack_v1_ut.cpp index bbc3b8712b9..2a75f441f2d 100644 --- a/library/cpp/monlib/encode/spack/spack_v1_ut.cpp +++ b/library/cpp/monlib/encode/spack/spack_v1_ut.cpp @@ -2,6 +2,7 @@ #include <library/cpp/monlib/encode/protobuf/protobuf.h> #include <library/cpp/monlib/metrics/labels.h> +#include <library/cpp/monlib/metrics/histogram_snapshot.h> #include <library/cpp/monlib/metrics/metric.h> #include <library/cpp/testing/unittest/registar.h> @@ -811,6 +812,417 @@ Y_UNIT_TEST_SUITE(TSpackTest) { } } + Y_UNIT_TEST(SimpleV13) { + ui8 expectedSerialized[] = { + // header + 0x53, 0x50, // magic "SP" (fixed ui16) + // minor, major + 0x03, 0x01, // version (fixed ui16) + 0x18, 0x00, // header size (fixed ui16) + 0x00, // time precision (fixed ui8) + 0x00, // compression algorithm (fixed ui8) + 0x02, 0x00, 0x00, 0x00, // label names count (fixed ui32) -- 2 names: "project", "name" + 0x03, 0x00, 0x00, 0x00, // label values count (fixed ui32) -- 3 values: "solomon", "temperature", "speed" + 0x02, 0x00, 0x00, 0x00, // metric count (fixed ui32) + 0x02, 0x00, 0x00, 0x00, // points count (fixed ui32) + + // string pools (length-delimited) + // label names (sorted by frequency: "name" appears 2x, "project" 1x) + 0x04, 0x6e, 0x61, 0x6d, 0x65, // varint(4) + "name" + 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, // varint(7) + "project" + // label values (sorted by frequency: all appear 1x, insertion order preserved) + 0x07, 0x73, 0x6f, 0x6c, 0x6f, 0x6d, 0x6f, 0x6e, // varint(7) + "solomon" + 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x65, 0x72, 0x61, // varint(11) + "temperature" + 0x74, 0x75, 0x72, 0x65, + 0x05, 0x73, 0x70, 0x65, 0x65, 0x64, // varint(5) + "speed" + + // common time + 0x00, 0x2f, 0x68, 0x59, // common time in seconds (fixed ui32) + + // common labels + 0x01, // common labels count (varint) + 0x01, // label name index (project) (varint) + 0x00, // label value index (solomon) (varint) + + // metric 1: COUNTER, ONE_WITHOUT_TS + 0x09, // types (COUNTER | ONE_WITHOUT_TS) (fixed ui8) + 0x00, // flags (fixed ui8) + // no metric name index (v1_3 does not have it) + 0x01, // metric labels count (varint) + 0x00, // label name index (name) (varint) + 0x01, // label value index (temperature) (varint) + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // value 17 (fixed ui64) + + // metric 2: GAUGE, ONE_WITH_TS + 0x06, // types (GAUGE | ONE_WITH_TS) (fixed ui8) + 0x00, // flags (fixed ui8) + 0x01, // metric labels count (varint) + 0x00, // label name index (name) (varint) + 0x02, // label value index (speed) (varint) + 0x0b, 0x63, 0xfe, 0x59, // time in seconds (fixed ui32) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x40, // value 42.0 (double IEEE754) + }; + + // encode + { + TBuffer actualSerialized; + { + TBufferOutput out(actualSerialized); + auto e = EncoderSpackV13( + &out, + ETimePrecision::SECONDS, + ECompression::IDENTITY); + + e->OnStreamBegin(); + e->OnCommonTime(TInstant::Seconds(1500000000)); + { + e->OnLabelsBegin(); + e->OnLabel("project", "solomon"); + e->OnLabelsEnd(); + } + + { + e->OnMetricBegin(EMetricType::COUNTER); + { + e->OnLabelsBegin(); + e->OnLabel("name", "temperature"); + e->OnLabelsEnd(); + } + e->OnUint64(TInstant::Zero(), 17); + e->OnMetricEnd(); + } + + { + e->OnMetricBegin(EMetricType::GAUGE); + { + e->OnLabelsBegin(); + e->OnLabel("name", "speed"); + e->OnLabelsEnd(); + } + e->OnDouble(now, 42); + e->OnMetricEnd(); + } + + e->OnStreamEnd(); + e->Close(); + } + + UNIT_ASSERT_VALUES_EQUAL(actualSerialized.Size(), Y_ARRAY_SIZE(expectedSerialized)); + UNIT_ASSERT_BINARY_EQUALS(actualSerialized.Data(), expectedSerialized); + } + + // decode + { + NProto::TMultiSamplesList samples; + { + auto input = TMemoryInput(expectedSerialized, Y_ARRAY_SIZE(expectedSerialized)); + auto encoder = EncoderProtobuf(&samples); + DecodeSpackV1(&input, encoder.Get()); + } + + UNIT_ASSERT_VALUES_EQUAL(TInstant::MilliSeconds(samples.GetCommonTime()), + TInstant::Seconds(1500000000)); + + UNIT_ASSERT_VALUES_EQUAL(samples.CommonLabelsSize(), 1); + AssertLabelEqual(samples.GetCommonLabels(0), "project", "solomon"); + + UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 2); + { + const auto& s = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::COUNTER); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + AssertLabelEqual(s.GetLabels(0), "name", "temperature"); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1); + AssertPointEqual(s.GetPoints(0), TInstant::Zero(), ui64(17)); + } + { + const auto& s = samples.GetSamples(1); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::GAUGE); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + AssertLabelEqual(s.GetLabels(0), "name", "speed"); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1); + AssertPointEqual(s.GetPoints(0), now, double(42)); + } + } + } + + // Ported from Java MetricSpackEncoderTest.doEncodeDecode + void V13EncodeDecodeImpl(ETimePrecision timePrecision, ECompression compression) { + const TInstant commonTime = TInstant::Seconds(1500000000); + const TInstant alignedNow = (timePrecision == ETimePrecision::SECONDS) + ? TInstant::Seconds(now.Seconds()) + : now; + + // label value with embedded null bytes (v1_3 feature) + TStringBuilder nullLabelBuilder; + nullLabelBuilder << "thįs"; + nullLabelBuilder << '\0'; + nullLabelBuilder << "īs"; + nullLabelBuilder << '\0'; + nullLabelBuilder << "fïne"; + + TString nullLabel = nullLabelBuilder; + UNIT_ASSERT_VALUES_EQUAL(nullLabel.size(), 15u); + + TBuffer buffer; + { + TBufferOutput out(buffer); + auto e = EncoderSpackV13(&out, timePrecision, compression); + + e->OnStreamBegin(); + e->OnCommonTime(commonTime); + { + e->OnLabelsBegin(); + e->OnLabel("project", "solomon"); + e->OnLabel("cluster", "production"); + e->OnLabel("service", "stockpile"); + e->OnLabelsEnd(); + } + { // metric #1 (no values) + e->OnMetricBegin(EMetricType::GAUGE); + { + e->OnLabelsBegin(); + e->OnLabel("sensor", "q1"); + e->OnLabelsEnd(); + } + e->OnMetricEnd(); + } + { // metric #2 (one value without ts) + e->OnMetricBegin(EMetricType::COUNTER); + { + e->OnLabelsBegin(); + e->OnLabel("sensor", "q2"); + e->OnLabelsEnd(); + } + e->OnUint64(TInstant::Zero(), 42); + e->OnMetricEnd(); + } + { // metric #3 (one value with ts) + e->OnMetricBegin(EMetricType::GAUGE); + { + e->OnLabelsBegin(); + e->OnLabel("sensor", "q3"); + e->OnLabelsEnd(); + } + e->OnDouble(alignedNow + TDuration::Seconds(5), 3.14159); + e->OnMetricEnd(); + } + { // metric #4 (many values with ts) + e->OnMetricBegin(EMetricType::COUNTER); + { + e->OnLabelsBegin(); + e->OnLabel("sensor", "q4"); + e->OnLabelsEnd(); + } + e->OnUint64(alignedNow + TDuration::Seconds(5), 43); + e->OnUint64(alignedNow + TDuration::Seconds(10), 44); + e->OnUint64(alignedNow + TDuration::Seconds(15), 45); + e->OnMetricEnd(); + } + { // metric #5 (igauge) + e->OnMetricBegin(EMetricType::IGAUGE); + { + e->OnLabelsBegin(); + e->OnLabel("sensor", "q5"); + e->OnLabelsEnd(); + } + e->OnInt64(alignedNow, 46); + e->OnMetricEnd(); + } + { // metric #6 (hist_rate) + e->OnMetricBegin(EMetricType::HIST_RATE); + { + e->OnLabelsBegin(); + e->OnLabel("sensor", "q6"); + e->OnLabelsEnd(); + } + auto h = ExplicitHistogramSnapshot({10, 20, 30, Max<double>()}, {0, 1, 0, 100}); + e->OnHistogram(alignedNow, h); + e->OnMetricEnd(); + } + { // metric #7 (hist, many values) + e->OnMetricBegin(EMetricType::HIST); + { + e->OnLabelsBegin(); + e->OnLabel("sensor", "q7"); + e->OnLabelsEnd(); + } + { + auto h = ExplicitHistogramSnapshot({10, 20, 30, Max<double>()}, {0, 1, 0, 13}); + e->OnHistogram(alignedNow + TDuration::Seconds(5), h); + } + { + auto h = ExplicitHistogramSnapshot({10, 20, 30, Max<double>()}, {1, 0, 2, 42}); + e->OnHistogram(alignedNow + TDuration::Seconds(10), h); + } + e->OnMetricEnd(); + } + { // metric #8 (counter with embedded null bytes in label) + e->OnMetricBegin(EMetricType::COUNTER); + { + e->OnLabelsBegin(); + e->OnLabel("sensor", nullLabel); + e->OnLabelsEnd(); + } + e->OnUint64(TInstant::Zero(), 1234); + e->OnMetricEnd(); + } + { // metric #9 (mem-only) + e->OnMetricBegin(EMetricType::COUNTER); + { + e->OnLabelsBegin(); + e->OnLabel("sensor", "aggregate"); + e->OnLabelsEnd(); + } + e->OnUint64(TInstant::Zero(), 42); + e->OnMemOnly(true); + e->OnMetricEnd(); + } + e->OnStreamEnd(); + e->Close(); + } + + // verify header + auto* header = reinterpret_cast<const TSpackHeader*>(buffer.Data()); + UNIT_ASSERT_VALUES_EQUAL(header->Version, static_cast<ui16>(SV1_03)); + UNIT_ASSERT_VALUES_EQUAL(DecodeCompression(header->Compression), compression); + UNIT_ASSERT_VALUES_EQUAL(header->MetricCount, 9u); + UNIT_ASSERT_VALUES_EQUAL(header->PointsCount, 11u); + + // decode and verify + NProto::TMultiSamplesList samples; + { + IMetricEncoderPtr e = EncoderProtobuf(&samples); + TBufferInput in(buffer); + DecodeSpackV1(&in, e.Get()); + } + + UNIT_ASSERT_VALUES_EQUAL( + TInstant::MilliSeconds(samples.GetCommonTime()), + commonTime); + + UNIT_ASSERT_VALUES_EQUAL(samples.CommonLabelsSize(), 3); + AssertLabelEqual(samples.GetCommonLabels(0), "project", "solomon"); + AssertLabelEqual(samples.GetCommonLabels(1), "cluster", "production"); + AssertLabelEqual(samples.GetCommonLabels(2), "service", "stockpile"); + + UNIT_ASSERT_VALUES_EQUAL(samples.SamplesSize(), 9); + { // #1 no values + const auto& s = samples.GetSamples(0); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::GAUGE); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + AssertLabelEqual(s.GetLabels(0), "sensor", "q1"); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 0); + } + { // #2 one value without ts + const auto& s = samples.GetSamples(1); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::COUNTER); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + AssertLabelEqual(s.GetLabels(0), "sensor", "q2"); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1); + AssertPointEqual(s.GetPoints(0), TInstant::Zero(), ui64(42)); + } + { // #3 one value with ts + const auto& s = samples.GetSamples(2); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::GAUGE); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + AssertLabelEqual(s.GetLabels(0), "sensor", "q3"); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1); + AssertPointEqual(s.GetPoints(0), alignedNow + TDuration::Seconds(5), 3.14159); + } + { // #4 many values with ts + const auto& s = samples.GetSamples(3); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::COUNTER); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + AssertLabelEqual(s.GetLabels(0), "sensor", "q4"); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 3); + AssertPointEqual(s.GetPoints(0), alignedNow + TDuration::Seconds(5), ui64(43)); + AssertPointEqual(s.GetPoints(1), alignedNow + TDuration::Seconds(10), ui64(44)); + AssertPointEqual(s.GetPoints(2), alignedNow + TDuration::Seconds(15), ui64(45)); + } + { // #5 igauge + const auto& s = samples.GetSamples(4); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::IGAUGE); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + AssertLabelEqual(s.GetLabels(0), "sensor", "q5"); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1); + AssertPointEqual(s.GetPoints(0), alignedNow, i64(46)); + } + { // #6 hist_rate + const auto& s = samples.GetSamples(5); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::HIST_RATE); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + AssertLabelEqual(s.GetLabels(0), "sensor", "q6"); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1); + + const auto& h = s.GetPoints(0).GetHistogram(); + UNIT_ASSERT_VALUES_EQUAL(h.BoundsSize(), 4u); + UNIT_ASSERT_VALUES_EQUAL(h.ValuesSize(), 4u); + } + { // #7 hist, many + const auto& s = samples.GetSamples(6); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::HISTOGRAM); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + AssertLabelEqual(s.GetLabels(0), "sensor", "q7"); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 2); + } + { // #8 embedded null bytes in label + const auto& s = samples.GetSamples(7); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::COUNTER); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + UNIT_ASSERT_STRINGS_EQUAL(s.GetLabels(0).GetName(), "sensor"); + UNIT_ASSERT_VALUES_EQUAL(s.GetLabels(0).GetValue().size(), nullLabel.size()); + UNIT_ASSERT_VALUES_EQUAL(s.GetLabels(0).GetValue(), nullLabel); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1); + AssertPointEqual(s.GetPoints(0), TInstant::Zero(), ui64(1234)); + } + { // #9 mem-only + const auto& s = samples.GetSamples(8); + UNIT_ASSERT_EQUAL(s.GetMetricType(), NProto::COUNTER); + UNIT_ASSERT_EQUAL(s.GetIsMemOnly(), true); + UNIT_ASSERT_VALUES_EQUAL(s.LabelsSize(), 1); + AssertLabelEqual(s.GetLabels(0), "sensor", "aggregate"); + UNIT_ASSERT_VALUES_EQUAL(s.PointsSize(), 1); + AssertPointEqual(s.GetPoints(0), TInstant::Zero(), ui64(42)); + } + } + + Y_UNIT_TEST(V13EncodeDecodeSeconds) { + V13EncodeDecodeImpl(ETimePrecision::SECONDS, ECompression::IDENTITY); + } + + Y_UNIT_TEST(V13EncodeDecodeMillis) { + V13EncodeDecodeImpl(ETimePrecision::MILLIS, ECompression::IDENTITY); + } + + Y_UNIT_TEST(V13EncodeDecodeZlib) { + V13EncodeDecodeImpl(ETimePrecision::SECONDS, ECompression::ZLIB); + } + + Y_UNIT_TEST(V13EncodeDecodeZstd) { + V13EncodeDecodeImpl(ETimePrecision::SECONDS, ECompression::ZSTD); + } + + Y_UNIT_TEST(V13EncodeDecodeLz4) { + V13EncodeDecodeImpl(ETimePrecision::SECONDS, ECompression::LZ4); + } + + Y_UNIT_TEST(V13EmptyStream) { + TBuffer buffer; + { + TBufferOutput out(buffer); + auto e = EncoderSpackV13(&out, ETimePrecision::SECONDS, ECompression::IDENTITY); + e->OnStreamBegin(); + e->OnStreamEnd(); + e->Close(); + } + + auto* header = reinterpret_cast<const TSpackHeader*>(buffer.Data()); + UNIT_ASSERT_VALUES_EQUAL(header->Version, static_cast<ui16>(SV1_03)); + UNIT_ASSERT_VALUES_EQUAL(header->MetricCount, 0u); + UNIT_ASSERT_VALUES_EQUAL(header->PointsCount, 0u); + } + Y_UNIT_TEST(V12MissingNameForOneMetric) { TBuffer b; TBufferOutput out(b); |
