github.com/thanos-io/thanos@v0.32.5/pkg/dedup/pushdown_iter.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package dedup 5 6 import ( 7 "fmt" 8 "math" 9 10 "github.com/prometheus/prometheus/model/histogram" 11 "github.com/prometheus/prometheus/model/labels" 12 "github.com/prometheus/prometheus/tsdb/chunkenc" 13 ) 14 15 // PushdownMarker is a label that gets attached on pushed down series so that 16 // the receiver would be able to handle them in potentially special way. 17 var PushdownMarker = labels.Label{Name: "__thanos_pushed_down", Value: "true"} 18 19 type pushdownSeriesIterator struct { 20 a, b chunkenc.Iterator 21 aval, bval chunkenc.ValueType 22 aused, bused bool 23 24 function func(float64, float64) float64 25 } 26 27 // newPushdownSeriesIterator constructs a new iterator that steps through both 28 // series and performs the following algorithm: 29 // * If both timestamps match up then the function is applied on them; 30 // * If one of the series has a gap then the other one is used until the timestamps match up. 31 // It is guaranteed that stepping through both of them that the timestamps will match eventually 32 // because the samples have been processed by a PromQL engine. 33 func newPushdownSeriesIterator(a, b chunkenc.Iterator, function string) *pushdownSeriesIterator { 34 var fn func(float64, float64) float64 35 switch function { 36 case "max", "max_over_time": 37 fn = math.Max 38 case "min", "min_over_time": 39 fn = math.Min 40 default: 41 panic(fmt.Errorf("unsupported function %s passed", function)) 42 } 43 return &pushdownSeriesIterator{ 44 a: a, b: b, function: fn, aused: true, bused: true, 45 } 46 } 47 48 func (it *pushdownSeriesIterator) Next() chunkenc.ValueType { 49 // Push A if we've used A before. Push B if we've used B before. 50 // Push both if we've used both before. 51 switch { 52 case !it.aused && !it.bused: 53 return chunkenc.ValNone 54 case it.aused && !it.bused: 55 it.aval = it.a.Next() 56 case !it.aused && it.bused: 57 it.bval = it.b.Next() 58 case it.aused && it.bused: 59 it.aval = it.a.Next() 60 it.bval = it.b.Next() 61 } 62 it.aused = false 63 it.bused = false 64 65 if it.aval != chunkenc.ValNone { 66 return it.aval 67 } 68 69 if it.bval != chunkenc.ValNone { 70 return it.bval 71 } 72 73 return chunkenc.ValNone 74 } 75 76 func (it *pushdownSeriesIterator) At() (int64, float64) { 77 78 var timestamp int64 79 var val float64 80 81 if it.aval != chunkenc.ValNone && it.bval != chunkenc.ValNone { 82 ta, va := it.a.At() 83 tb, vb := it.b.At() 84 if ta == tb { 85 val = it.function(va, vb) 86 timestamp = ta 87 it.aused = true 88 it.bused = true 89 } else { 90 if ta < tb { 91 timestamp = ta 92 val = va 93 it.aused = true 94 } else { 95 timestamp = tb 96 val = vb 97 it.bused = true 98 } 99 } 100 } else if it.aval != chunkenc.ValNone { 101 ta, va := it.a.At() 102 val = va 103 timestamp = ta 104 it.aused = true 105 } else { 106 tb, vb := it.b.At() 107 val = vb 108 timestamp = tb 109 it.bused = true 110 } 111 112 return timestamp, val 113 } 114 115 // TODO(rabenhorst): Needs to be implemented for native histogram support. 116 func (it *pushdownSeriesIterator) AtHistogram() (int64, *histogram.Histogram) { 117 panic("not implemented") 118 } 119 120 func (it *pushdownSeriesIterator) AtFloatHistogram() (int64, *histogram.FloatHistogram) { 121 panic("not implemented") 122 } 123 124 func (it *pushdownSeriesIterator) AtT() int64 { 125 t := it.a.AtT() 126 return t 127 } 128 129 func (it *pushdownSeriesIterator) Seek(t int64) chunkenc.ValueType { 130 for { 131 ts := it.AtT() 132 if ts >= t { 133 return chunkenc.ValFloat 134 } 135 if it.Next() == chunkenc.ValNone { 136 return chunkenc.ValNone 137 } 138 } 139 } 140 141 func (it *pushdownSeriesIterator) Err() error { 142 if it.a.Err() != nil { 143 return it.a.Err() 144 } 145 return it.b.Err() 146 }