github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/tsdb/index/chunk.go (about) 1 package index 2 3 import ( 4 "sort" 5 6 "github.com/prometheus/common/model" 7 ) 8 9 // Meta holds information about a chunk of data. 10 type ChunkMeta struct { 11 Checksum uint32 12 13 MinTime, MaxTime int64 14 15 // Bytes stored, rounded to nearest KB 16 KB uint32 17 18 Entries uint32 19 } 20 21 func (c ChunkMeta) From() model.Time { return model.Time(c.MinTime) } 22 func (c ChunkMeta) Through() model.Time { return model.Time(c.MaxTime) } 23 func (c ChunkMeta) Bounds() (model.Time, model.Time) { return c.From(), c.Through() } 24 25 type ChunkMetas []ChunkMeta 26 27 func (c ChunkMetas) Len() int { return len(c) } 28 func (c ChunkMetas) Swap(i, j int) { c[i], c[j] = c[j], c[i] } 29 func (c ChunkMetas) Bounds() (mint, maxt model.Time) { 30 ln := len(c) 31 if ln == 0 { 32 return 33 } 34 35 mint, maxt = model.Time(c[0].MinTime), model.Time(c[ln-1].MaxTime) 36 // even when sorted, we need to check all chunks for maxt 37 // since we sort by (min, max, checksum). Therefore 38 // check mint here as well to ensure this works on unordered ChunkMetas too 39 for _, chk := range c { 40 from, through := chk.Bounds() 41 if mint > from { 42 mint = from 43 } 44 45 if maxt < through { 46 maxt = through 47 } 48 } 49 return 50 } 51 52 // Sort by (MinTime, MaxTime, Checksum) 53 func (c ChunkMetas) Less(i, j int) bool { 54 a, b := c[i], c[j] 55 if a.MinTime != b.MinTime { 56 return a.MinTime < b.MinTime 57 } 58 59 if a.MaxTime != b.MaxTime { 60 return a.MaxTime < b.MaxTime 61 } 62 63 return a.Checksum < b.Checksum 64 } 65 66 // Finalize sorts and dedupes 67 // TODO(owen-d): can we remove the need for this by ensuring we only push 68 // in order and without duplicates? 69 func (c ChunkMetas) Finalize() ChunkMetas { 70 sort.Sort(c) 71 72 if len(c) == 0 { 73 return c 74 } 75 76 res := ChunkMetasPool.Get() 77 lastDuplicate := -1 78 prior := c[0] 79 80 // minimize reslicing costs due to duplicates 81 for i := 1; i < len(c); i++ { 82 x := c[i] 83 if x.MinTime == prior.MinTime && x.MaxTime == prior.MaxTime && x.Checksum == prior.Checksum { 84 res = append(res, c[lastDuplicate+1:i]...) 85 lastDuplicate = i 86 } 87 prior = x 88 } 89 90 // no duplicates were found, short circuit 91 // by returning unmodified underlying slice 92 if len(res) == 0 { 93 ChunkMetasPool.Put(res) // release unused slice to pool 94 return c 95 } 96 97 // otherwise, append any remaining values 98 res = append(res, c[lastDuplicate+1:]...) 99 // release self to pool; res will be returned instead 100 ChunkMetasPool.Put(c) 101 return res 102 103 } 104 105 // Add adds ChunkMeta at the right place in order. It assumes existing ChunkMetas have already been sorted by using Finalize. 106 // There is no chance of a data loss even if the chunks are not sorted because the chunk would anyways be added so the assumption is relatively safe. 107 func (c ChunkMetas) Add(chk ChunkMeta) ChunkMetas { 108 j := sort.Search(len(c), func(i int) bool { 109 ichk := c[i] 110 if ichk.MinTime != chk.MinTime { 111 return ichk.MinTime > chk.MinTime 112 } 113 114 if ichk.MaxTime != chk.MaxTime { 115 return ichk.MaxTime > chk.MaxTime 116 } 117 118 return ichk.Checksum >= chk.Checksum 119 }) 120 121 if j < len(c) && c[j].MinTime == chk.MinTime && c[j].MaxTime == chk.MaxTime && c[j].Checksum == chk.Checksum { 122 return c 123 } 124 125 res := append(c, chk) 126 copy(res[j+1:], res[j:]) 127 res[j] = chk 128 129 return res 130 } 131 132 // Drop drops ChunkMeta. It assumes existing ChunkMetas have already been sorted by using Finalize. 133 // Calling Drop on non-sorted result can result in not dropping the chunk even if it exists. 134 // It returns a boolean indicating if the chunk was found and dropped or not so the caller can take appropriate actions if not. 135 // ToDo(Sandeep): See if we can do something about the assumption on sorted chunks. 136 // Maybe always build ChunkMetas using Add method which should always keep the ChunkMetas deduped and sorted. 137 func (c ChunkMetas) Drop(chk ChunkMeta) (ChunkMetas, bool) { 138 j := sort.Search(len(c), func(i int) bool { 139 ichk := c[i] 140 if ichk.MinTime != chk.MinTime { 141 return ichk.MinTime >= chk.MinTime 142 } 143 144 if ichk.MaxTime != chk.MaxTime { 145 return ichk.MaxTime >= chk.MaxTime 146 } 147 148 return ichk.Checksum >= chk.Checksum 149 }) 150 151 if j >= len(c) || c[j] != chk { 152 return c, false 153 } 154 155 return c[:j+copy(c[j:], c[j+1:])], true 156 }