From 0e5da205a68f77919f4f653eb6bccb61fca45aa7 Mon Sep 17 00:00:00 2001 From: Sandeep Sukhani Date: Thu, 21 Dec 2023 18:03:09 +0530 Subject: [PATCH] structured metadata: track usage of structured metadata in queries (#11505) **What this PR does / why we need it**: Adds support for tracking queries referencing structured metadata. It tracks the following kinds of references: * Fields in structured metadata used as label filters, `drop`/`keep` stages etc. in the query. * Fields in structured metadata used in metric queries like `uwrap` and `by` or `without` clauses in aggregations. **Checklist** - [x] Tests updated --- pkg/chunkenc/memchunk.go | 28 +++ pkg/chunkenc/memchunk_test.go | 3 + pkg/chunkenc/unordered.go | 7 + pkg/ingester/instance_test.go | 8 + pkg/logql/log/labels.go | 27 ++- pkg/logql/log/labels_test.go | 29 ++- pkg/logql/log/metrics_extraction.go | 13 ++ pkg/logql/log/metrics_extraction_test.go | 4 + pkg/logql/log/pipeline.go | 13 ++ pkg/logql/log/pipeline_test.go | 4 + pkg/logql/metrics.go | 1 + pkg/logqlmodel/stats/context.go | 12 ++ pkg/logqlmodel/stats/context_test.go | 32 +-- pkg/logqlmodel/stats/stats.pb.go | 197 ++++++++++------- pkg/logqlmodel/stats/stats.proto | 2 + pkg/querier/queryrange/codec_test.go | 15 +- pkg/querier/queryrange/prometheus_test.go | 2 + pkg/storage/store_test.go | 247 +++++++++++++++++++++- pkg/util/marshal/legacy/marshal_test.go | 2 + pkg/util/marshal/marshal_test.go | 2 + 20 files changed, 543 insertions(+), 105 deletions(-) diff --git a/pkg/chunkenc/memchunk.go b/pkg/chunkenc/memchunk.go index f0a5127480..df59db7c75 100644 --- a/pkg/chunkenc/memchunk.go +++ b/pkg/chunkenc/memchunk.go @@ -1231,6 +1231,10 @@ func (hb *headBlock) Iterator(ctx context.Context, direction logproto.Direction, } } + if pipeline.ReferencedStructuredMetadata() { + stats.SetQueryReferencedStructuredMetadata() + } + if len(streams) == 0 { return iter.NoopIterator } @@ -1279,6 +1283,9 @@ func (hb *headBlock) SampleIterator(ctx context.Context, mint, maxt int64, extra }) } + if extractor.ReferencedStructuredMetadata() { + stats.SetQueryReferencedStructuredMetadata() + } if len(series) == 0 { return iter.NoopIterator } @@ -1586,12 +1593,14 @@ func newEntryIterator(ctx context.Context, pool ReaderPool, b []byte, pipeline l return &entryBufferedIterator{ bufferedIterator: newBufferedIterator(ctx, pool, b, format, symbolizer), pipeline: pipeline, + stats: stats.FromContext(ctx), } } type entryBufferedIterator struct { *bufferedIterator pipeline log.StreamPipeline + stats *stats.Context cur logproto.Entry currLabels log.LabelsResult @@ -1624,10 +1633,19 @@ func (e *entryBufferedIterator) Next() bool { return false } +func (e *entryBufferedIterator) Close() error { + if e.pipeline.ReferencedStructuredMetadata() { + e.stats.SetQueryReferencedStructuredMetadata() + } + + return e.bufferedIterator.Close() +} + func newSampleIterator(ctx context.Context, pool ReaderPool, b []byte, format byte, extractor log.StreamSampleExtractor, symbolizer *symbolizer) iter.SampleIterator { it := &sampleBufferedIterator{ bufferedIterator: newBufferedIterator(ctx, pool, b, format, symbolizer), extractor: extractor, + stats: stats.FromContext(ctx), } return it } @@ -1636,6 +1654,7 @@ type sampleBufferedIterator struct { *bufferedIterator extractor log.StreamSampleExtractor + stats *stats.Context cur logproto.Sample currLabels log.LabelsResult @@ -1656,6 +1675,15 @@ func (e *sampleBufferedIterator) Next() bool { } return false } + +func (e *sampleBufferedIterator) Close() error { + if e.extractor.ReferencedStructuredMetadata() { + e.stats.SetQueryReferencedStructuredMetadata() + } + + return e.bufferedIterator.Close() +} + func (e *sampleBufferedIterator) Labels() string { return e.currLabels.String() } func (e *sampleBufferedIterator) StreamHash() uint64 { return e.extractor.BaseLabels().Hash() } diff --git a/pkg/chunkenc/memchunk_test.go b/pkg/chunkenc/memchunk_test.go index 151ad846ec..071d7bab21 100644 --- a/pkg/chunkenc/memchunk_test.go +++ b/pkg/chunkenc/memchunk_test.go @@ -849,6 +849,9 @@ func (nomatchPipeline) Process(_ int64, line []byte, _ ...labels.Label) ([]byte, func (nomatchPipeline) ProcessString(_ int64, line string, _ ...labels.Label) (string, log.LabelsResult, bool) { return line, nil, false } +func (nomatchPipeline) ReferencedStructuredMetadata() bool { + return false +} func BenchmarkRead(b *testing.B) { for _, bs := range testBlockSizes { diff --git a/pkg/chunkenc/unordered.go b/pkg/chunkenc/unordered.go index cd7ea5dec1..883be05154 100644 --- a/pkg/chunkenc/unordered.go +++ b/pkg/chunkenc/unordered.go @@ -282,6 +282,9 @@ func (hb *unorderedHeadBlock) Iterator(ctx context.Context, direction logproto.D }, ) + if pipeline.ReferencedStructuredMetadata() { + stats.FromContext(ctx).SetQueryReferencedStructuredMetadata() + } if len(streams) == 0 { return iter.NoopIterator } @@ -335,6 +338,10 @@ func (hb *unorderedHeadBlock) SampleIterator( }, ) + if extractor.ReferencedStructuredMetadata() { + stats.FromContext(ctx).SetQueryReferencedStructuredMetadata() + } + if len(series) == 0 { return iter.NoopIterator } diff --git a/pkg/ingester/instance_test.go b/pkg/ingester/instance_test.go index d96960da6c..48c3a8b0bc 100644 --- a/pkg/ingester/instance_test.go +++ b/pkg/ingester/instance_test.go @@ -754,6 +754,10 @@ type mockStreamPipeline struct { called int } +func (p *mockStreamPipeline) ReferencedStructuredMetadata() bool { + return false +} + func (p *mockStreamPipeline) BaseLabels() log.LabelsResult { return p.wrappedSP.BaseLabels() } @@ -839,6 +843,10 @@ type mockStreamExtractor struct { called int } +func (p *mockStreamExtractor) ReferencedStructuredMetadata() bool { + return false +} + func (p *mockStreamExtractor) BaseLabels() log.LabelsResult { return p.wrappedSP.BaseLabels() } diff --git a/pkg/logql/log/labels.go b/pkg/logql/log/labels.go index 567c446b8c..76a1ae0d7d 100644 --- a/pkg/logql/log/labels.go +++ b/pkg/logql/log/labels.go @@ -134,9 +134,10 @@ type BaseLabelsBuilder struct { // nolint:structcheck errDetails string - groups []string - parserKeyHints ParserHint // label key hints for metric queries that allows to limit parser extractions to only this list of labels. - without, noLabels bool + groups []string + parserKeyHints ParserHint // label key hints for metric queries that allows to limit parser extractions to only this list of labels. + without, noLabels bool + referencedStructuredMetadata bool resultCache map[uint64]LabelsResult *hasher @@ -286,6 +287,16 @@ func (b *LabelsBuilder) BaseHas(key string) bool { // GetWithCategory returns the value and the category of a labels key if it exists. func (b *LabelsBuilder) GetWithCategory(key string) (string, LabelCategory, bool) { + v, category, ok := b.getWithCategory(key) + if category == StructuredMetadataLabel { + b.referencedStructuredMetadata = true + } + + return v, category, ok +} + +// GetWithCategory returns the value and the category of a labels key if it exists. +func (b *LabelsBuilder) getWithCategory(key string) (string, LabelCategory, bool) { for category, lbls := range b.add { for _, l := range lbls { if l.Name == key { @@ -596,9 +607,12 @@ Outer: continue Outer } } - for _, la := range b.add { + for category, la := range b.add { for _, l := range la { if g == l.Name { + if LabelCategory(category) == StructuredMetadataLabel { + b.referencedStructuredMetadata = true + } b.buf = append(b.buf, l) continue Outer } @@ -646,11 +660,14 @@ Outer: b.buf = append(b.buf, l) } - for _, lbls := range b.add { + for category, lbls := range b.add { OuterAdd: for _, la := range lbls { for _, lg := range b.groups { if la.Name == lg { + if LabelCategory(category) == StructuredMetadataLabel { + b.referencedStructuredMetadata = true + } continue OuterAdd } } diff --git a/pkg/logql/log/labels_test.go b/pkg/logql/log/labels_test.go index 25cebfc5ff..b4efaaa1b6 100644 --- a/pkg/logql/log/labels_test.go +++ b/pkg/logql/log/labels_test.go @@ -16,8 +16,19 @@ func TestLabelsBuilder_Get(t *testing.T) { b.Reset() b.Set(StructuredMetadataLabel, "foo", "bar") b.Set(ParsedLabel, "bar", "buzz") + + _, category, ok := b.GetWithCategory("bar") + require.Equal(t, ParsedLabel, category) + require.True(t, ok) + require.False(t, b.referencedStructuredMetadata) + + _, category, ok = b.GetWithCategory("foo") + require.Equal(t, StructuredMetadataLabel, category) + require.True(t, ok) + require.True(t, b.referencedStructuredMetadata) + b.Del("foo") - _, _, ok := b.GetWithCategory("foo") + _, _, ok = b.GetWithCategory("foo") require.False(t, ok) v, category, ok := b.GetWithCategory("bar") require.True(t, ok) @@ -136,6 +147,12 @@ func TestLabelsBuilder_GroupedLabelsResult(t *testing.T) { b.Reset() b.Set(StreamLabel, "namespace", "tempo") assertLabelResult(t, labels.FromStrings("job", "us-central1/loki"), b.GroupedLabels()) + require.False(t, b.referencedStructuredMetadata) + + b = NewBaseLabelsBuilderWithGrouping([]string{"foo"}, nil, false, false).ForLabels(lbs, lbs.Hash()) + b.Set(StructuredMetadataLabel, "foo", "bar") + assertLabelResult(t, labels.FromStrings("foo", "bar"), b.GroupedLabels()) + require.True(t, b.referencedStructuredMetadata) b = NewBaseLabelsBuilderWithGrouping([]string{"job"}, nil, true, false).ForLabels(lbs, lbs.Hash()) b.Del("job") @@ -146,6 +163,16 @@ func TestLabelsBuilder_GroupedLabelsResult(t *testing.T) { "foo", "bar", ) assertLabelResult(t, expected, b.GroupedLabels()) + require.False(t, b.referencedStructuredMetadata) + + b = NewBaseLabelsBuilderWithGrouping([]string{"foo"}, nil, true, false).ForLabels(lbs, lbs.Hash()) + b.Set(StructuredMetadataLabel, "foo", "bar") + expected = labels.FromStrings("namespace", "loki", + "job", "us-central1/loki", + "cluster", "us-central1", + ) + assertLabelResult(t, expected, b.GroupedLabels()) + require.True(t, b.referencedStructuredMetadata) b = NewBaseLabelsBuilderWithGrouping(nil, nil, false, false).ForLabels(lbs, lbs.Hash()) b.Set(StructuredMetadataLabel, "foo", "bar") diff --git a/pkg/logql/log/metrics_extraction.go b/pkg/logql/log/metrics_extraction.go index 5dce57af82..e8605f6b29 100644 --- a/pkg/logql/log/metrics_extraction.go +++ b/pkg/logql/log/metrics_extraction.go @@ -37,6 +37,7 @@ type StreamSampleExtractor interface { BaseLabels() LabelsResult Process(ts int64, line []byte, structuredMetadata ...labels.Label) (float64, LabelsResult, bool) ProcessString(ts int64, line string, structuredMetadata ...labels.Label) (float64, LabelsResult, bool) + ReferencedStructuredMetadata() bool } // SampleExtractorWrapper takes an extractor, wraps it is some desired functionality @@ -87,6 +88,10 @@ type streamLineSampleExtractor struct { builder *LabelsBuilder } +func (l *streamLineSampleExtractor) ReferencedStructuredMetadata() bool { + return l.builder.referencedStructuredMetadata +} + func (l *streamLineSampleExtractor) Process(ts int64, line []byte, structuredMetadata ...labels.Label) (float64, LabelsResult, bool) { l.builder.Reset() l.builder.Add(StructuredMetadataLabel, structuredMetadata...) @@ -164,6 +169,10 @@ type streamLabelSampleExtractor struct { builder *LabelsBuilder } +func (l *labelSampleExtractor) ReferencedStructuredMetadata() bool { + return l.baseBuilder.referencedStructuredMetadata +} + func (l *labelSampleExtractor) ForStream(labels labels.Labels) StreamSampleExtractor { hash := l.baseBuilder.Hash(labels) if res, ok := l.streamExtractors[hash]; ok { @@ -254,6 +263,10 @@ type filteringStreamExtractor struct { extractor StreamSampleExtractor } +func (sp *filteringStreamExtractor) ReferencedStructuredMetadata() bool { + return false +} + func (sp *filteringStreamExtractor) BaseLabels() LabelsResult { return sp.extractor.BaseLabels() } diff --git a/pkg/logql/log/metrics_extraction_test.go b/pkg/logql/log/metrics_extraction_test.go index 46f65498d9..f059271cb8 100644 --- a/pkg/logql/log/metrics_extraction_test.go +++ b/pkg/logql/log/metrics_extraction_test.go @@ -485,3 +485,7 @@ func (p *stubStreamExtractor) Process(_ int64, _ []byte, _ ...labels.Label) (flo func (p *stubStreamExtractor) ProcessString(_ int64, _ string, _ ...labels.Label) (float64, LabelsResult, bool) { return 0, nil, true } + +func (p *stubStreamExtractor) ReferencedStructuredMetadata() bool { + return false +} diff --git a/pkg/logql/log/pipeline.go b/pkg/logql/log/pipeline.go index df7f4ba657..1396e552c6 100644 --- a/pkg/logql/log/pipeline.go +++ b/pkg/logql/log/pipeline.go @@ -26,6 +26,7 @@ type StreamPipeline interface { // The buffer returned for the log line can be reused on subsequent calls to Process and therefore must be copied. Process(ts int64, line []byte, structuredMetadata ...labels.Label) (resultLine []byte, resultLabels LabelsResult, matches bool) ProcessString(ts int64, line string, structuredMetadata ...labels.Label) (resultLine string, resultLabels LabelsResult, matches bool) + ReferencedStructuredMetadata() bool } // Stage is a single step of a Pipeline. @@ -94,6 +95,10 @@ type noopStreamPipeline struct { builder *LabelsBuilder } +func (n noopStreamPipeline) ReferencedStructuredMetadata() bool { + return false +} + func (n noopStreamPipeline) Process(_ int64, line []byte, structuredMetadata ...labels.Label) ([]byte, LabelsResult, bool) { n.builder.Reset() n.builder.Add(StructuredMetadataLabel, structuredMetadata...) @@ -208,6 +213,10 @@ func (p *pipeline) Reset() { } } +func (p *streamPipeline) ReferencedStructuredMetadata() bool { + return p.builder.referencedStructuredMetadata +} + func (p *streamPipeline) Process(ts int64, line []byte, structuredMetadata ...labels.Label) ([]byte, LabelsResult, bool) { var ok bool p.builder.Reset() @@ -299,6 +308,10 @@ type filteringStreamPipeline struct { pipeline StreamPipeline } +func (sp *filteringStreamPipeline) ReferencedStructuredMetadata() bool { + return false +} + func (sp *filteringStreamPipeline) BaseLabels() LabelsResult { return sp.pipeline.BaseLabels() } diff --git a/pkg/logql/log/pipeline_test.go b/pkg/logql/log/pipeline_test.go index 5a6f40fa76..12a1a61fcc 100644 --- a/pkg/logql/log/pipeline_test.go +++ b/pkg/logql/log/pipeline_test.go @@ -280,6 +280,10 @@ func (p *stubStreamPipeline) ProcessString(_ int64, _ string, _ ...labels.Label) return "", nil, true } +func (p *stubStreamPipeline) ReferencedStructuredMetadata() bool { + return false +} + var ( resMatches bool resLine []byte diff --git a/pkg/logql/metrics.go b/pkg/logql/metrics.go index 9db8ee96e4..e86a85ba7b 100644 --- a/pkg/logql/metrics.go +++ b/pkg/logql/metrics.go @@ -142,6 +142,7 @@ func RecordRangeAndInstantQueryMetrics( "queue_time", logql_stats.ConvertSecondsToNanoseconds(stats.Summary.QueueTime), "splits", stats.Summary.Splits, "shards", stats.Summary.Shards, + "query_referenced_structured_metadata", stats.QueryReferencedStructuredMetadata(), "chunk_refs_fetch_time", stats.ChunkRefsFetchTime(), "cache_chunk_req", stats.Caches.Chunk.EntriesRequested, "cache_chunk_hit", stats.Caches.Chunk.EntriesFound, diff --git a/pkg/logqlmodel/stats/context.go b/pkg/logqlmodel/stats/context.go index 33517d6cce..187bd88763 100644 --- a/pkg/logqlmodel/stats/context.go +++ b/pkg/logqlmodel/stats/context.go @@ -187,6 +187,9 @@ func (s *Store) Merge(m Store) { s.Chunk.CompressedBytes += m.Chunk.CompressedBytes s.Chunk.TotalDuplicates += m.Chunk.TotalDuplicates s.Chunk.PostFilterLines += m.Chunk.PostFilterLines + if m.QueryReferencedStructured { + s.QueryReferencedStructured = true + } } func (s *Summary) Merge(m Summary) { @@ -277,6 +280,10 @@ func (r Result) TotalDecompressedLines() int64 { return r.Querier.Store.Chunk.DecompressedLines + r.Ingester.Store.Chunk.DecompressedLines } +func (r Result) QueryReferencedStructuredMetadata() bool { + return r.Querier.Store.QueryReferencedStructured || r.Ingester.Store.QueryReferencedStructured +} + func (c *Context) AddIngesterBatch(size int64) { atomic.AddInt64(&c.ingester.TotalBatches, 1) atomic.AddInt64(&c.ingester.TotalLinesSent, size) @@ -420,6 +427,10 @@ func (c *Context) AddSplitQueries(num int64) { atomic.AddInt64(&c.result.Summary.Splits, num) } +func (c *Context) SetQueryReferencedStructuredMetadata() { + c.store.QueryReferencedStructured = true +} + func (c *Context) getCacheStatsByType(t CacheType) *Cache { var stats *Cache switch t { @@ -469,6 +480,7 @@ func (r Result) Log(log log.Logger) { "Querier.PostFilterLInes", r.Querier.Store.Chunk.PostFilterLines, "Querier.CompressedBytes", humanize.Bytes(uint64(r.Querier.Store.Chunk.CompressedBytes)), "Querier.TotalDuplicates", r.Querier.Store.Chunk.TotalDuplicates, + "Querier.QueryReferencedStructuredMetadata", r.Querier.Store.QueryReferencedStructured, ) r.Caches.Log(log) r.Summary.Log(log) diff --git a/pkg/logqlmodel/stats/context_test.go b/pkg/logqlmodel/stats/context_test.go index d86db8de54..438cd17b12 100644 --- a/pkg/logqlmodel/stats/context_test.go +++ b/pkg/logqlmodel/stats/context_test.go @@ -25,6 +25,7 @@ func TestResult(t *testing.T) { stats.AddCacheRequest(ChunkCache, 3) stats.AddCacheRequest(IndexCache, 4) stats.AddCacheRequest(ResultCache, 1) + stats.SetQueryReferencedStructuredMetadata() fakeIngesterQuery(ctx) fakeIngesterQuery(ctx) @@ -50,9 +51,10 @@ func TestResult(t *testing.T) { }, Querier: Querier{ Store: Store{ - TotalChunksRef: 50, - TotalChunksDownloaded: 60, - ChunksDownloadTime: time.Second.Nanoseconds(), + TotalChunksRef: 50, + TotalChunksDownloaded: 60, + ChunksDownloadTime: time.Second.Nanoseconds(), + QueryReferencedStructured: true, Chunk: Chunk{ HeadChunkBytes: 10, HeadChunkLines: 20, @@ -96,6 +98,7 @@ func TestSnapshot_JoinResults(t *testing.T) { TotalLinesSent: 60, TotalReached: 2, Store: Store{ + QueryReferencedStructured: true, Chunk: Chunk{ HeadChunkBytes: 10, HeadChunkLines: 20, @@ -108,9 +111,10 @@ func TestSnapshot_JoinResults(t *testing.T) { }, Querier: Querier{ Store: Store{ - TotalChunksRef: 50, - TotalChunksDownloaded: 60, - ChunksDownloadTime: time.Second.Nanoseconds(), + TotalChunksRef: 50, + TotalChunksDownloaded: 60, + ChunksDownloadTime: time.Second.Nanoseconds(), + QueryReferencedStructured: true, Chunk: Chunk{ HeadChunkBytes: 10, HeadChunkLines: 20, @@ -181,9 +185,10 @@ func TestResult_Merge(t *testing.T) { }, Querier: Querier{ Store: Store{ - TotalChunksRef: 50, - TotalChunksDownloaded: 60, - ChunksDownloadTime: time.Second.Nanoseconds(), + TotalChunksRef: 50, + TotalChunksDownloaded: 60, + ChunksDownloadTime: time.Second.Nanoseconds(), + QueryReferencedStructured: true, Chunk: Chunk{ HeadChunkBytes: 10, HeadChunkLines: 20, @@ -242,9 +247,10 @@ func TestResult_Merge(t *testing.T) { }, Querier: Querier{ Store: Store{ - TotalChunksRef: 2 * 50, - TotalChunksDownloaded: 2 * 60, - ChunksDownloadTime: 2 * time.Second.Nanoseconds(), + TotalChunksRef: 2 * 50, + TotalChunksDownloaded: 2 * 60, + ChunksDownloadTime: 2 * time.Second.Nanoseconds(), + QueryReferencedStructured: true, Chunk: Chunk{ HeadChunkBytes: 2 * 10, HeadChunkLines: 2 * 20, @@ -297,12 +303,14 @@ func TestIngester(t *testing.T) { statsCtx.AddCompressedBytes(100) statsCtx.AddDuplicates(10) statsCtx.AddHeadChunkBytes(200) + statsCtx.SetQueryReferencedStructuredMetadata() require.Equal(t, Ingester{ TotalReached: 1, TotalChunksMatched: 100, TotalBatches: 25, TotalLinesSent: 30, Store: Store{ + QueryReferencedStructured: true, Chunk: Chunk{ HeadChunkBytes: 200, CompressedBytes: 100, diff --git a/pkg/logqlmodel/stats/stats.pb.go b/pkg/logqlmodel/stats/stats.pb.go index 91cbb1e31e..af008968eb 100644 --- a/pkg/logqlmodel/stats/stats.pb.go +++ b/pkg/logqlmodel/stats/stats.pb.go @@ -447,7 +447,9 @@ type Store struct { TotalChunksDownloaded int64 `protobuf:"varint,2,opt,name=totalChunksDownloaded,proto3" json:"totalChunksDownloaded"` // Time spent fetching chunks in nanoseconds. ChunksDownloadTime int64 `protobuf:"varint,3,opt,name=chunksDownloadTime,proto3" json:"chunksDownloadTime"` - Chunk Chunk `protobuf:"bytes,4,opt,name=chunk,proto3" json:"chunk"` + // Whether the query referenced structured metadata + QueryReferencedStructured bool `protobuf:"varint,13,opt,name=queryReferencedStructured,proto3" json:"queryReferencedStructuredMetadata"` + Chunk Chunk `protobuf:"bytes,4,opt,name=chunk,proto3" json:"chunk"` // Time spent fetching chunk refs from index. ChunkRefsFetchTime int64 `protobuf:"varint,5,opt,name=chunkRefsFetchTime,proto3" json:"chunkRefsFetchTime"` } @@ -505,6 +507,13 @@ func (m *Store) GetChunksDownloadTime() int64 { return 0 } +func (m *Store) GetQueryReferencedStructured() bool { + if m != nil { + return m.QueryReferencedStructured + } + return false +} + func (m *Store) GetChunk() Chunk { if m != nil { return m.Chunk @@ -740,78 +749,80 @@ func init() { func init() { proto.RegisterFile("pkg/logqlmodel/stats/stats.proto", fileDescriptor_6cdfe5d2aea33ebb) } var fileDescriptor_6cdfe5d2aea33ebb = []byte{ - // 1127 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4f, 0x6f, 0xe3, 0x44, - 0x14, 0x4f, 0x52, 0x9c, 0x74, 0xa7, 0x7f, 0x77, 0xda, 0x65, 0x03, 0x48, 0x76, 0x15, 0x40, 0x2c, - 0x02, 0x35, 0xe2, 0x8f, 0x84, 0x40, 0xac, 0x84, 0xdc, 0xa5, 0x52, 0xa5, 0x5d, 0x51, 0x5e, 0xe1, - 0xc2, 0xcd, 0xb5, 0xa7, 0x89, 0x55, 0xc7, 0x4e, 0xed, 0xf1, 0xb2, 0x3d, 0xc1, 0x47, 0xe0, 0x63, - 0x70, 0xe1, 0x80, 0x38, 0xf1, 0x0d, 0xf6, 0xd8, 0x1b, 0x7b, 0xb2, 0x68, 0x7a, 0x41, 0x3e, 0xad, - 0xc4, 0x1d, 0xa1, 0x79, 0x33, 0xb1, 0x3d, 0x8e, 0xa3, 0xed, 0x25, 0x9e, 0xf7, 0x7b, 0xbf, 0xdf, - 0x7b, 0xe3, 0x99, 0x79, 0xcf, 0x13, 0xb2, 0x37, 0x3d, 0x1f, 0x0d, 0x83, 0x68, 0x74, 0x11, 0x4c, - 0x22, 0x8f, 0x05, 0xc3, 0x84, 0x3b, 0x3c, 0x91, 0xbf, 0xfb, 0xd3, 0x38, 0xe2, 0x11, 0x35, 0xd0, - 0x78, 0x73, 0x77, 0x14, 0x8d, 0x22, 0x44, 0x86, 0x62, 0x24, 0x9d, 0x83, 0x7f, 0xdb, 0xa4, 0x0b, - 0x2c, 0x49, 0x03, 0x4e, 0x3f, 0x27, 0xbd, 0x24, 0x9d, 0x4c, 0x9c, 0xf8, 0xb2, 0xdf, 0xde, 0x6b, - 0x3f, 0x58, 0xfb, 0x78, 0x73, 0x5f, 0x86, 0x39, 0x91, 0xa8, 0xbd, 0xf5, 0x3c, 0xb3, 0x5a, 0x79, - 0x66, 0xcd, 0x69, 0x30, 0x1f, 0x08, 0xe9, 0x45, 0xca, 0x62, 0x9f, 0xc5, 0xfd, 0x8e, 0x26, 0xfd, - 0x56, 0xa2, 0xa5, 0x54, 0xd1, 0x60, 0x3e, 0xa0, 0x0f, 0xc9, 0xaa, 0x1f, 0x8e, 0x58, 0xc2, 0x59, - 0xdc, 0x5f, 0x41, 0xed, 0x96, 0xd2, 0x1e, 0x29, 0xd8, 0xde, 0x56, 0xe2, 0x82, 0x08, 0xc5, 0x88, - 0x7e, 0x4a, 0xba, 0xae, 0xe3, 0x8e, 0x59, 0xd2, 0x7f, 0x0d, 0xc5, 0x1b, 0x4a, 0x7c, 0x80, 0xa0, - 0xbd, 0xa1, 0xa4, 0x06, 0x92, 0x40, 0x71, 0x07, 0xbf, 0x77, 0x48, 0x57, 0x32, 0xe8, 0x47, 0xc4, - 0x70, 0xc7, 0x69, 0x78, 0xae, 0xde, 0x79, 0xbd, 0xaa, 0xaf, 0xc8, 0x05, 0x05, 0xe4, 0x43, 0x48, - 0xfc, 0xd0, 0x63, 0xcf, 0xd4, 0xbb, 0x2e, 0x91, 0x20, 0x05, 0xe4, 0x43, 0x4c, 0x33, 0xc6, 0x55, - 0x56, 0xef, 0xa8, 0x6b, 0x36, 0x95, 0x46, 0x71, 0x40, 0x3d, 0xe9, 0x01, 0x59, 0x43, 0x9a, 0xdc, - 0x20, 0xf5, 0x86, 0xba, 0x74, 0x47, 0x49, 0xab, 0x44, 0xa8, 0x1a, 0xf4, 0x90, 0xac, 0x3f, 0x8d, - 0x82, 0x74, 0xc2, 0x54, 0x14, 0xa3, 0x21, 0xca, 0xae, 0x8a, 0xa2, 0x31, 0x41, 0xb3, 0x06, 0x7f, - 0x76, 0x49, 0x4f, 0x9d, 0x04, 0xfa, 0x3d, 0xb9, 0x7f, 0x7a, 0xc9, 0x59, 0x72, 0x1c, 0x47, 0x2e, - 0x4b, 0x12, 0xe6, 0x1d, 0xb3, 0xf8, 0x84, 0xb9, 0x51, 0xe8, 0xe1, 0x32, 0xae, 0xd8, 0x6f, 0xe5, - 0x99, 0xb5, 0x8c, 0x02, 0xcb, 0x1c, 0x22, 0x6c, 0xe0, 0x87, 0x8d, 0x61, 0x3b, 0x65, 0xd8, 0x25, - 0x14, 0x58, 0xe6, 0xa0, 0x47, 0x64, 0x87, 0x47, 0xdc, 0x09, 0x6c, 0x2d, 0x2d, 0xee, 0xc4, 0x8a, - 0x7d, 0x3f, 0xcf, 0xac, 0x26, 0x37, 0x34, 0x81, 0x45, 0xa8, 0xc7, 0x5a, 0x2a, 0xdc, 0x99, 0x6a, - 0x28, 0xdd, 0x0d, 0x4d, 0x20, 0x7d, 0x40, 0x56, 0xd9, 0x33, 0xe6, 0x7e, 0xe7, 0x4f, 0x18, 0xee, - 0x49, 0xdb, 0x5e, 0x17, 0x67, 0x7c, 0x8e, 0x41, 0x31, 0xa2, 0x1f, 0x90, 0x3b, 0x17, 0x29, 0x4b, - 0x19, 0x52, 0xbb, 0x48, 0xdd, 0xc8, 0x33, 0xab, 0x04, 0xa1, 0x1c, 0xd2, 0x7d, 0x42, 0x92, 0xf4, - 0x54, 0x56, 0x57, 0xd2, 0xef, 0xe1, 0xc4, 0x36, 0xf3, 0xcc, 0xaa, 0xa0, 0x50, 0x19, 0xd3, 0xc7, - 0x64, 0x17, 0x67, 0xf7, 0x75, 0xc8, 0xd1, 0xc7, 0x78, 0x1a, 0x87, 0xcc, 0xeb, 0xaf, 0xa2, 0xb2, - 0x9f, 0x67, 0x56, 0xa3, 0x1f, 0x1a, 0x51, 0x3a, 0x20, 0xdd, 0x64, 0x1a, 0xf8, 0x3c, 0xe9, 0xdf, - 0x41, 0x3d, 0x11, 0xa7, 0x5a, 0x22, 0xa0, 0x9e, 0xc8, 0x19, 0x3b, 0xb1, 0x97, 0xf4, 0x49, 0x85, - 0x83, 0x08, 0xa8, 0x67, 0x31, 0xab, 0xe3, 0x28, 0xe1, 0x87, 0x7e, 0xc0, 0x59, 0x8c, 0xab, 0xd7, - 0x5f, 0xab, 0xcd, 0xaa, 0xe6, 0x87, 0x46, 0x94, 0xfe, 0x44, 0xde, 0x45, 0xfc, 0x84, 0xc7, 0xa9, - 0xcb, 0xd3, 0x98, 0x79, 0x4f, 0x18, 0x77, 0x3c, 0x87, 0x3b, 0xb5, 0x23, 0xb1, 0x8e, 0xe1, 0xdf, - 0xcf, 0x33, 0xeb, 0x76, 0x02, 0xb8, 0x1d, 0x6d, 0xf0, 0x25, 0xe9, 0xa9, 0x4e, 0x28, 0x9a, 0x47, - 0xc2, 0xa3, 0x98, 0xd5, 0xfa, 0xcd, 0x89, 0xc0, 0xca, 0xe6, 0x81, 0x14, 0x90, 0x8f, 0xc1, 0x6f, - 0x1d, 0xb2, 0x7a, 0x54, 0x36, 0xbc, 0x75, 0xcc, 0x09, 0x4c, 0x54, 0xae, 0xac, 0x37, 0xc3, 0xde, - 0x16, 0xc5, 0x5b, 0xc5, 0x41, 0xb3, 0xe8, 0x21, 0xa1, 0x68, 0x1f, 0x88, 0x06, 0x96, 0x3c, 0x71, - 0x38, 0x6a, 0x65, 0x51, 0xbd, 0x9e, 0x67, 0x56, 0x83, 0x17, 0x1a, 0xb0, 0x22, 0xbb, 0x8d, 0x76, - 0xa2, 0x6a, 0xa8, 0xcc, 0xae, 0x70, 0xd0, 0x2c, 0xfa, 0x05, 0xd9, 0x2c, 0x2b, 0xe0, 0x84, 0x85, - 0x5c, 0x15, 0x0c, 0xcd, 0x33, 0xab, 0xe6, 0x81, 0x9a, 0x5d, 0xae, 0x97, 0x71, 0xeb, 0xf5, 0xfa, - 0xab, 0x43, 0x0c, 0xf4, 0x17, 0x89, 0xe5, 0x4b, 0x00, 0x3b, 0x53, 0xed, 0xa9, 0x4c, 0x5c, 0x78, - 0xa0, 0x66, 0xd3, 0x6f, 0xc8, 0xbd, 0x0a, 0xf2, 0x28, 0xfa, 0x31, 0x0c, 0x22, 0xc7, 0x2b, 0x56, - 0xed, 0x8d, 0x3c, 0xb3, 0x9a, 0x09, 0xd0, 0x0c, 0x8b, 0x3d, 0x70, 0x35, 0x0c, 0xeb, 0x79, 0xa5, - 0xdc, 0x83, 0x45, 0x2f, 0x34, 0x60, 0xe5, 0x17, 0xab, 0xf6, 0x3d, 0x10, 0xd8, 0x92, 0x2f, 0xd6, - 0x3c, 0x35, 0xb0, 0xb3, 0xe4, 0x90, 0x71, 0x77, 0x5c, 0x74, 0x9d, 0x6a, 0x6a, 0xcd, 0x0b, 0x0d, - 0xd8, 0xe0, 0x0f, 0x83, 0x18, 0x98, 0x47, 0xac, 0xec, 0x98, 0x39, 0x9e, 0x4c, 0x2a, 0x0e, 0x7b, - 0x75, 0x4b, 0x75, 0x0f, 0xd4, 0x6c, 0x4d, 0x2b, 0xcb, 0xda, 0x68, 0xd0, 0xca, 0x82, 0xae, 0xd9, - 0xf4, 0x80, 0xdc, 0xf5, 0x98, 0x1b, 0x4d, 0xa6, 0x31, 0x56, 0x96, 0x4c, 0xdd, 0x45, 0xf9, 0xbd, - 0x3c, 0xb3, 0x16, 0x9d, 0xb0, 0x08, 0xd5, 0x83, 0xc8, 0x39, 0xf4, 0x9a, 0x83, 0xc8, 0x69, 0x2c, - 0x42, 0xf4, 0x21, 0xd9, 0xaa, 0xcf, 0x43, 0xf6, 0xcc, 0x9d, 0x3c, 0xb3, 0xea, 0x2e, 0xa8, 0x03, - 0x42, 0x8e, 0xc7, 0xe4, 0x51, 0x3a, 0x0d, 0x7c, 0xd7, 0x11, 0xf2, 0x3b, 0xa5, 0xbc, 0xe6, 0x82, - 0x3a, 0x20, 0xe4, 0xd3, 0x5a, 0x6f, 0x24, 0xa5, 0xbc, 0xe6, 0x82, 0x3a, 0x40, 0xa7, 0x64, 0xaf, - 0x58, 0xd8, 0x25, 0xdd, 0x4b, 0xf5, 0xda, 0x77, 0xf2, 0xcc, 0x7a, 0x25, 0x17, 0x5e, 0xc9, 0xa0, - 0x97, 0xe4, 0xed, 0xea, 0x1a, 0x2e, 0x4b, 0x2a, 0x3b, 0xf0, 0x7b, 0x79, 0x66, 0xdd, 0x86, 0x0e, - 0xb7, 0x21, 0x0d, 0xfe, 0xeb, 0x10, 0x03, 0xef, 0x39, 0xa2, 0x7d, 0x31, 0xf9, 0xc5, 0x3a, 0x8c, - 0xd2, 0x50, 0x6b, 0x9e, 0x55, 0x1c, 0x34, 0x8b, 0x7e, 0x45, 0xb6, 0xd9, 0xfc, 0x3b, 0x77, 0x91, - 0x8a, 0x36, 0x2c, 0x9b, 0x80, 0x61, 0xef, 0xe6, 0x99, 0xb5, 0xe0, 0x83, 0x05, 0x84, 0x7e, 0x46, - 0x36, 0x14, 0x86, 0x7d, 0x49, 0xde, 0x3d, 0x0c, 0xfb, 0x6e, 0x9e, 0x59, 0xba, 0x03, 0x74, 0x53, - 0x08, 0xf1, 0xb2, 0x04, 0xcc, 0x65, 0xfe, 0xd3, 0xe2, 0xa6, 0x81, 0x42, 0xcd, 0x01, 0xba, 0x29, - 0xee, 0x0c, 0x08, 0x60, 0xb7, 0x95, 0xe5, 0x85, 0x77, 0x86, 0x02, 0x84, 0x72, 0x28, 0xae, 0x22, - 0xb1, 0x9c, 0xab, 0xac, 0x25, 0x43, 0x5e, 0x45, 0xe6, 0x18, 0x14, 0x23, 0xb1, 0x80, 0x5e, 0xb5, - 0x7b, 0xf5, 0xca, 0xfe, 0x5f, 0xc5, 0x41, 0xb3, 0xec, 0xd3, 0xab, 0x6b, 0xb3, 0xf5, 0xe2, 0xda, - 0x6c, 0xbd, 0xbc, 0x36, 0xdb, 0x3f, 0xcf, 0xcc, 0xf6, 0xaf, 0x33, 0xb3, 0xfd, 0x7c, 0x66, 0xb6, - 0xaf, 0x66, 0x66, 0xfb, 0xef, 0x99, 0xd9, 0xfe, 0x67, 0x66, 0xb6, 0x5e, 0xce, 0xcc, 0xf6, 0x2f, - 0x37, 0x66, 0xeb, 0xea, 0xc6, 0x6c, 0xbd, 0xb8, 0x31, 0x5b, 0x3f, 0x7c, 0x38, 0xf2, 0xf9, 0x38, - 0x3d, 0xdd, 0x77, 0xa3, 0xc9, 0x70, 0x14, 0x3b, 0x67, 0x4e, 0xe8, 0x0c, 0x83, 0xe8, 0xdc, 0x1f, - 0x36, 0xfd, 0xe3, 0x39, 0xed, 0xe2, 0xff, 0x99, 0x4f, 0xfe, 0x0f, 0x00, 0x00, 0xff, 0xff, 0x14, - 0xee, 0x83, 0x9b, 0x10, 0x0d, 0x00, 0x00, + // 1163 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x57, 0x4d, 0x6f, 0xe4, 0x44, + 0x13, 0x1e, 0x27, 0xaf, 0x67, 0xb2, 0x9d, 0xcf, 0xed, 0x64, 0xdf, 0xf5, 0x82, 0x64, 0x87, 0x81, + 0x15, 0x41, 0xa0, 0x8c, 0xf8, 0x90, 0x10, 0x88, 0x95, 0x90, 0xb3, 0x44, 0x8a, 0xb4, 0x2b, 0x42, + 0x05, 0x2e, 0xdc, 0x1c, 0xbb, 0x33, 0x63, 0xc5, 0x63, 0x4f, 0xec, 0xf6, 0xb2, 0x39, 0xc1, 0x4f, + 0xe0, 0x67, 0x70, 0xe1, 0x80, 0x38, 0x21, 0xf1, 0x03, 0xf6, 0x98, 0xe3, 0x9e, 0x2c, 0x32, 0xb9, + 0x20, 0x9f, 0x56, 0xe2, 0x8e, 0x50, 0x57, 0xf7, 0xf8, 0x6b, 0x3c, 0xda, 0x5c, 0xc6, 0x5d, 0x4f, + 0x3d, 0x4f, 0xf5, 0x67, 0x55, 0xf7, 0x90, 0xdd, 0xc9, 0xf9, 0x70, 0x10, 0x44, 0xc3, 0x8b, 0x60, + 0x1c, 0x79, 0x2c, 0x18, 0x24, 0xdc, 0xe1, 0x89, 0xfc, 0xdd, 0x9f, 0xc4, 0x11, 0x8f, 0xa8, 0x8e, + 0xc6, 0x1b, 0x3b, 0xc3, 0x68, 0x18, 0x21, 0x32, 0x10, 0x2d, 0xe9, 0xec, 0xff, 0xa3, 0x91, 0x2e, + 0xb0, 0x24, 0x0d, 0x38, 0xfd, 0x8c, 0xf4, 0x92, 0x74, 0x3c, 0x76, 0xe2, 0x4b, 0x43, 0xdb, 0xd5, + 0xf6, 0x56, 0x3f, 0xda, 0xd8, 0x97, 0x61, 0x4e, 0x24, 0x6a, 0x6f, 0xbe, 0xc8, 0xac, 0x4e, 0x9e, + 0x59, 0x33, 0x1a, 0xcc, 0x1a, 0x42, 0x7a, 0x91, 0xb2, 0xd8, 0x67, 0xb1, 0xb1, 0x54, 0x93, 0x7e, + 0x23, 0xd1, 0x52, 0xaa, 0x68, 0x30, 0x6b, 0xd0, 0x47, 0x64, 0xc5, 0x0f, 0x87, 0x2c, 0xe1, 0x2c, + 0x36, 0x96, 0x51, 0xbb, 0xa9, 0xb4, 0x47, 0x0a, 0xb6, 0xb7, 0x94, 0xb8, 0x20, 0x42, 0xd1, 0xa2, + 0x9f, 0x90, 0xae, 0xeb, 0xb8, 0x23, 0x96, 0x18, 0xff, 0x43, 0xf1, 0xba, 0x12, 0x1f, 0x20, 0x68, + 0xaf, 0x2b, 0xa9, 0x8e, 0x24, 0x50, 0xdc, 0xfe, 0x6f, 0x4b, 0xa4, 0x2b, 0x19, 0xf4, 0x43, 0xa2, + 0xbb, 0xa3, 0x34, 0x3c, 0x57, 0x73, 0x5e, 0xab, 0xea, 0x2b, 0x72, 0x41, 0x01, 0xf9, 0x11, 0x12, + 0x3f, 0xf4, 0xd8, 0x73, 0x35, 0xd7, 0x05, 0x12, 0xa4, 0x80, 0xfc, 0x88, 0x61, 0xc6, 0xb8, 0xca, + 0x6a, 0x8e, 0x75, 0xcd, 0x86, 0xd2, 0x28, 0x0e, 0xa8, 0x2f, 0x3d, 0x20, 0xab, 0x48, 0x93, 0x1b, + 0xa4, 0x66, 0x58, 0x97, 0x6e, 0x2b, 0x69, 0x95, 0x08, 0x55, 0x83, 0x1e, 0x92, 0xb5, 0x67, 0x51, + 0x90, 0x8e, 0x99, 0x8a, 0xa2, 0xb7, 0x44, 0xd9, 0x51, 0x51, 0x6a, 0x4c, 0xa8, 0x59, 0xfd, 0x3f, + 0xba, 0xa4, 0xa7, 0x4e, 0x02, 0xfd, 0x8e, 0xdc, 0x3f, 0xbd, 0xe4, 0x2c, 0x39, 0x8e, 0x23, 0x97, + 0x25, 0x09, 0xf3, 0x8e, 0x59, 0x7c, 0xc2, 0xdc, 0x28, 0xf4, 0x70, 0x19, 0x97, 0xed, 0x37, 0xf3, + 0xcc, 0x5a, 0x44, 0x81, 0x45, 0x0e, 0x11, 0x36, 0xf0, 0xc3, 0xd6, 0xb0, 0x4b, 0x65, 0xd8, 0x05, + 0x14, 0x58, 0xe4, 0xa0, 0x47, 0x64, 0x9b, 0x47, 0xdc, 0x09, 0xec, 0x5a, 0xb7, 0xb8, 0x13, 0xcb, + 0xf6, 0xfd, 0x3c, 0xb3, 0xda, 0xdc, 0xd0, 0x06, 0x16, 0xa1, 0x9e, 0xd4, 0xba, 0xc2, 0x9d, 0xa9, + 0x86, 0xaa, 0xbb, 0xa1, 0x0d, 0xa4, 0x7b, 0x64, 0x85, 0x3d, 0x67, 0xee, 0xb7, 0xfe, 0x98, 0xe1, + 0x9e, 0x68, 0xf6, 0x9a, 0x38, 0xe3, 0x33, 0x0c, 0x8a, 0x16, 0x7d, 0x9f, 0xdc, 0xb9, 0x48, 0x59, + 0xca, 0x90, 0xda, 0x45, 0xea, 0x7a, 0x9e, 0x59, 0x25, 0x08, 0x65, 0x93, 0xee, 0x13, 0x92, 0xa4, + 0xa7, 0x32, 0xbb, 0x12, 0xa3, 0x87, 0x03, 0xdb, 0xc8, 0x33, 0xab, 0x82, 0x42, 0xa5, 0x4d, 0x9f, + 0x90, 0x1d, 0x1c, 0xdd, 0x57, 0x21, 0x47, 0x1f, 0xe3, 0x69, 0x1c, 0x32, 0xcf, 0x58, 0x41, 0xa5, + 0x91, 0x67, 0x56, 0xab, 0x1f, 0x5a, 0x51, 0xda, 0x27, 0xdd, 0x64, 0x12, 0xf8, 0x3c, 0x31, 0xee, + 0xa0, 0x9e, 0x88, 0x53, 0x2d, 0x11, 0x50, 0x5f, 0xe4, 0x8c, 0x9c, 0xd8, 0x4b, 0x0c, 0x52, 0xe1, + 0x20, 0x02, 0xea, 0x5b, 0x8c, 0xea, 0x38, 0x4a, 0xf8, 0xa1, 0x1f, 0x70, 0x16, 0xe3, 0xea, 0x19, + 0xab, 0x8d, 0x51, 0x35, 0xfc, 0xd0, 0x8a, 0xd2, 0x1f, 0xc9, 0x43, 0xc4, 0x4f, 0x78, 0x9c, 0xba, + 0x3c, 0x8d, 0x99, 0xf7, 0x94, 0x71, 0xc7, 0x73, 0xb8, 0xd3, 0x38, 0x12, 0x6b, 0x18, 0xfe, 0xbd, + 0x3c, 0xb3, 0x6e, 0x27, 0x80, 0xdb, 0xd1, 0xfa, 0x5f, 0x90, 0x9e, 0xaa, 0x84, 0xa2, 0x78, 0x24, + 0x3c, 0x8a, 0x59, 0xa3, 0xde, 0x9c, 0x08, 0xac, 0x2c, 0x1e, 0x48, 0x01, 0xf9, 0xe9, 0xff, 0xba, + 0x44, 0x56, 0x8e, 0xca, 0x82, 0xb7, 0x86, 0x7d, 0x02, 0x13, 0x99, 0x2b, 0xf3, 0x4d, 0xb7, 0xb7, + 0x44, 0xf2, 0x56, 0x71, 0xa8, 0x59, 0xf4, 0x90, 0x50, 0xb4, 0x0f, 0x44, 0x01, 0x4b, 0x9e, 0x3a, + 0x1c, 0xb5, 0x32, 0xa9, 0xfe, 0x9f, 0x67, 0x56, 0x8b, 0x17, 0x5a, 0xb0, 0xa2, 0x77, 0x1b, 0xed, + 0x44, 0xe5, 0x50, 0xd9, 0xbb, 0xc2, 0xa1, 0x66, 0xd1, 0xcf, 0xc9, 0x46, 0x99, 0x01, 0x27, 0x2c, + 0xe4, 0x2a, 0x61, 0x68, 0x9e, 0x59, 0x0d, 0x0f, 0x34, 0xec, 0x72, 0xbd, 0xf4, 0x5b, 0xaf, 0xd7, + 0x9f, 0xcb, 0x44, 0x47, 0x7f, 0xd1, 0xb1, 0x9c, 0x04, 0xb0, 0x33, 0x55, 0x9e, 0xca, 0x8e, 0x0b, + 0x0f, 0x34, 0x6c, 0xfa, 0x35, 0xb9, 0x57, 0x41, 0x1e, 0x47, 0x3f, 0x84, 0x41, 0xe4, 0x78, 0xc5, + 0xaa, 0x3d, 0xc8, 0x33, 0xab, 0x9d, 0x00, 0xed, 0xb0, 0xd8, 0x03, 0xb7, 0x86, 0x61, 0x3e, 0x2f, + 0x97, 0x7b, 0x30, 0xef, 0x85, 0x16, 0x8c, 0xba, 0xe4, 0x81, 0x48, 0xde, 0x4b, 0x60, 0x67, 0x2c, + 0x66, 0xa1, 0xcb, 0xbc, 0xf2, 0xfc, 0x19, 0xeb, 0xbb, 0xda, 0xde, 0x8a, 0xfd, 0x30, 0xcf, 0xac, + 0xb7, 0x16, 0x92, 0x66, 0x87, 0x14, 0x16, 0xc7, 0x29, 0xaf, 0xc5, 0xc6, 0xa5, 0x23, 0xb0, 0x05, + 0xd7, 0xe2, 0x6c, 0x7e, 0xc0, 0xce, 0x92, 0x43, 0xc6, 0xdd, 0x51, 0x51, 0xda, 0xaa, 0xf3, 0xab, + 0x79, 0xa1, 0x05, 0xeb, 0xff, 0xae, 0x13, 0x1d, 0xfb, 0x11, 0xdb, 0x37, 0x62, 0x8e, 0x27, 0x3b, + 0x15, 0x19, 0x55, 0x3d, 0x37, 0x75, 0x0f, 0x34, 0xec, 0x9a, 0x56, 0xd6, 0x0e, 0xbd, 0x45, 0x2b, + 0xab, 0x46, 0xc3, 0xa6, 0x07, 0xe4, 0xae, 0xc7, 0xdc, 0x68, 0x3c, 0x89, 0x31, 0x7d, 0x65, 0xd7, + 0x5d, 0x94, 0xdf, 0xcb, 0x33, 0x6b, 0xde, 0x09, 0xf3, 0x50, 0x33, 0x88, 0x1c, 0x43, 0xaf, 0x3d, + 0x88, 0x1c, 0xc6, 0x3c, 0x44, 0x1f, 0x91, 0xcd, 0xe6, 0x38, 0x64, 0x61, 0xde, 0xce, 0x33, 0xab, + 0xe9, 0x82, 0x26, 0x20, 0xe4, 0x78, 0x16, 0x1f, 0xa7, 0x93, 0xc0, 0x77, 0x1d, 0x21, 0xbf, 0x53, + 0xca, 0x1b, 0x2e, 0x68, 0x02, 0x42, 0x3e, 0x69, 0x14, 0x60, 0x52, 0xca, 0x1b, 0x2e, 0x68, 0x02, + 0x74, 0x42, 0x76, 0x8b, 0x85, 0x5d, 0x50, 0x22, 0x55, 0x41, 0x7f, 0x27, 0xcf, 0xac, 0xd7, 0x72, + 0xe1, 0xb5, 0x0c, 0x7a, 0x49, 0xde, 0xae, 0xae, 0xe1, 0xa2, 0x4e, 0x65, 0x99, 0x7f, 0x37, 0xcf, + 0xac, 0xdb, 0xd0, 0xe1, 0x36, 0xa4, 0xfe, 0xbf, 0x4b, 0x44, 0xc7, 0xc7, 0x94, 0xa8, 0x91, 0x4c, + 0x5e, 0x8b, 0x87, 0x51, 0x1a, 0xd6, 0x2a, 0x74, 0x15, 0x87, 0x9a, 0x45, 0xbf, 0x24, 0x5b, 0x6c, + 0x76, 0x99, 0x5e, 0xa4, 0xa2, 0xd6, 0xcb, 0x4a, 0xa3, 0xdb, 0x3b, 0x79, 0x66, 0xcd, 0xf9, 0x60, + 0x0e, 0xa1, 0x9f, 0x92, 0x75, 0x85, 0x61, 0xf1, 0x93, 0x0f, 0x1c, 0xdd, 0xbe, 0x9b, 0x67, 0x56, + 0xdd, 0x01, 0x75, 0x53, 0x08, 0xf1, 0x45, 0x06, 0xcc, 0x65, 0xfe, 0xb3, 0xe2, 0x39, 0x83, 0xc2, + 0x9a, 0x03, 0xea, 0xa6, 0x78, 0x98, 0x20, 0x80, 0x25, 0x5d, 0xa6, 0x17, 0x3e, 0x4c, 0x0a, 0x10, + 0xca, 0xa6, 0x78, 0xef, 0xc4, 0x72, 0xac, 0x32, 0x97, 0x74, 0xf9, 0xde, 0x99, 0x61, 0x50, 0xb4, + 0xc4, 0x02, 0x7a, 0xd5, 0x12, 0xd9, 0x2b, 0x2f, 0x99, 0x2a, 0x0e, 0x35, 0xcb, 0x3e, 0xbd, 0xba, + 0x36, 0x3b, 0x2f, 0xaf, 0xcd, 0xce, 0xab, 0x6b, 0x53, 0xfb, 0x69, 0x6a, 0x6a, 0xbf, 0x4c, 0x4d, + 0xed, 0xc5, 0xd4, 0xd4, 0xae, 0xa6, 0xa6, 0xf6, 0xd7, 0xd4, 0xd4, 0xfe, 0x9e, 0x9a, 0x9d, 0x57, + 0x53, 0x53, 0xfb, 0xf9, 0xc6, 0xec, 0x5c, 0xdd, 0x98, 0x9d, 0x97, 0x37, 0x66, 0xe7, 0xfb, 0x0f, + 0x86, 0x3e, 0x1f, 0xa5, 0xa7, 0xfb, 0x6e, 0x34, 0x1e, 0x0c, 0x63, 0xe7, 0xcc, 0x09, 0x9d, 0x41, + 0x10, 0x9d, 0xfb, 0x83, 0xb6, 0xbf, 0x55, 0xa7, 0x5d, 0xfc, 0xd3, 0xf4, 0xf1, 0x7f, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x02, 0xba, 0xb6, 0x0a, 0x75, 0x0d, 0x00, 0x00, } func (this *Result) Equal(that interface{}) bool { @@ -1028,6 +1039,9 @@ func (this *Store) Equal(that interface{}) bool { if this.ChunksDownloadTime != that1.ChunksDownloadTime { return false } + if this.QueryReferencedStructured != that1.QueryReferencedStructured { + return false + } if !this.Chunk.Equal(&that1.Chunk) { return false } @@ -1202,11 +1216,12 @@ func (this *Store) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 9) + s := make([]string, 0, 10) s = append(s, "&stats.Store{") s = append(s, "TotalChunksRef: "+fmt.Sprintf("%#v", this.TotalChunksRef)+",\n") s = append(s, "TotalChunksDownloaded: "+fmt.Sprintf("%#v", this.TotalChunksDownloaded)+",\n") s = append(s, "ChunksDownloadTime: "+fmt.Sprintf("%#v", this.ChunksDownloadTime)+",\n") + s = append(s, "QueryReferencedStructured: "+fmt.Sprintf("%#v", this.QueryReferencedStructured)+",\n") s = append(s, "Chunk: "+strings.Replace(this.Chunk.GoString(), `&`, ``, 1)+",\n") s = append(s, "ChunkRefsFetchTime: "+fmt.Sprintf("%#v", this.ChunkRefsFetchTime)+",\n") s = append(s, "}") @@ -1581,6 +1596,16 @@ func (m *Store) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.QueryReferencedStructured { + i-- + if m.QueryReferencedStructured { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x68 + } if m.ChunkRefsFetchTime != 0 { i = encodeVarintStats(dAtA, i, uint64(m.ChunkRefsFetchTime)) i-- @@ -1886,6 +1911,9 @@ func (m *Store) Size() (n int) { if m.ChunkRefsFetchTime != 0 { n += 1 + sovStats(uint64(m.ChunkRefsFetchTime)) } + if m.QueryReferencedStructured { + n += 2 + } return n } @@ -2043,6 +2071,7 @@ func (this *Store) String() string { `ChunksDownloadTime:` + fmt.Sprintf("%v", this.ChunksDownloadTime) + `,`, `Chunk:` + strings.Replace(strings.Replace(this.Chunk.String(), "Chunk", "Chunk", 1), `&`, ``, 1) + `,`, `ChunkRefsFetchTime:` + fmt.Sprintf("%v", this.ChunkRefsFetchTime) + `,`, + `QueryReferencedStructured:` + fmt.Sprintf("%v", this.QueryReferencedStructured) + `,`, `}`, }, "") return s @@ -3143,6 +3172,26 @@ func (m *Store) Unmarshal(dAtA []byte) error { break } } + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field QueryReferencedStructured", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStats + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.QueryReferencedStructured = bool(v != 0) default: iNdEx = preIndex skippy, err := skipStats(dAtA[iNdEx:]) diff --git a/pkg/logqlmodel/stats/stats.proto b/pkg/logqlmodel/stats/stats.proto index f9f440f691..3aeaab7d0c 100644 --- a/pkg/logqlmodel/stats/stats.proto +++ b/pkg/logqlmodel/stats/stats.proto @@ -114,6 +114,8 @@ message Store { int64 totalChunksDownloaded = 2 [(gogoproto.jsontag) = "totalChunksDownloaded"]; // Time spent fetching chunks in nanoseconds. int64 chunksDownloadTime = 3 [(gogoproto.jsontag) = "chunksDownloadTime"]; + // Whether the query referenced structured metadata + bool queryReferencedStructured = 13 [(gogoproto.jsontag) = "queryReferencedStructuredMetadata"]; Chunk chunk = 4 [ (gogoproto.nullable) = false, diff --git a/pkg/querier/queryrange/codec_test.go b/pkg/querier/queryrange/codec_test.go index 4f7a88edec..215590f267 100644 --- a/pkg/querier/queryrange/codec_test.go +++ b/pkg/querier/queryrange/codec_test.go @@ -1556,7 +1556,8 @@ var ( "chunksDownloadTime": 0, "totalChunksRef": 0, "totalChunksDownloaded": 0, - "chunkRefsFetchTime": 0 + "chunkRefsFetchTime": 0, + "queryReferencedStructuredMetadata": false }, "totalBatches": 6, "totalChunksMatched": 7, @@ -1579,7 +1580,8 @@ var ( "chunksDownloadTime": 16, "totalChunksRef": 17, "totalChunksDownloaded": 18, - "chunkRefsFetchTime": 19 + "chunkRefsFetchTime": 19, + "queryReferencedStructuredMetadata": true } }, "cache": { @@ -1973,10 +1975,11 @@ var ( PostFilterLines: 0, TotalDuplicates: 19, }, - ChunksDownloadTime: 16, - TotalChunksRef: 17, - TotalChunksDownloaded: 18, - ChunkRefsFetchTime: 19, + ChunksDownloadTime: 16, + TotalChunksRef: 17, + TotalChunksDownloaded: 18, + ChunkRefsFetchTime: 19, + QueryReferencedStructured: true, }, }, diff --git a/pkg/querier/queryrange/prometheus_test.go b/pkg/querier/queryrange/prometheus_test.go index de55c1e747..c052990624 100644 --- a/pkg/querier/queryrange/prometheus_test.go +++ b/pkg/querier/queryrange/prometheus_test.go @@ -19,6 +19,7 @@ var emptyStats = `"stats": { "totalChunksRef": 0, "totalChunksDownloaded": 0, "chunkRefsFetchTime": 0, + "queryReferencedStructuredMetadata": false, "chunk" :{ "compressedBytes": 0, "decompressedBytes": 0, @@ -42,6 +43,7 @@ var emptyStats = `"stats": { "totalChunksRef": 0, "totalChunksDownloaded": 0, "chunkRefsFetchTime": 0, + "queryReferencedStructuredMetadata": false, "chunk" :{ "compressedBytes": 0, "decompressedBytes": 0, diff --git a/pkg/storage/store_test.go b/pkg/storage/store_test.go index 2fc7f94b1a..59806f6a79 100644 --- a/pkg/storage/store_test.go +++ b/pkg/storage/store_test.go @@ -13,8 +13,6 @@ import ( "testing" "time" - lokilog "github.com/grafana/loki/pkg/logql/log" - "github.com/cespare/xxhash/v2" "github.com/go-kit/log" "github.com/grafana/dskit/flagext" @@ -23,10 +21,15 @@ import ( "github.com/prometheus/prometheus/model/labels" "github.com/stretchr/testify/require" + "github.com/grafana/loki/pkg/chunkenc" + "github.com/grafana/loki/pkg/ingester/client" "github.com/grafana/loki/pkg/iter" "github.com/grafana/loki/pkg/logproto" "github.com/grafana/loki/pkg/logql" + lokilog "github.com/grafana/loki/pkg/logql/log" "github.com/grafana/loki/pkg/logql/syntax" + "github.com/grafana/loki/pkg/logqlmodel/stats" + "github.com/grafana/loki/pkg/push" "github.com/grafana/loki/pkg/querier/astmapper" "github.com/grafana/loki/pkg/querier/plan" "github.com/grafana/loki/pkg/storage/chunk" @@ -44,7 +47,7 @@ var ( m runtime.MemStats ctx = user.InjectOrgID(context.Background(), "fake") cm = NewClientMetrics() - chunkStore = getLocalStore(cm) + chunkStore = getLocalStore("/tmp/benchmark/", cm) ) // go test -bench=. -benchmem -memprofile memprofile.out -cpuprofile profile.out @@ -187,7 +190,7 @@ func printHeap(b *testing.B, show bool) { } } -func getLocalStore(cm ClientMetrics) Store { +func getLocalStore(path string, cm ClientMetrics) Store { limits, err := validation.NewOverrides(validation.Limits{ MaxQueryLength: model.Duration(6000 * time.Hour), }, nil) @@ -196,8 +199,8 @@ func getLocalStore(cm ClientMetrics) Store { } storeConfig := Config{ - BoltDBConfig: local.BoltDBConfig{Directory: "/tmp/benchmark/index"}, - FSConfig: local.FSConfig{Directory: "/tmp/benchmark/chunks"}, + BoltDBConfig: local.BoltDBConfig{Directory: filepath.Join(path, "index")}, + FSConfig: local.FSConfig{Directory: filepath.Join(path, "chunks")}, MaxChunkBatchSize: 10, } @@ -207,12 +210,13 @@ func getLocalStore(cm ClientMetrics) Store { From: config.DayTime{Time: start}, IndexType: "boltdb", ObjectType: config.StorageTypeFileSystem, - Schema: "v9", + Schema: "v13", IndexTables: config.IndexPeriodicTableConfig{ PeriodicTableConfig: config.PeriodicTableConfig{ Prefix: "index_", Period: time.Hour * 168, }}, + RowShards: 16, }, }, } @@ -963,6 +967,10 @@ type mockStreamPipeline struct { called int } +func (p *mockStreamPipeline) ReferencedStructuredMetadata() bool { + return false +} + func (p *mockStreamPipeline) BaseLabels() lokilog.LabelsResult { return p.wrappedSP.BaseLabels() } @@ -1044,6 +1052,10 @@ type mockStreamExtractor struct { called int } +func (p *mockStreamExtractor) ReferencedStructuredMetadata() bool { + return false +} + func (p *mockStreamExtractor) BaseLabels() lokilog.LabelsResult { return p.wrappedSP.BaseLabels() } @@ -1851,3 +1863,224 @@ func TestStore_BoltdbTsdbSameIndexPrefix(t *testing.T) { } } } + +func TestQueryReferencingStructuredMetadata(t *testing.T) { + ctx := user.InjectOrgID(context.Background(), "fake") + tempDir := t.TempDir() + store := getLocalStore(tempDir, cm) + + schemaCfg := store.(*LokiStore).schemaCfg + periodcfg, err := schemaCfg.SchemaForTime(start) + require.NoError(t, err) + + chunkfmt, headfmt, err := periodcfg.ChunkFormat() + require.NoError(t, err) + + now := time.Now() + chkFrom := now.Add(-50 * time.Second) + chkThrough := now + + // add some streams with and without structured metadata + for _, withStructuredMetadata := range []bool{true, false} { + stream := fmt.Sprintf(`{sm="%v"}`, withStructuredMetadata) + lbs, err := syntax.ParseLabels(stream) + if err != nil { + panic(err) + } + labelsBuilder := labels.NewBuilder(lbs) + labelsBuilder.Set(labels.MetricName, "logs") + metric := labelsBuilder.Labels() + fp := client.Fingerprint(lbs) + + chunkEnc := chunkenc.NewMemChunk(chunkfmt, chunkenc.EncLZ4_4M, headfmt, 262144, 1572864) + for ts := chkFrom; !ts.After(chkThrough); ts = ts.Add(time.Second) { + entry := logproto.Entry{ + Timestamp: ts, + Line: fmt.Sprintf("ts=%d level=info", ts.Unix()), + } + + if withStructuredMetadata { + entry.StructuredMetadata = push.LabelsAdapter{ + { + Name: "fizz", + Value: "buzz", + }, + { + Name: "num", + Value: "1", + }, + } + } + require.NoError(t, chunkEnc.Append(&entry)) + } + + require.NoError(t, chunkEnc.Close()) + from, to := chunkEnc.Bounds() + c := chunk.NewChunk("fake", fp, metric, chunkenc.NewFacade(chunkEnc, 0, 0), model.TimeFromUnixNano(from.UnixNano()), model.TimeFromUnixNano(to.UnixNano())) + if err := c.Encode(); err != nil { + panic(err) + } + require.NoError(t, store.Put(ctx, []chunk.Chunk{c})) + + // verify the data by querying it + it, err := store.SelectLogs(ctx, logql.SelectLogParams{QueryRequest: &logproto.QueryRequest{ + Selector: stream, + Limit: 1000, + Direction: logproto.FORWARD, + Start: chkFrom, + End: chkThrough.Add(time.Minute), + Plan: &plan.QueryPlan{ + AST: syntax.MustParseExpr(stream), + }, + }}) + require.NoError(t, err) + + for ts := chkFrom; !ts.After(chkThrough); ts = ts.Add(time.Second) { + require.True(t, it.Next()) + expectedEntry := logproto.Entry{ + Timestamp: ts.Truncate(0), + Line: fmt.Sprintf("ts=%d level=info", ts.Unix()), + } + + if withStructuredMetadata { + expectedEntry.StructuredMetadata = push.LabelsAdapter{ + { + Name: "fizz", + Value: "buzz", + }, + { + Name: "num", + Value: "1", + }, + } + } + require.Equal(t, expectedEntry, it.Entry()) + } + + require.False(t, it.Next()) + require.NoError(t, it.Close()) + } + + // test cases for logs queries + t.Run("logs queries", func(t *testing.T) { + for _, tc := range []struct { + name string + query string + + expectedReferencedStructuredMetadata bool + }{ + { + name: "logs not having structured metadata", + query: `{sm="false"}`, + }, + { + name: "not referencing structured metadata in logs having structured metadata", + query: `{sm="true"}`, + }, + { + name: "referencing a parsed field", + query: `{sm="true"} | logfmt | level="info"`, + }, + { + name: "referencing structured metadata with label filter", + query: `{sm="true"} | fizz="buzz"`, + + expectedReferencedStructuredMetadata: true, + }, + { + name: "referencing structured metadata to drop it", + query: `{sm="true"} | drop fizz`, + + expectedReferencedStructuredMetadata: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + ctx := user.InjectOrgID(context.Background(), "fake") + _, ctx = stats.NewContext(ctx) + it, err := store.SelectLogs(ctx, logql.SelectLogParams{QueryRequest: &logproto.QueryRequest{ + Selector: tc.query, + Limit: 1000, + Direction: logproto.FORWARD, + Start: chkFrom, + End: chkThrough.Add(time.Minute), + Plan: &plan.QueryPlan{ + AST: syntax.MustParseExpr(tc.query), + }, + }}) + require.NoError(t, err) + numEntries := int64(0) + for it.Next() { + numEntries++ + } + require.NoError(t, it.Close()) + require.Equal(t, chkThrough.Unix()-chkFrom.Unix()+1, numEntries) + + statsCtx := stats.FromContext(ctx) + require.Equal(t, tc.expectedReferencedStructuredMetadata, statsCtx.Result(0, 0, 0).QueryReferencedStructuredMetadata()) + }) + } + }) + + // test cases for metric queries + t.Run("metric queries", func(t *testing.T) { + for _, tc := range []struct { + name string + query string + + expectedReferencedStructuredMetadata bool + }{ + { + name: "logs not having structured metadata", + query: `sum(count_over_time({sm="false"}[1m]))`, + }, + { + name: "not referencing structured metadata in logs having structured metadata", + query: `sum(count_over_time({sm="true"}[1m]))`, + }, + { + name: "referencing a parsed field", + query: `sum by (level) (count_over_time({sm="true"} | logfmt | level="info"[1m]))`, + }, + { + name: "referencing structured metadata with label filter", + query: `sum(count_over_time({sm="true"} | fizz="buzz"[1m]))`, + + expectedReferencedStructuredMetadata: true, + }, + { + name: "referencing structured metadata in by aggregation clause", + query: `sum by (fizz) (count_over_time({sm="true"}[1m]))`, + + expectedReferencedStructuredMetadata: true, + }, + { + name: "referencing structured metadata in without aggregation clause", + query: `sum without (fizz) (count_over_time({sm="true"}[1m]))`, + + expectedReferencedStructuredMetadata: true, + }, + { + name: "referencing structured metadata in unwrap", + query: `sum(sum_over_time({sm="true"} | unwrap num[1m]))`, + + expectedReferencedStructuredMetadata: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + ctx := user.InjectOrgID(context.Background(), "fake") + _, ctx = stats.NewContext(ctx) + it, err := store.SelectSamples(ctx, logql.SelectSampleParams{SampleQueryRequest: newSampleQuery(tc.query, chkFrom, chkThrough.Add(time.Minute), nil)}) + require.NoError(t, err) + numSamples := int64(0) + for it.Next() { + numSamples++ + } + require.NoError(t, it.Close()) + require.Equal(t, chkThrough.Unix()-chkFrom.Unix()+1, numSamples) + + statsCtx := stats.FromContext(ctx) + require.Equal(t, tc.expectedReferencedStructuredMetadata, statsCtx.Result(0, 0, 0).QueryReferencedStructuredMetadata()) + }) + } + }) +} diff --git a/pkg/util/marshal/legacy/marshal_test.go b/pkg/util/marshal/legacy/marshal_test.go index 83ed90ce35..79f40c8990 100644 --- a/pkg/util/marshal/legacy/marshal_test.go +++ b/pkg/util/marshal/legacy/marshal_test.go @@ -62,6 +62,7 @@ var queryTests = []struct { "totalChunksRef": 0, "totalChunksDownloaded": 0, "chunkRefsFetchTime": 0, + "queryReferencedStructuredMetadata": false, "chunk" :{ "compressedBytes": 0, "decompressedBytes": 0, @@ -85,6 +86,7 @@ var queryTests = []struct { "totalChunksRef": 0, "totalChunksDownloaded": 0, "chunkRefsFetchTime": 0, + "queryReferencedStructuredMetadata": false, "chunk" :{ "compressedBytes": 0, "decompressedBytes": 0, diff --git a/pkg/util/marshal/marshal_test.go b/pkg/util/marshal/marshal_test.go index b35b5e2b05..6d816841da 100644 --- a/pkg/util/marshal/marshal_test.go +++ b/pkg/util/marshal/marshal_test.go @@ -30,6 +30,7 @@ const emptyStats = `{ "totalChunksRef": 0, "totalChunksDownloaded": 0, "chunkRefsFetchTime": 0, + "queryReferencedStructuredMetadata": false, "chunk" :{ "compressedBytes": 0, "decompressedBytes": 0, @@ -53,6 +54,7 @@ const emptyStats = `{ "totalChunksRef": 0, "totalChunksDownloaded": 0, "chunkRefsFetchTime": 0, + "queryReferencedStructuredMetadata": false, "chunk" :{ "compressedBytes": 0, "decompressedBytes": 0,