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 }