github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/storage/m3/encoded_step_iterator_generic.go (about)

     1  // Copyright (c) 2019 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 m3
    22  
    23  import (
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/m3db/m3/src/dbnode/encoding"
    28  	"github.com/m3db/m3/src/query/block"
    29  	"github.com/m3db/m3/src/query/storage/m3/consolidators"
    30  	xerrors "github.com/m3db/m3/src/x/errors"
    31  	xsync "github.com/m3db/m3/src/x/sync"
    32  	xtime "github.com/m3db/m3/src/x/time"
    33  )
    34  
    35  type updateFn func()
    36  
    37  type encodedStepIterWithCollector struct {
    38  	lastBlock bool
    39  	finished  bool
    40  	err       error
    41  
    42  	stepTime   xtime.UnixNano
    43  	bufferTime xtime.UnixNano
    44  	blockEnd   xtime.UnixNano
    45  	meta       block.Metadata
    46  	seriesMeta []block.SeriesMeta
    47  
    48  	seriesCollectors []consolidators.StepCollector
    49  	seriesPeek       []peekValue
    50  	seriesIters      []encoding.SeriesIterator
    51  
    52  	updateFn updateFn
    53  
    54  	workerPool xsync.PooledWorkerPool
    55  	wg         sync.WaitGroup
    56  }
    57  
    58  // Moves to the next step for the i-th series in the block, populating
    59  // the collector for that step. Will keep reading values until either
    60  // hitting the next step boundary and returning, or until encountering
    61  // a point beyond the step boundary. This point is then added to a stored
    62  // peeked value that is consumed on the next pass.
    63  func nextForStep(
    64  	peek peekValue,
    65  	iter encoding.SeriesIterator,
    66  	collector consolidators.StepCollector,
    67  	stepTime xtime.UnixNano,
    68  ) (peekValue, consolidators.StepCollector, error) {
    69  	if peek.finished {
    70  		// No next value in this iterator.
    71  		return peek, collector, nil
    72  	}
    73  
    74  	if peek.started {
    75  		point := peek.point
    76  		if point.TimestampNanos.After(stepTime) {
    77  			// This point exists further than the current step
    78  			// There are next values, but this point should be NaN.
    79  			return peek, collector, nil
    80  		}
    81  
    82  		// Currently at a potentially viable data point.
    83  		// Record previously peeked value, and all potentially valid
    84  		// values, then apply consolidation function to them to get the
    85  		// consolidated point.
    86  		collector.AddPoint(point)
    87  		// clear peeked point.
    88  		peek.started = false
    89  		// If this point is currently at the boundary, finish here as there is no
    90  		// need to check any additional points in the enclosed iterator.
    91  		if point.TimestampNanos.Equal(stepTime) {
    92  			return peek, collector, nil
    93  		}
    94  	}
    95  
    96  	// Read through iterator until finding a data point outside of the
    97  	// range of this consolidated step; then consolidate those points into
    98  	// a value, set the next peek value.
    99  	for iter.Next() {
   100  		dp, _, _ := iter.Current()
   101  
   102  		// If this datapoint is before the current timestamp, add it as a
   103  		// consolidation candidate.
   104  		if !dp.TimestampNanos.After(stepTime) {
   105  			peek.started = false
   106  			collector.AddPoint(dp)
   107  		} else {
   108  			// This point exists further than the current step.
   109  			// Set peeked value to this point, then consolidate the retrieved
   110  			// series.
   111  			peek.point = dp
   112  			peek.started = true
   113  			return peek, collector, nil
   114  		}
   115  	}
   116  
   117  	peek.finished = true
   118  	return peek, collector, iter.Err()
   119  }
   120  
   121  func (it *encodedStepIterWithCollector) nextParallel(steps int) error {
   122  	var (
   123  		multiErr     xerrors.MultiError
   124  		multiErrLock sync.Mutex
   125  	)
   126  
   127  	for i := range it.seriesIters {
   128  		var (
   129  			i         = i
   130  			peek      = it.seriesPeek[i]
   131  			iter      = it.seriesIters[i]
   132  			collector = it.seriesCollectors[i]
   133  		)
   134  
   135  		it.wg.Add(1)
   136  		it.workerPool.Go(func() {
   137  			var err error
   138  			for i := 0; i < steps && err == nil; i++ {
   139  				peek, collector, err = nextForStep(peek, iter, collector,
   140  					it.bufferTime.Add(time.Duration(i)*it.meta.Bounds.StepSize))
   141  				collector.BufferStep()
   142  			}
   143  
   144  			it.seriesPeek[i] = peek
   145  			it.seriesCollectors[i] = collector
   146  			if err != nil {
   147  				multiErrLock.Lock()
   148  				multiErr = multiErr.Add(err)
   149  				multiErrLock.Unlock()
   150  			}
   151  			it.wg.Done()
   152  		})
   153  	}
   154  
   155  	it.wg.Wait()
   156  	if it.err = multiErr.FinalError(); it.err != nil {
   157  		return it.err
   158  	}
   159  
   160  	return nil
   161  }
   162  
   163  func (it *encodedStepIterWithCollector) nextSequential(steps int) error {
   164  	for i, iter := range it.seriesIters {
   165  		var (
   166  			peek      = it.seriesPeek[i]
   167  			collector = it.seriesCollectors[i]
   168  			err       error
   169  		)
   170  
   171  		for i := 0; i < steps && err == nil; i++ {
   172  			peek, collector, err = nextForStep(
   173  				peek,
   174  				iter,
   175  				collector,
   176  				it.bufferTime.Add(time.Duration(i)*it.meta.Bounds.StepSize),
   177  			)
   178  			collector.BufferStep()
   179  		}
   180  
   181  		it.seriesPeek[i] = peek
   182  		it.seriesCollectors[i] = collector
   183  		if err != nil {
   184  			return err
   185  		}
   186  	}
   187  
   188  	return nil
   189  }
   190  
   191  func (it *encodedStepIterWithCollector) Next() bool {
   192  	if it.err != nil || it.finished {
   193  		return false
   194  	}
   195  
   196  	if !it.bufferTime.After(it.stepTime) {
   197  		it.bufferTime = it.stepTime
   198  
   199  		steps := int(it.blockEnd.Sub(it.stepTime) / it.meta.Bounds.StepSize)
   200  		if steps > consolidators.BufferSteps {
   201  			steps = consolidators.BufferSteps
   202  		}
   203  
   204  		if steps > 0 {
   205  			// NB: If no reader worker pool configured, use sequential iteration.
   206  			if it.workerPool == nil {
   207  				it.err = it.nextSequential(steps)
   208  			} else {
   209  				it.err = it.nextParallel(steps)
   210  			}
   211  
   212  			bufferedDuration := time.Duration(steps) * it.meta.Bounds.StepSize
   213  			it.bufferTime = it.bufferTime.Add(bufferedDuration)
   214  		}
   215  	}
   216  
   217  	if it.err != nil {
   218  		return false
   219  	}
   220  
   221  	it.stepTime = it.stepTime.Add(it.meta.Bounds.StepSize)
   222  	next := !it.blockEnd.Before(it.stepTime)
   223  	if next && it.updateFn != nil {
   224  		it.updateFn()
   225  	}
   226  
   227  	if !next {
   228  		it.finished = true
   229  	}
   230  
   231  	return next
   232  }
   233  
   234  func (it *encodedStepIterWithCollector) StepCount() int {
   235  	return it.meta.Bounds.Steps()
   236  }
   237  
   238  func (it *encodedStepIterWithCollector) SeriesMeta() []block.SeriesMeta {
   239  	return it.seriesMeta
   240  }
   241  
   242  func (it *encodedStepIterWithCollector) Err() error {
   243  	return it.err
   244  }
   245  
   246  func (it *encodedStepIterWithCollector) Close() {
   247  	// noop, as the resources at the step may still be in use;
   248  	// instead call Close() on the encodedBlock that generated this
   249  }