github.com/thanos-io/thanos@v0.32.5/pkg/query/iter.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package query 5 6 import ( 7 "github.com/pkg/errors" 8 "github.com/prometheus/prometheus/model/histogram" 9 "github.com/prometheus/prometheus/model/labels" 10 "github.com/prometheus/prometheus/storage" 11 "github.com/prometheus/prometheus/tsdb/chunkenc" 12 13 "github.com/thanos-io/thanos/pkg/compact/downsample" 14 "github.com/thanos-io/thanos/pkg/dedup" 15 "github.com/thanos-io/thanos/pkg/store/storepb" 16 ) 17 18 // promSeriesSet implements the SeriesSet interface of the Prometheus storage 19 // package on top of our storepb SeriesSet. Overlapping chunks will be naively deduplicated (random selection). 20 type promSeriesSet struct { 21 set storepb.SeriesSet 22 23 mint, maxt int64 24 aggrs []storepb.Aggr 25 26 warns storage.Warnings 27 } 28 29 func (s *promSeriesSet) Next() bool { 30 return s.set.Next() 31 } 32 33 func (s *promSeriesSet) At() storage.Series { 34 if s.set.Err() != nil { 35 return nil 36 } 37 38 currLset, currChunks := s.set.At() 39 return newChunkSeries(currLset, currChunks, s.mint, s.maxt, s.aggrs) 40 } 41 42 func (s *promSeriesSet) Err() error { 43 return s.set.Err() 44 } 45 46 func (s *promSeriesSet) Warnings() storage.Warnings { 47 return s.warns 48 } 49 50 // storeSeriesSet implements a storepb SeriesSet against a list of storepb.Series. 51 type storeSeriesSet struct { 52 // TODO(bwplotka): Don't buffer all, we have to buffer single series (to sort and dedup chunks), but nothing more. 53 series []storepb.Series 54 i int 55 } 56 57 func newStoreSeriesSet(s []storepb.Series) *storeSeriesSet { 58 return &storeSeriesSet{series: s, i: -1} 59 } 60 61 func (s *storeSeriesSet) Next() bool { 62 if s.i >= len(s.series)-1 { 63 return false 64 } 65 s.i++ 66 return true 67 } 68 69 func (*storeSeriesSet) Err() error { 70 return nil 71 } 72 73 func (s *storeSeriesSet) At() (labels.Labels, []storepb.AggrChunk) { 74 return s.series[s.i].PromLabels(), s.series[s.i].Chunks 75 } 76 77 // chunkSeries implements storage.Series for a series on storepb types. 78 type chunkSeries struct { 79 lset labels.Labels 80 chunks []storepb.AggrChunk 81 mint, maxt int64 82 aggrs []storepb.Aggr 83 } 84 85 // newChunkSeries allows to iterate over samples for each sorted and non-overlapped chunks. 86 func newChunkSeries(lset labels.Labels, chunks []storepb.AggrChunk, mint, maxt int64, aggrs []storepb.Aggr) *chunkSeries { 87 return &chunkSeries{ 88 lset: lset, 89 chunks: chunks, 90 mint: mint, 91 maxt: maxt, 92 aggrs: aggrs, 93 } 94 } 95 96 func (s *chunkSeries) Labels() labels.Labels { 97 return s.lset 98 } 99 100 func (s *chunkSeries) Iterator(_ chunkenc.Iterator) chunkenc.Iterator { 101 var sit chunkenc.Iterator 102 its := make([]chunkenc.Iterator, 0, len(s.chunks)) 103 104 if len(s.aggrs) == 1 { 105 switch s.aggrs[0] { 106 case storepb.Aggr_COUNT: 107 for _, c := range s.chunks { 108 its = append(its, getFirstIterator(c.Count, c.Raw)) 109 } 110 sit = newChunkSeriesIterator(its) 111 case storepb.Aggr_SUM: 112 for _, c := range s.chunks { 113 its = append(its, getFirstIterator(c.Sum, c.Raw)) 114 } 115 sit = newChunkSeriesIterator(its) 116 case storepb.Aggr_MIN: 117 for _, c := range s.chunks { 118 its = append(its, getFirstIterator(c.Min, c.Raw)) 119 } 120 sit = newChunkSeriesIterator(its) 121 case storepb.Aggr_MAX: 122 for _, c := range s.chunks { 123 its = append(its, getFirstIterator(c.Max, c.Raw)) 124 } 125 sit = newChunkSeriesIterator(its) 126 case storepb.Aggr_COUNTER: 127 for _, c := range s.chunks { 128 its = append(its, getFirstIterator(c.Counter, c.Raw)) 129 } 130 // TODO(bwplotka): This breaks resets function. See https://github.com/thanos-io/thanos/issues/3644 131 sit = downsample.NewApplyCounterResetsIterator(its...) 132 default: 133 return errSeriesIterator{err: errors.Errorf("unexpected result aggregate type %v", s.aggrs)} 134 } 135 return dedup.NewBoundedSeriesIterator(sit, s.mint, s.maxt) 136 } 137 138 if len(s.aggrs) != 2 { 139 return errSeriesIterator{err: errors.Errorf("unexpected result aggregate type %v", s.aggrs)} 140 } 141 142 switch { 143 case s.aggrs[0] == storepb.Aggr_SUM && s.aggrs[1] == storepb.Aggr_COUNT, 144 s.aggrs[0] == storepb.Aggr_COUNT && s.aggrs[1] == storepb.Aggr_SUM: 145 146 for _, c := range s.chunks { 147 if c.Raw != nil { 148 its = append(its, getFirstIterator(c.Raw)) 149 } else { 150 sum, cnt := getFirstIterator(c.Sum), getFirstIterator(c.Count) 151 its = append(its, downsample.NewAverageChunkIterator(cnt, sum)) 152 } 153 } 154 sit = newChunkSeriesIterator(its) 155 default: 156 return errSeriesIterator{err: errors.Errorf("unexpected result aggregate type %v", s.aggrs)} 157 } 158 return dedup.NewBoundedSeriesIterator(sit, s.mint, s.maxt) 159 } 160 161 func getFirstIterator(cs ...*storepb.Chunk) chunkenc.Iterator { 162 for _, c := range cs { 163 if c == nil { 164 continue 165 } 166 chk, err := chunkenc.FromData(chunkEncoding(c.Type), c.Data) 167 if err != nil { 168 return errSeriesIterator{err} 169 } 170 return chk.Iterator(nil) 171 } 172 return errSeriesIterator{errors.New("no valid chunk found")} 173 } 174 175 func chunkEncoding(e storepb.Chunk_Encoding) chunkenc.Encoding { 176 switch e { 177 case storepb.Chunk_XOR: 178 return chunkenc.EncXOR 179 case storepb.Chunk_HISTOGRAM: 180 return chunkenc.EncHistogram 181 case storepb.Chunk_FLOAT_HISTOGRAM: 182 return chunkenc.EncFloatHistogram 183 } 184 return 255 // Invalid. 185 } 186 187 type errSeriesIterator struct { 188 err error 189 } 190 191 func (errSeriesIterator) Seek(int64) chunkenc.ValueType { return chunkenc.ValNone } 192 func (errSeriesIterator) Next() chunkenc.ValueType { return chunkenc.ValNone } 193 func (errSeriesIterator) At() (int64, float64) { return 0, 0 } 194 func (errSeriesIterator) AtHistogram() (int64, *histogram.Histogram) { return 0, nil } 195 func (errSeriesIterator) AtFloatHistogram() (int64, *histogram.FloatHistogram) { return 0, nil } 196 func (errSeriesIterator) AtT() int64 { return 0 } 197 func (it errSeriesIterator) Err() error { return it.err } 198 199 // chunkSeriesIterator implements a series iterator on top 200 // of a list of time-sorted, non-overlapping chunks. 201 type chunkSeriesIterator struct { 202 chunks []chunkenc.Iterator 203 i int 204 lastVal chunkenc.ValueType 205 } 206 207 func newChunkSeriesIterator(cs []chunkenc.Iterator) chunkenc.Iterator { 208 if len(cs) == 0 { 209 // This should not happen. StoreAPI implementations should not send empty results. 210 return errSeriesIterator{err: errors.Errorf("store returned an empty result")} 211 } 212 return &chunkSeriesIterator{chunks: cs} 213 } 214 215 func (it *chunkSeriesIterator) Seek(t int64) chunkenc.ValueType { 216 // We generally expect the chunks already to be cut down 217 // to the range we are interested in. There's not much to be gained from 218 // hopping across chunks so we just call next until we reach t. 219 for { 220 ct := it.AtT() 221 if ct >= t { 222 return it.lastVal 223 } 224 it.lastVal = it.Next() 225 if it.lastVal == chunkenc.ValNone { 226 return chunkenc.ValNone 227 } 228 } 229 } 230 231 func (it *chunkSeriesIterator) At() (t int64, v float64) { 232 return it.chunks[it.i].At() 233 } 234 235 func (it *chunkSeriesIterator) AtHistogram() (int64, *histogram.Histogram) { 236 return it.chunks[it.i].AtHistogram() 237 } 238 239 func (it *chunkSeriesIterator) AtFloatHistogram() (int64, *histogram.FloatHistogram) { 240 return it.chunks[it.i].AtFloatHistogram() 241 } 242 243 func (it *chunkSeriesIterator) AtT() int64 { 244 return it.chunks[it.i].AtT() 245 } 246 247 func (it *chunkSeriesIterator) Next() chunkenc.ValueType { 248 lastT := it.AtT() 249 250 if valueType := it.chunks[it.i].Next(); valueType != chunkenc.ValNone { 251 it.lastVal = valueType 252 return valueType 253 } 254 if it.Err() != nil { 255 return chunkenc.ValNone 256 } 257 if it.i >= len(it.chunks)-1 { 258 return chunkenc.ValNone 259 } 260 // Chunks are guaranteed to be ordered but not generally guaranteed to not overlap. 261 // We must ensure to skip any overlapping range between adjacent chunks. 262 it.i++ 263 return it.Seek(lastT + 1) 264 } 265 266 func (it *chunkSeriesIterator) Err() error { 267 return it.chunks[it.i].Err() 268 } 269 270 type lazySeriesSet struct { 271 create func() (s storage.SeriesSet, ok bool) 272 273 set storage.SeriesSet 274 } 275 276 func (c *lazySeriesSet) Next() bool { 277 if c.set != nil { 278 return c.set.Next() 279 } 280 281 var ok bool 282 c.set, ok = c.create() 283 return ok 284 } 285 286 func (c *lazySeriesSet) Err() error { 287 if c.set != nil { 288 return c.set.Err() 289 } 290 return nil 291 } 292 293 func (c *lazySeriesSet) At() storage.Series { 294 if c.set != nil { 295 return c.set.At() 296 } 297 return nil 298 } 299 300 func (c *lazySeriesSet) Warnings() storage.Warnings { 301 if c.set != nil { 302 return c.set.Warnings() 303 } 304 return nil 305 }