github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/internal/cache/lfucache/compaction.go (about) 1 // Copyright 2021 The Bitalosdb author(hustxrb@163.com) and other contributors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package lfucache 16 17 import ( 18 "encoding/binary" 19 "fmt" 20 "runtime/debug" 21 "time" 22 23 "github.com/zuoyebang/bitalosdb/internal/cache/lfucache/internal/arenaskl" 24 "github.com/zuoyebang/bitalosdb/internal/humanize" 25 "github.com/zuoyebang/bitalosdb/internal/utils" 26 ) 27 28 const ( 29 keyFlushInvalid int = iota 30 keyFlushDelete 31 keyFlushReserve 32 ) 33 34 const ( 35 compactTypeMemFlush int = iota + 1 36 compactTypeClosed 37 ) 38 39 func (s *shard) compact(ctype int) { 40 if ctype == compactTypeMemFlush && s.getMemtableNum() <= 1 { 41 return 42 } 43 44 var n, memFlushNum int 45 var flushing flushableList 46 47 s.mu.Lock() 48 49 if ctype == compactTypeClosed { 50 s.mu.memMutable.writerUnref() 51 memFlushNum = len(s.mu.memQueue) 52 } else { 53 memFlushNum = len(s.mu.memQueue) - 1 54 } 55 56 for ; n < memFlushNum; n++ { 57 if !s.mu.memQueue[n].readyForFlush() { 58 break 59 } 60 } 61 62 if n > 0 { 63 flushing = append(flushing, s.mu.memQueue[:n]...) 64 if s.mu.arrtable != nil { 65 flushing = append(flushing, s.mu.arrtable) 66 } 67 } 68 69 s.mu.Unlock() 70 71 if len(flushing) == 0 { 72 return 73 } 74 75 arrtable, err := s.runCompaction(flushing, ctype) 76 if err != nil { 77 s.lc.logger.Errorf("[FLUSHLFUCACHE] shard:%d flushCnt:%d ctype:%d runCompaction err:%s", s.index, s.flushCnt, ctype, err) 78 return 79 } 80 81 s.mu.Lock() 82 defer s.mu.Unlock() 83 84 s.mu.memQueue = s.mu.memQueue[n:] 85 s.mu.arrtable = arrtable 86 s.updateReadStateLocked() 87 for i := range flushing { 88 flushing[i].readerUnref() 89 } 90 } 91 92 func (s *shard) runCompaction(flushing flushableList, ctype int) (_ *flushableEntry, retErr error) { 93 var bytesIterated uint64 94 var prevVal []byte 95 var prevValLen int 96 var prevFreq uint16 97 var prevEntrySize int 98 99 s.flushCnt++ 100 logTag := fmt.Sprintf("[FLUSHLFUCACHE] shard:%d flushCnt:%d ctype:%d", s.index, s.flushCnt, ctype) 101 s.lc.logger.Infof("%s flush start", logTag) 102 startTime := time.Now() 103 defer func() { 104 if r := recover(); r != nil { 105 s.lc.logger.Errorf("%s runCompaction panic err:%v stack=%s", logTag, r, string(debug.Stack())) 106 } 107 }() 108 109 iiter := newInputIter(flushing, &bytesIterated) 110 iter := newCompactionIter(iiter) 111 defer func() { 112 if iter != nil { 113 retErr = utils.FirstError(retErr, iter.Close()) 114 } 115 }() 116 117 nowTime := uint16((time.Now().Unix() - s.lc.launchTime) / 3600) 118 119 checkKeyStatus := func(k *internalKey, v []byte, t uint16, f uint16) int { 120 if k.Kind() != internalKeyKindSet { 121 return keyFlushDelete 122 } 123 124 vLen := len(v) 125 if t > 0 && nowTime-binary.BigEndian.Uint16(v[vLen-LFU_META_LENGTH:]) > t { 126 return keyFlushInvalid 127 } 128 129 if f > 0 && binary.BigEndian.Uint16(v[vLen-LFU_FREQ_LENGTH:]) < f { 130 return keyFlushInvalid 131 } 132 133 return keyFlushReserve 134 } 135 136 s.clearFreqLevelStat() 137 138 for key, val := iter.First(); key != nil; key, val = iter.Next() { 139 keyStatus := checkKeyStatus(key, val, 0, 0) 140 if keyStatus == keyFlushReserve { 141 if prevValLen > 2 { 142 prevFreq = binary.BigEndian.Uint16(prevVal[prevValLen-LFU_FREQ_LENGTH:]) 143 tm := nowTime - binary.BigEndian.Uint16(prevVal[prevValLen-LFU_META_LENGTH:]) 144 s.updateFreqLevelStat(tm, prevFreq, prevEntrySize) 145 } 146 147 prevVal = val 148 prevValLen = len(prevVal) 149 prevEntrySize = arrayTableEntrySize(len(key.UserKey), len(val)) 150 } else { 151 prevVal = nil 152 prevValLen = 0 153 prevEntrySize = 0 154 } 155 } 156 157 if prevValLen > 2 { 158 prevFreq = binary.BigEndian.Uint16(prevVal[prevValLen-LFU_FREQ_LENGTH:]) 159 tm := nowTime - binary.BigEndian.Uint16(prevVal[prevValLen-LFU_META_LENGTH:]) 160 s.updateFreqLevelStat(tm, prevFreq, prevEntrySize) 161 } 162 163 atSize := s.memSize + s.memSize/2 164 at, atEntry := s.newArrayTable(atSize) 165 defer func() { 166 if retErr != nil { 167 atEntry.readerUnref() 168 } 169 }() 170 171 freqThreshold, freqTm, freqAvg := s.calculateFreqLevel(atSize) 172 173 for key, val := iter.First(); key != nil; key, val = iter.Next() { 174 keyStatus := checkKeyStatus(key, val, freqTm, freqThreshold) 175 if keyStatus == keyFlushReserve { 176 freq := binary.BigEndian.Uint16(val[len(val)-LFU_FREQ_LENGTH:]) 177 if freq <= freqAvg { 178 freq = 0 179 } else { 180 freq -= freqAvg 181 } 182 binary.BigEndian.PutUint16(val[len(val)-LFU_FREQ_LENGTH:], freq) 183 184 retErr = at.add(key.UserKey, val) 185 if retErr != nil { 186 if retErr == arenaskl.ErrArenaFull { 187 break 188 } else { 189 return nil, retErr 190 } 191 } 192 } 193 } 194 195 duration := time.Since(startTime) 196 s.lc.logger.Infof("%s flush finish table(%d) iterated(%s) written(%s), in %.3fs, output rate %s/s", 197 logTag, 198 len(flushing), 199 humanize.Uint64(bytesIterated), 200 humanize.Uint64(uint64(atSize)), 201 duration.Seconds(), 202 humanize.Uint64(uint64(float64(atSize)/duration.Seconds())), 203 ) 204 205 return atEntry, nil 206 } 207 208 func newInputIter(flushing flushableList, bytesIterated *uint64) internalIterator { 209 if len(flushing) == 1 { 210 iter := flushing[0].newFlushIter(nil, bytesIterated) 211 return iter 212 } 213 214 iters := make([]internalIterator, 0, len(flushing)) 215 for i := range flushing { 216 iters = append(iters, flushing[i].newFlushIter(nil, bytesIterated)) 217 } 218 return newMergingIter(iters...) 219 }