github.com/thanos-io/thanos@v0.32.5/pkg/compactv2/chunk_series_set.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package compactv2
     5  
     6  import (
     7  	"context"
     8  
     9  	"github.com/pkg/errors"
    10  	"github.com/prometheus/prometheus/model/histogram"
    11  	"github.com/prometheus/prometheus/model/labels"
    12  	"github.com/prometheus/prometheus/storage"
    13  	"github.com/prometheus/prometheus/tsdb"
    14  	"github.com/prometheus/prometheus/tsdb/chunkenc"
    15  	"github.com/prometheus/prometheus/tsdb/chunks"
    16  	"github.com/prometheus/prometheus/tsdb/index"
    17  
    18  	"github.com/thanos-io/thanos/pkg/block"
    19  )
    20  
    21  type lazyPopulateChunkSeriesSet struct {
    22  	sReader seriesReader
    23  
    24  	all index.Postings
    25  
    26  	bufChks []chunks.Meta
    27  	bufLbls labels.ScratchBuilder
    28  
    29  	curr *storage.ChunkSeriesEntry
    30  	err  error
    31  }
    32  
    33  func newLazyPopulateChunkSeriesSet(sReader seriesReader, all index.Postings) *lazyPopulateChunkSeriesSet {
    34  	return &lazyPopulateChunkSeriesSet{sReader: sReader, all: all}
    35  }
    36  
    37  func (s *lazyPopulateChunkSeriesSet) Next() bool {
    38  	for s.all.Next() {
    39  		if err := s.sReader.ir.Series(s.all.At(), &s.bufLbls, &s.bufChks); err != nil {
    40  			// Postings may be stale. Skip if no underlying series exists.
    41  			if errors.Cause(err) == storage.ErrNotFound {
    42  				continue
    43  			}
    44  			s.err = errors.Wrapf(err, "get series %d", s.all.At())
    45  			return false
    46  		}
    47  
    48  		if len(s.bufChks) == 0 {
    49  			continue
    50  		}
    51  
    52  		for i := range s.bufChks {
    53  			s.bufChks[i].Chunk = &lazyPopulatableChunk{cr: s.sReader.cr, m: &s.bufChks[i]}
    54  		}
    55  		s.curr = &storage.ChunkSeriesEntry{
    56  			Lset: s.bufLbls.Labels(),
    57  			ChunkIteratorFn: func(_ chunks.Iterator) chunks.Iterator {
    58  				return storage.NewListChunkSeriesIterator(s.bufChks...)
    59  			},
    60  		}
    61  		return true
    62  	}
    63  	return false
    64  }
    65  
    66  func (s *lazyPopulateChunkSeriesSet) At() storage.ChunkSeries {
    67  	return s.curr
    68  }
    69  
    70  func (s *lazyPopulateChunkSeriesSet) Err() error {
    71  	if s.err != nil {
    72  		return s.err
    73  	}
    74  	return s.all.Err()
    75  }
    76  
    77  func (s *lazyPopulateChunkSeriesSet) Warnings() storage.Warnings { return nil }
    78  
    79  type lazyPopulatableChunk struct {
    80  	m *chunks.Meta
    81  
    82  	cr tsdb.ChunkReader
    83  
    84  	populated chunkenc.Chunk
    85  }
    86  
    87  type errChunkIterator struct{ err error }
    88  
    89  func (e errChunkIterator) Seek(int64) chunkenc.ValueType { return chunkenc.ValNone }
    90  func (e errChunkIterator) At() (int64, float64)          { return 0, 0 }
    91  
    92  // TODO(rabenhorst): Needs to be implemented for native histogram support.
    93  func (e errChunkIterator) AtHistogram() (int64, *histogram.Histogram) { panic("not implemented") }
    94  func (e errChunkIterator) AtFloatHistogram() (int64, *histogram.FloatHistogram) {
    95  	panic("not implemented")
    96  }
    97  func (e errChunkIterator) AtT() int64               { return 0 }
    98  func (e errChunkIterator) Next() chunkenc.ValueType { return chunkenc.ValNone }
    99  func (e errChunkIterator) Err() error               { return e.err }
   100  
   101  type errChunk struct{ err errChunkIterator }
   102  
   103  func (e errChunk) Bytes() []byte                                { return nil }
   104  func (e errChunk) Encoding() chunkenc.Encoding                  { return chunkenc.EncXOR }
   105  func (e errChunk) Appender() (chunkenc.Appender, error)         { return nil, e.err.err }
   106  func (e errChunk) Iterator(chunkenc.Iterator) chunkenc.Iterator { return e.err }
   107  func (e errChunk) NumSamples() int                              { return 0 }
   108  func (e errChunk) Compact()                                     {}
   109  
   110  func (l *lazyPopulatableChunk) populate() {
   111  	// TODO(bwplotka): In most cases we don't need to parse anything, just copy. Extend reader/writer for this.
   112  	var err error
   113  	l.populated, err = l.cr.Chunk(*l.m)
   114  	if err != nil {
   115  		l.m.Chunk = errChunk{err: errChunkIterator{err: errors.Wrapf(err, "cannot populate chunk %d", l.m.Ref)}}
   116  		return
   117  	}
   118  
   119  	l.m.Chunk = l.populated
   120  }
   121  
   122  func (l *lazyPopulatableChunk) Bytes() []byte {
   123  	if l.populated == nil {
   124  		l.populate()
   125  	}
   126  	return l.populated.Bytes()
   127  }
   128  
   129  func (l *lazyPopulatableChunk) Encoding() chunkenc.Encoding {
   130  	if l.populated == nil {
   131  		l.populate()
   132  	}
   133  	return l.populated.Encoding()
   134  }
   135  
   136  func (l *lazyPopulatableChunk) Appender() (chunkenc.Appender, error) {
   137  	if l.populated == nil {
   138  		l.populate()
   139  	}
   140  	return l.populated.Appender()
   141  }
   142  
   143  func (l *lazyPopulatableChunk) Iterator(iterator chunkenc.Iterator) chunkenc.Iterator {
   144  	if l.populated == nil {
   145  		l.populate()
   146  	}
   147  	return l.populated.Iterator(iterator)
   148  }
   149  
   150  func (l *lazyPopulatableChunk) NumSamples() int {
   151  	if l.populated == nil {
   152  		l.populate()
   153  	}
   154  	return l.populated.NumSamples()
   155  }
   156  
   157  func (l *lazyPopulatableChunk) Compact() {
   158  	if l.populated == nil {
   159  		l.populate()
   160  	}
   161  	l.populated.Compact()
   162  }
   163  
   164  func (w *Compactor) write(ctx context.Context, symbols index.StringIter, populatedSet storage.ChunkSeriesSet, sWriter block.SeriesWriter, p ProgressLogger) error {
   165  	var (
   166  		chks []chunks.Meta
   167  		ref  storage.SeriesRef
   168  	)
   169  
   170  	for symbols.Next() {
   171  		if err := sWriter.AddSymbol(symbols.At()); err != nil {
   172  			return errors.Wrap(err, "add symbol")
   173  		}
   174  	}
   175  	if err := symbols.Err(); err != nil {
   176  		return errors.Wrap(err, "symbols")
   177  	}
   178  
   179  	// Iterate over all sorted chunk series.
   180  	for populatedSet.Next() {
   181  		select {
   182  		case <-ctx.Done():
   183  			return ctx.Err()
   184  		default:
   185  		}
   186  
   187  		s := populatedSet.At()
   188  		chksIter := s.Iterator(nil)
   189  		chks = chks[:0]
   190  		for chksIter.Next() {
   191  			// We are not iterating in streaming way over chunk as it's more efficient to do bulk write for index and
   192  			// chunk file purposes.
   193  			chks = append(chks, chksIter.At())
   194  		}
   195  
   196  		if chksIter.Err() != nil {
   197  			return errors.Wrap(chksIter.Err(), "chunk iter")
   198  		}
   199  
   200  		// Skip the series with all deleted chunks.
   201  		if len(chks) == 0 {
   202  			// All series will be ignored.
   203  			p.SeriesProcessed()
   204  			continue
   205  		}
   206  
   207  		if err := sWriter.WriteChunks(chks...); err != nil {
   208  			return errors.Wrap(err, "write chunks")
   209  		}
   210  		if err := sWriter.AddSeries(ref, s.Labels(), chks...); err != nil {
   211  			return errors.Wrap(err, "add series")
   212  		}
   213  		for _, chk := range chks {
   214  			// ChunkPool is used by tsdb.OpenBlock BlockReader.
   215  			if err := w.chunkPool.Put(chk.Chunk); err != nil {
   216  				return errors.Wrap(err, "put chunk")
   217  			}
   218  		}
   219  		ref++
   220  		p.SeriesProcessed()
   221  	}
   222  	if populatedSet.Err() != nil {
   223  		return errors.Wrap(populatedSet.Err(), "iterate populated chunk series set")
   224  	}
   225  
   226  	return nil
   227  }