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
pull/11542/head^2
Sandeep Sukhani 1 year ago committed by GitHub
parent 64392ba164
commit 0e5da205a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 28
      pkg/chunkenc/memchunk.go
  2. 3
      pkg/chunkenc/memchunk_test.go
  3. 7
      pkg/chunkenc/unordered.go
  4. 8
      pkg/ingester/instance_test.go
  5. 21
      pkg/logql/log/labels.go
  6. 29
      pkg/logql/log/labels_test.go
  7. 13
      pkg/logql/log/metrics_extraction.go
  8. 4
      pkg/logql/log/metrics_extraction_test.go
  9. 13
      pkg/logql/log/pipeline.go
  10. 4
      pkg/logql/log/pipeline_test.go
  11. 1
      pkg/logql/metrics.go
  12. 12
      pkg/logqlmodel/stats/context.go
  13. 8
      pkg/logqlmodel/stats/context_test.go
  14. 195
      pkg/logqlmodel/stats/stats.pb.go
  15. 2
      pkg/logqlmodel/stats/stats.proto
  16. 7
      pkg/querier/queryrange/codec_test.go
  17. 2
      pkg/querier/queryrange/prometheus_test.go
  18. 247
      pkg/storage/store_test.go
  19. 2
      pkg/util/marshal/legacy/marshal_test.go
  20. 2
      pkg/util/marshal/marshal_test.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() }

@ -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 {

@ -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
}

@ -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()
}

@ -137,6 +137,7 @@ type BaseLabelsBuilder struct {
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
}
}

@ -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")

@ -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()
}

@ -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
}

@ -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()
}

@ -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

@ -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,

@ -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)

@ -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)
@ -53,6 +54,7 @@ func TestResult(t *testing.T) {
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,
@ -111,6 +114,7 @@ func TestSnapshot_JoinResults(t *testing.T) {
TotalChunksRef: 50,
TotalChunksDownloaded: 60,
ChunksDownloadTime: time.Second.Nanoseconds(),
QueryReferencedStructured: true,
Chunk: Chunk{
HeadChunkBytes: 10,
HeadChunkLines: 20,
@ -184,6 +188,7 @@ func TestResult_Merge(t *testing.T) {
TotalChunksRef: 50,
TotalChunksDownloaded: 60,
ChunksDownloadTime: time.Second.Nanoseconds(),
QueryReferencedStructured: true,
Chunk: Chunk{
HeadChunkBytes: 10,
HeadChunkLines: 20,
@ -245,6 +250,7 @@ func TestResult_Merge(t *testing.T) {
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,

@ -447,6 +447,8 @@ 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"`
// 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:])

@ -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,

@ -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": {
@ -1977,6 +1979,7 @@ var (
TotalChunksRef: 17,
TotalChunksDownloaded: 18,
ChunkRefsFetchTime: 19,
QueryReferencedStructured: true,
},
},

@ -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,

@ -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())
})
}
})
}

@ -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,

@ -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,

Loading…
Cancel
Save