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 }