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  }