github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/encoding/iterators.go (about)

     1  // Copyright (c) 2017 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package encoding
    22  
    23  import (
    24  	"math"
    25  	"sort"
    26  
    27  	"github.com/m3db/m3/src/dbnode/ts"
    28  	xtime "github.com/m3db/m3/src/x/time"
    29  )
    30  
    31  var (
    32  	// UnixNano is an int64, so the max time is the max of that type.
    33  	timeMaxNanos = xtime.UnixNano(math.MaxInt64)
    34  )
    35  
    36  // iterators is a collection of iterators, and allows for reading in order values
    37  // from the underlying iterators that are separately in order themselves.
    38  type iterators struct {
    39  	values             []Iterator
    40  	earliest           []Iterator
    41  	earliestAt         xtime.UnixNano
    42  	filterStart        xtime.UnixNano
    43  	filterEnd          xtime.UnixNano
    44  	filtering          bool
    45  	equalTimesStrategy IterateEqualTimestampStrategy
    46  
    47  	firstAnnotationHolder annotationHolder
    48  
    49  	// Used for caching reuse of value frequency lookup
    50  	valueFrequencies map[float64]int
    51  
    52  	// closeIters controls whether the iterators is responsible for closing the underlying iters.
    53  	closeIters bool
    54  }
    55  
    56  func (i *iterators) len() int {
    57  	return len(i.values)
    58  }
    59  
    60  func (i *iterators) current() (ts.Datapoint, xtime.Unit, ts.Annotation) {
    61  	numIters := len(i.earliest)
    62  
    63  	switch i.equalTimesStrategy {
    64  	case IterateHighestValue:
    65  		sort.Slice(i.earliest, func(a, b int) bool {
    66  			currA, _, _ := i.earliest[a].Current()
    67  			currB, _, _ := i.earliest[b].Current()
    68  			return currA.Value < currB.Value
    69  		})
    70  
    71  	case IterateLowestValue:
    72  		sort.Slice(i.earliest, func(a, b int) bool {
    73  			currA, _, _ := i.earliest[a].Current()
    74  			currB, _, _ := i.earliest[b].Current()
    75  			return currA.Value > currB.Value
    76  		})
    77  
    78  	case IterateHighestFrequencyValue:
    79  		// Calculate frequencies
    80  		if i.valueFrequencies == nil {
    81  			i.valueFrequencies = make(map[float64]int)
    82  		}
    83  		for _, iter := range i.earliest {
    84  			curr, _, _ := iter.Current()
    85  			i.valueFrequencies[curr.Value]++
    86  		}
    87  
    88  		// Sort
    89  		sort.Slice(i.earliest, func(a, b int) bool {
    90  			currA, _, _ := i.earliest[a].Current()
    91  			currB, _, _ := i.earliest[b].Current()
    92  			freqA := i.valueFrequencies[currA.Value]
    93  			freqB := i.valueFrequencies[currB.Value]
    94  			return freqA < freqB
    95  		})
    96  
    97  		// Reset reusable value frequencies
    98  		for key := range i.valueFrequencies {
    99  			delete(i.valueFrequencies, key)
   100  		}
   101  
   102  	default:
   103  		// IterateLastPushed or unknown strategy code path, don't panic on unknown
   104  		// as this is an internal data structure and this option is validated at a
   105  		// layer above.
   106  	}
   107  
   108  	return i.earliest[numIters-1].Current()
   109  }
   110  
   111  func (i *iterators) at() xtime.UnixNano {
   112  	return i.earliestAt
   113  }
   114  
   115  func (i *iterators) push(iter Iterator) bool {
   116  	_, _, annotation := iter.Current()
   117  	if i.filtering && !i.moveIteratorToFilterNext(iter) {
   118  		return false
   119  	}
   120  	i.values = append(i.values, iter)
   121  	i.tryAddEarliest(iter, annotation)
   122  	return true
   123  }
   124  
   125  func (i *iterators) tryAddEarliest(iter Iterator, firstAnnotation ts.Annotation) {
   126  	dp, _, _ := iter.Current()
   127  	if dp.TimestampNanos == i.earliestAt {
   128  		// Push equal earliest
   129  		i.earliest = append(i.earliest, iter)
   130  	} else if dp.TimestampNanos < i.earliestAt {
   131  		// Reset earliest and push new iter
   132  		i.earliest = append(i.earliest[:0], iter)
   133  		i.earliestAt = dp.TimestampNanos
   134  		if len(firstAnnotation) > 0 {
   135  			i.firstAnnotationHolder.set(firstAnnotation)
   136  		}
   137  	}
   138  }
   139  
   140  func (i *iterators) moveIteratorToFilterNext(iter Iterator) bool {
   141  	next := true
   142  	for next {
   143  		dp, _, _ := iter.Current()
   144  		if dp.TimestampNanos < i.filterStart {
   145  			// Filter out any before start
   146  			next = iter.Next()
   147  			continue
   148  		}
   149  		if dp.TimestampNanos >= i.filterEnd {
   150  			// Filter out completely if after end
   151  			next = false
   152  			break
   153  		}
   154  		// Within filter
   155  		break
   156  	}
   157  	return next
   158  }
   159  
   160  func (i *iterators) moveToValidNext() (bool, error) {
   161  	var (
   162  		prevAt = i.earliestAt
   163  		n      = len(i.values)
   164  	)
   165  	for _, iter := range i.earliest {
   166  		next := iter.Next()
   167  		if next && i.filtering {
   168  			// Filter out values if applying filters
   169  			next = i.moveIteratorToFilterNext(iter)
   170  		}
   171  
   172  		err := iter.Err()
   173  		if err != nil {
   174  			i.reset()
   175  			return false, err
   176  		}
   177  
   178  		if next {
   179  			continue
   180  		}
   181  
   182  		// No next so swap tail in and shrink by one
   183  		if i.closeIters {
   184  			iter.Close()
   185  		}
   186  		idx := -1
   187  		for i, curr := range i.values {
   188  			if curr == iter {
   189  				idx = i
   190  				break
   191  			}
   192  		}
   193  		i.values[idx] = i.values[n-1]
   194  		i.values[n-1] = nil
   195  		i.values = i.values[:n-1]
   196  		n = n - 1
   197  	}
   198  
   199  	// Reset earliest
   200  	for idx := range i.earliest {
   201  		i.earliest[idx] = nil
   202  	}
   203  	i.earliest = i.earliest[:0]
   204  
   205  	// No iterators left
   206  	if n == 0 {
   207  		i.reset()
   208  		return false, nil
   209  	}
   210  
   211  	// Force first to be new earliest, evaluate rest
   212  	i.earliestAt = timeMaxNanos
   213  	for _, iter := range i.values {
   214  		i.tryAddEarliest(iter, nil)
   215  	}
   216  
   217  	// Apply filter to new earliest if necessary
   218  	if i.filtering {
   219  		inFilter := i.earliestAt < i.filterEnd &&
   220  			i.earliestAt >= i.filterStart
   221  		if !inFilter {
   222  			return i.moveToValidNext()
   223  		}
   224  	}
   225  
   226  	return i.validateNext(true, prevAt)
   227  }
   228  
   229  func (i *iterators) validateNext(next bool, prevAt xtime.UnixNano) (bool, error) {
   230  	if i.earliestAt < prevAt {
   231  		// Out of order datapoint
   232  		i.reset()
   233  		return false, errOutOfOrderIterator
   234  	}
   235  	return next, nil
   236  }
   237  
   238  func (i *iterators) firstAnnotation() ts.Annotation {
   239  	return i.firstAnnotationHolder.get()
   240  }
   241  
   242  func (i *iterators) reset() {
   243  	for idx := range i.values {
   244  		if i.closeIters {
   245  			i.values[idx].Close()
   246  		}
   247  		i.values[idx] = nil
   248  	}
   249  	i.values = i.values[:0]
   250  	for idx := range i.earliest {
   251  		i.earliest[idx] = nil
   252  	}
   253  	i.earliest = i.earliest[:0]
   254  	i.earliestAt = timeMaxNanos
   255  	i.firstAnnotationHolder.reset()
   256  }
   257  
   258  func (i *iterators) setFilter(start, end xtime.UnixNano) {
   259  	i.filtering = true
   260  	i.filterStart = start
   261  	i.filterEnd = end
   262  }