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 }