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  }