github.com/zuoyebang/bitalosdb@v1.1.1-0.20240516111551-79a8c4d8ce20/bitree/bithash.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 bitree
    16  
    17  import (
    18  	"fmt"
    19  	"time"
    20  
    21  	"github.com/zuoyebang/bitalosdb/bithash"
    22  	"github.com/zuoyebang/bitalosdb/internal/base"
    23  	"github.com/zuoyebang/bitalosdb/internal/hash"
    24  	"github.com/zuoyebang/bitalosdb/internal/utils"
    25  )
    26  
    27  func (t *Bitree) bithashGetByHash(key []byte, khash uint32, fn uint32) ([]byte, func(), error) {
    28  	return t.bhash.Get(key, khash, bithash.FileNum(fn))
    29  }
    30  
    31  func (t *Bitree) bithashGet(key []byte, fn uint32) ([]byte, func(), error) {
    32  	khash := hash.Crc32(key)
    33  	return t.bhash.Get(key, khash, bithash.FileNum(fn))
    34  }
    35  
    36  func (t *Bitree) bithashDelete(fn uint32) error {
    37  	if !t.opts.UseBithash {
    38  		return nil
    39  	}
    40  
    41  	return t.bhash.Delete(bithash.FileNum(fn))
    42  }
    43  
    44  func (t *Bitree) BithashStats() *bithash.Stats {
    45  	if t.bhash == nil {
    46  		return nil
    47  	}
    48  	return t.bhash.Stats()
    49  }
    50  
    51  func (t *Bitree) BithashDebugInfo(dataType string) string {
    52  	if t.bhash == nil {
    53  		return ""
    54  	}
    55  	return t.bhash.DebugInfo(dataType)
    56  }
    57  
    58  func (t *Bitree) CompactBithash(deletePercent float64) {
    59  	if t.bhash == nil {
    60  		return
    61  	}
    62  
    63  	logTag := fmt.Sprintf("[COMPACTBITHASH %d]", t.index)
    64  
    65  	delFiles := t.bhash.CheckFilesDelPercent(deletePercent)
    66  	delFilesNum := len(delFiles)
    67  	if delFilesNum > 1 {
    68  		var remainPercent float64
    69  		var delFileNums []bithash.FileNum
    70  		for i, file := range delFiles {
    71  			if file.DelPercent < 1 {
    72  				remainPercent += 1 - file.DelPercent
    73  			}
    74  			delFileNums = append(delFileNums, file.FileNum)
    75  			if remainPercent > 0.95 || (i == delFilesNum-1 && len(delFileNums) > 1) {
    76  				t.opts.Logger.Infof("%s compact bithash delPercent files start compactFn:%v remainPercent:%.4f",
    77  					logTag, delFileNums, remainPercent)
    78  				err := t.compactBithashFiles(delFileNums, logTag)
    79  				if err != nil {
    80  					t.opts.Logger.Errorf("%s compact bithash delPercent files fail err:%s", logTag, err)
    81  				}
    82  				remainPercent = 0
    83  				delFileNums = delFileNums[:0]
    84  			}
    85  		}
    86  	}
    87  
    88  	miniFiles := t.bhash.CheckFilesMiniSize()
    89  	miniFilesNum := len(miniFiles)
    90  	if miniFilesNum > 1 {
    91  		var remainSize int64
    92  		var miniFileNums []bithash.FileNum
    93  		fileMaxSize := t.bhash.TableMaxSize() - (1 << 20)
    94  		for i, file := range miniFiles {
    95  			if file.Size > 0 {
    96  				remainSize += file.Size
    97  			}
    98  			miniFileNums = append(miniFileNums, file.FileNum)
    99  			if remainSize > fileMaxSize || (i == miniFilesNum-1 && len(miniFileNums) > 1) {
   100  				t.opts.Logger.Infof("%s compact bithash miniSize files start compactFn:%v remainSize:%s",
   101  					logTag, miniFileNums, utils.FmtSize(uint64(remainSize)))
   102  				err := t.compactBithashFiles(miniFileNums, logTag)
   103  				if err != nil {
   104  					t.opts.Logger.Errorf("%s compact bithash miniSize files fail err:%s", logTag, err)
   105  				}
   106  				remainSize = 0
   107  				miniFileNums = miniFileNums[:0]
   108  			}
   109  		}
   110  	}
   111  }
   112  
   113  func (t *Bitree) compactBithashFiles(fileNums []bithash.FileNum, logTag string) error {
   114  	t.dbState.LockTask()
   115  	defer t.dbState.UnlockTask()
   116  
   117  	bw, err := t.bhash.NewBithashWriter(true)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	dstFn := bw.GetFileNum()
   123  
   124  	defer func() {
   125  		if err != nil {
   126  			if err1 := bw.Remove(); err1 != nil {
   127  				t.opts.Logger.Errorf("%s remove BithashWriter fail err:%s", logTag, err1)
   128  			}
   129  		}
   130  	}()
   131  
   132  	var obsoleteFileNums []bithash.FileNum
   133  	var delKeyTotal int
   134  	mgFnMap := make(map[bithash.FileNum]bool, 1<<8)
   135  	delFnMap := make(map[bithash.FileNum]bool, 1<<8)
   136  
   137  	compactFile := func(srcFn bithash.FileNum) (e error) {
   138  		var iter *bithash.TableIterator
   139  		var mgKeyNum, delKeyNum int
   140  
   141  		start := time.Now()
   142  
   143  		iter, e = t.bhash.NewTableIter(srcFn)
   144  		if e != nil {
   145  			return
   146  		}
   147  		defer func() {
   148  			if iter != nil {
   149  				e = iter.Close()
   150  			}
   151  
   152  			cost := time.Since(start).Seconds()
   153  			if e == nil {
   154  				if delKeyNum > 0 {
   155  					delKeyTotal += delKeyNum
   156  				}
   157  				t.opts.Logger.Infof("%s compactFile %d to %d done mgKeyNum:%d delKeyNum:%d cost:%.4f",
   158  					logTag, srcFn, dstFn, mgKeyNum, delKeyNum, cost)
   159  			} else {
   160  				t.opts.Logger.Errorf("%s compactFile %d to %d fail err:%s", logTag, srcFn, dstFn, e)
   161  			}
   162  		}()
   163  
   164  		findKey := func(iterKey *base.InternalKey, khash uint32) bool {
   165  			v, vexist, vcloser := t.getInternal(iterKey.UserKey, khash)
   166  			if !vexist {
   167  				return false
   168  			}
   169  			defer vcloser()
   170  
   171  			dv := base.DecodeInternalValue(v)
   172  			return iterKey.SeqNum() == dv.SeqNum()
   173  		}
   174  
   175  		for ik, v, fn := iter.First(); iter.Valid(); ik, v, fn = iter.Next() {
   176  			if _, ok := delFnMap[fn]; !ok {
   177  				delFnMap[fn] = true
   178  			}
   179  
   180  			khash := hash.Crc32(ik.UserKey)
   181  
   182  			if !findKey(ik, khash) {
   183  				delKeyNum++
   184  				continue
   185  			}
   186  
   187  			if e = bw.AddIkey(ik, v, khash, fn); e != nil {
   188  				t.opts.Logger.Errorf("%s compactFile AddIkey fail ikey:%s keyFn:%d dstFn:%d err:%s", logTag, ik.String(), fn, dstFn, e)
   189  				return
   190  			}
   191  
   192  			if _, ok := mgFnMap[fn]; !ok {
   193  				mgFnMap[fn] = true
   194  			}
   195  			mgKeyNum++
   196  		}
   197  
   198  		for k := range delFnMap {
   199  			if _, ok := mgFnMap[k]; ok {
   200  				delete(delFnMap, k)
   201  			}
   202  		}
   203  
   204  		return
   205  	}
   206  
   207  	for _, compactFn := range fileNums {
   208  		if err = compactFile(compactFn); err != nil {
   209  			t.opts.Logger.Errorf("%s compactFile fail fn:%d err:%s", logTag, compactFn, err)
   210  			return err
   211  		}
   212  
   213  		obsoleteFileNums = append(obsoleteFileNums, compactFn)
   214  	}
   215  
   216  	if err = bw.Finish(); err != nil {
   217  		return err
   218  	}
   219  
   220  	for mgFn := range mgFnMap {
   221  		t.bhash.SetFileNumMap(dstFn, mgFn)
   222  	}
   223  	for delFn := range delFnMap {
   224  		t.bhash.DeleteFileNumMap(delFn)
   225  	}
   226  
   227  	if len(obsoleteFileNums) > 0 {
   228  		t.bhash.RemoveTableFiles(obsoleteFileNums)
   229  	}
   230  
   231  	delKeyTotalOld := int(t.bhash.StatsGetDelKeyTotal())
   232  	if delKeyTotal > 0 {
   233  		t.bhash.StatsSubKeyTotal(delKeyTotal)
   234  		if delKeyTotalOld >= delKeyTotal {
   235  			t.bhash.StatsSubDelKeyTotal(delKeyTotal)
   236  		} else {
   237  			t.bhash.StatsSetDelKeyTotal(0)
   238  		}
   239  	}
   240  
   241  	t.opts.Logger.Infof("%s compact %v to %d success delKey:%d delKeyTotalOld:%d delKeyTotalNew:%d",
   242  		logTag, obsoleteFileNums, dstFn, delKeyTotal, delKeyTotalOld, int(t.bhash.StatsGetDelKeyTotal()))
   243  
   244  	return nil
   245  }