github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/log_recycler.go (about) 1 // Copyright 2019 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package pebble 6 7 import ( 8 "sync" 9 10 "github.com/cockroachdb/errors" 11 ) 12 13 type logRecycler struct { 14 // The maximum number of log files to maintain for recycling. 15 limit int 16 17 // The minimum log number that is allowed to be recycled. Log numbers smaller 18 // than this will be subject to immediate deletion. This is used to prevent 19 // recycling a log written by a previous instance of the DB which may not 20 // have had log recycling enabled. If that previous instance of the DB was 21 // RocksDB, the old non-recyclable log record headers will be present. 22 minRecycleLogNum FileNum 23 24 mu struct { 25 sync.Mutex 26 logs []fileInfo 27 maxLogNum FileNum 28 } 29 } 30 31 // add attempts to recycle the log file specified by logInfo. Returns true if 32 // the log file should not be deleted (i.e. the log is being recycled), and 33 // false otherwise. 34 func (r *logRecycler) add(logInfo fileInfo) bool { 35 if logInfo.fileNum.FileNum() < r.minRecycleLogNum { 36 return false 37 } 38 39 r.mu.Lock() 40 defer r.mu.Unlock() 41 42 if logInfo.fileNum.FileNum() <= r.mu.maxLogNum { 43 // The log file number was already considered for recycling. Don't consider 44 // it again. This avoids a race between adding the same log file for 45 // recycling multiple times, and removing the log file for actual 46 // reuse. Note that we return true because the log was already considered 47 // for recycling and either it was deleted on the previous attempt (which 48 // means we shouldn't get here) or it was recycled and thus the file 49 // shouldn't be deleted. 50 return true 51 } 52 r.mu.maxLogNum = logInfo.fileNum.FileNum() 53 if len(r.mu.logs) >= r.limit { 54 return false 55 } 56 r.mu.logs = append(r.mu.logs, logInfo) 57 return true 58 } 59 60 // peek returns the log at the head of the recycling queue, or the zero value 61 // fileInfo and false if the queue is empty. 62 func (r *logRecycler) peek() (fileInfo, bool) { 63 r.mu.Lock() 64 defer r.mu.Unlock() 65 66 if len(r.mu.logs) == 0 { 67 return fileInfo{}, false 68 } 69 return r.mu.logs[0], true 70 } 71 72 func (r *logRecycler) stats() (count int, size uint64) { 73 r.mu.Lock() 74 defer r.mu.Unlock() 75 count = len(r.mu.logs) 76 for i := 0; i < count; i++ { 77 size += r.mu.logs[i].fileSize 78 } 79 return count, size 80 } 81 82 // pop removes the log number at the head of the recycling queue, enforcing 83 // that it matches the specified logNum. An error is returned of the recycling 84 // queue is empty or the head log number does not match the specified one. 85 func (r *logRecycler) pop(logNum FileNum) error { 86 r.mu.Lock() 87 defer r.mu.Unlock() 88 89 if len(r.mu.logs) == 0 { 90 return errors.New("pebble: log recycler empty") 91 } 92 if r.mu.logs[0].fileNum.FileNum() != logNum { 93 return errors.Errorf("pebble: log recycler invalid %d vs %d", errors.Safe(logNum), errors.Safe(fileInfoNums(r.mu.logs))) 94 } 95 r.mu.logs = r.mu.logs[1:] 96 return nil 97 } 98 99 func fileInfoNums(finfos []fileInfo) []FileNum { 100 if len(finfos) == 0 { 101 return nil 102 } 103 nums := make([]FileNum, len(finfos)) 104 for i := range finfos { 105 nums[i] = finfos[i].fileNum.FileNum() 106 } 107 return nums 108 }