github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/iter/cache.go (about) 1 package iter 2 3 import ( 4 "github.com/grafana/loki/pkg/logproto" 5 ) 6 7 type CacheEntryIterator interface { 8 EntryIterator 9 Wrapped() EntryIterator 10 Reset() 11 } 12 13 // cachedIterator is an iterator that caches iteration to be replayed later on. 14 type cachedIterator struct { 15 cache []entryWithLabels 16 wrapped EntryIterator // once set to nil it means we have to use the cache. 17 18 curr int 19 20 closeErr error 21 iterErr error 22 } 23 24 // NewCachedIterator creates an iterator that cache iteration result and can be iterated again 25 // after closing it without re-using the underlaying iterator `it`. 26 func NewCachedIterator(it EntryIterator, cap int) CacheEntryIterator { 27 c := &cachedIterator{ 28 wrapped: it, 29 cache: make([]entryWithLabels, 0, cap), 30 curr: -1, 31 } 32 return c 33 } 34 35 func (it *cachedIterator) Reset() { 36 it.curr = -1 37 } 38 39 func (it *cachedIterator) Wrapped() EntryIterator { 40 return it.wrapped 41 } 42 43 func (it *cachedIterator) consumeWrapped() bool { 44 if it.Wrapped() == nil { 45 return false 46 } 47 ok := it.Wrapped().Next() 48 // we're done with the base iterator. 49 if !ok { 50 it.closeErr = it.Wrapped().Close() 51 it.iterErr = it.Wrapped().Error() 52 it.wrapped = nil 53 return false 54 } 55 // we're caching entries 56 it.cache = append(it.cache, entryWithLabels{Entry: it.Wrapped().Entry(), labels: it.Wrapped().Labels(), streamHash: it.Wrapped().StreamHash()}) 57 it.curr++ 58 return true 59 } 60 61 func (it *cachedIterator) Next() bool { 62 if len(it.cache) == 0 && it.Wrapped() == nil { 63 return false 64 } 65 if it.curr+1 >= len(it.cache) { 66 if it.Wrapped() != nil { 67 return it.consumeWrapped() 68 } 69 return false 70 } 71 it.curr++ 72 return true 73 } 74 75 func (it *cachedIterator) Entry() logproto.Entry { 76 if len(it.cache) == 0 || it.curr < 0 || it.curr >= len(it.cache) { 77 return logproto.Entry{} 78 } 79 80 return it.cache[it.curr].Entry 81 } 82 83 func (it *cachedIterator) Labels() string { 84 if len(it.cache) == 0 || it.curr < 0 || it.curr >= len(it.cache) { 85 return "" 86 } 87 return it.cache[it.curr].labels 88 } 89 90 func (it *cachedIterator) StreamHash() uint64 { 91 if len(it.cache) == 0 || it.curr < 0 || it.curr >= len(it.cache) { 92 return 0 93 } 94 return it.cache[it.curr].streamHash 95 } 96 97 func (it *cachedIterator) Error() error { return it.iterErr } 98 99 func (it *cachedIterator) Close() error { 100 it.Reset() 101 return it.closeErr 102 } 103 104 type CacheSampleIterator interface { 105 SampleIterator 106 Wrapped() SampleIterator 107 Reset() 108 } 109 110 // cachedIterator is an iterator that caches iteration to be replayed later on. 111 type cachedSampleIterator struct { 112 cache []sampleWithLabels 113 wrapped SampleIterator 114 115 curr int 116 117 closeErr error 118 iterErr error 119 } 120 121 // newSampleCachedIterator creates an iterator that cache iteration result and can be iterated again 122 // after closing it without re-using the underlaying iterator `it`. 123 func NewCachedSampleIterator(it SampleIterator, cap int) CacheSampleIterator { 124 c := &cachedSampleIterator{ 125 wrapped: it, 126 cache: make([]sampleWithLabels, 0, cap), 127 curr: -1, 128 } 129 return c 130 } 131 132 func (it *cachedSampleIterator) Wrapped() SampleIterator { 133 return it.wrapped 134 } 135 136 func (it *cachedSampleIterator) Reset() { 137 it.curr = -1 138 } 139 140 func (it *cachedSampleIterator) consumeWrapped() bool { 141 if it.Wrapped() == nil { 142 return false 143 } 144 ok := it.Wrapped().Next() 145 // we're done with the base iterator. 146 if !ok { 147 it.closeErr = it.Wrapped().Close() 148 it.iterErr = it.Wrapped().Error() 149 it.wrapped = nil 150 return false 151 } 152 // we're caching entries 153 it.cache = append(it.cache, sampleWithLabels{Sample: it.Wrapped().Sample(), labels: it.Wrapped().Labels(), streamHash: it.Wrapped().StreamHash()}) 154 it.curr++ 155 return true 156 } 157 158 func (it *cachedSampleIterator) Next() bool { 159 if len(it.cache) == 0 && it.Wrapped() == nil { 160 return false 161 } 162 if it.curr+1 >= len(it.cache) { 163 if it.Wrapped() != nil { 164 return it.consumeWrapped() 165 } 166 return false 167 } 168 it.curr++ 169 return true 170 } 171 172 func (it *cachedSampleIterator) Sample() logproto.Sample { 173 if len(it.cache) == 0 || it.curr < 0 || it.curr >= len(it.cache) { 174 return logproto.Sample{} 175 } 176 return it.cache[it.curr].Sample 177 } 178 179 func (it *cachedSampleIterator) Labels() string { 180 if len(it.cache) == 0 || it.curr < 0 || it.curr >= len(it.cache) { 181 return "" 182 } 183 return it.cache[it.curr].labels 184 } 185 186 func (it *cachedSampleIterator) StreamHash() uint64 { 187 if len(it.cache) == 0 || it.curr < 0 || it.curr >= len(it.cache) { 188 return 0 189 } 190 return it.cache[it.curr].streamHash 191 } 192 193 func (it *cachedSampleIterator) Error() error { return it.iterErr } 194 195 func (it *cachedSampleIterator) Close() error { 196 it.Reset() 197 return it.closeErr 198 }