LCOV - code coverage report
Current view: top level - pebble/wal/wal - log_recycler.go (source / functions) Coverage Total Hit
Test: 2025-08-27 08:18Z 526e102e - meta test only.lcov Lines: 20.3 % 79 16
Test Date: 2025-08-27 08:20:04 Functions: - 0 0

            Line data    Source code
       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 wal
       6              : 
       7              : import (
       8              :         "sync"
       9              : 
      10              :         "github.com/cockroachdb/errors"
      11              :         "github.com/cockroachdb/pebble/internal/base"
      12              : )
      13              : 
      14              : // TODO(sumeer): hide LogRecycler once rest of Pebble is using wal.Manager.
      15              : 
      16              : // LogRecycler recycles WAL log files. It holds a set of log file numbers that
      17              : // are available for reuse. Writing to a recycled log file is faster than to a
      18              : // new log file on some common filesystems (xfs, and ext3/4) due to avoiding
      19              : // metadata updates.
      20              : type LogRecycler struct {
      21              :         // The maximum number of log files to maintain for recycling.
      22              :         limit int
      23              : 
      24              :         // The minimum log number that is allowed to be recycled. Log numbers smaller
      25              :         // than this will be subject to immediate deletion. This is used to prevent
      26              :         // recycling a log written by a previous instance of the DB which may not
      27              :         // have had log recycling enabled. If that previous instance of the DB was
      28              :         // RocksDB, the old non-recyclable log record headers will be present.
      29              :         minRecycleLogNum base.DiskFileNum
      30              : 
      31              :         mu struct {
      32              :                 sync.Mutex
      33              :                 logs      []base.FileInfo
      34              :                 maxLogNum base.DiskFileNum
      35              :         }
      36              : }
      37              : 
      38              : // Init initialized the LogRecycler.
      39            1 : func (r *LogRecycler) Init(maxNumLogFiles int) {
      40            1 :         r.limit = maxNumLogFiles
      41            1 : }
      42              : 
      43              : // MinRecycleLogNum returns the current minimum log number that is allowed to
      44              : // be recycled.
      45            1 : func (r *LogRecycler) MinRecycleLogNum() NumWAL {
      46            1 :         return NumWAL(r.minRecycleLogNum)
      47            1 : }
      48              : 
      49              : // SetMinRecycleLogNum sets the minimum log number that is allowed to be
      50              : // recycled.
      51            1 : func (r *LogRecycler) SetMinRecycleLogNum(n NumWAL) {
      52            1 :         r.minRecycleLogNum = base.DiskFileNum(n)
      53            1 : }
      54              : 
      55              : // Add attempts to recycle the log file specified by logInfo. Returns true if
      56              : // the log file should not be deleted (i.e. the log is being recycled), and
      57              : // false otherwise.
      58            0 : func (r *LogRecycler) Add(logInfo base.FileInfo) bool {
      59            0 :         if logInfo.FileNum < r.minRecycleLogNum {
      60            0 :                 return false
      61            0 :         }
      62              : 
      63            0 :         r.mu.Lock()
      64            0 :         defer r.mu.Unlock()
      65            0 : 
      66            0 :         if logInfo.FileNum <= r.mu.maxLogNum {
      67            0 :                 // The log file number was already considered for recycling. Don't consider
      68            0 :                 // it again. This avoids a race between adding the same log file for
      69            0 :                 // recycling multiple times, and removing the log file for actual
      70            0 :                 // reuse. Note that we return true because the log was already considered
      71            0 :                 // for recycling and either it was deleted on the previous attempt (which
      72            0 :                 // means we shouldn't get here) or it was recycled and thus the file
      73            0 :                 // shouldn't be deleted.
      74            0 :                 return true
      75            0 :         }
      76            0 :         r.mu.maxLogNum = logInfo.FileNum
      77            0 :         if len(r.mu.logs) >= r.limit {
      78            0 :                 return false
      79            0 :         }
      80            0 :         r.mu.logs = append(r.mu.logs, logInfo)
      81            0 :         return true
      82              : }
      83              : 
      84              : // Peek returns the log at the head of the recycling queue, or the zero value
      85              : // fileInfo and false if the queue is empty.
      86            1 : func (r *LogRecycler) Peek() (base.FileInfo, bool) {
      87            1 :         r.mu.Lock()
      88            1 :         defer r.mu.Unlock()
      89            1 : 
      90            1 :         if len(r.mu.logs) == 0 {
      91            1 :                 return base.FileInfo{}, false
      92            1 :         }
      93            0 :         return r.mu.logs[0], true
      94              : }
      95              : 
      96              : // Stats return current stats.
      97            0 : func (r *LogRecycler) Stats() (count int, size uint64) {
      98            0 :         r.mu.Lock()
      99            0 :         defer r.mu.Unlock()
     100            0 :         count = len(r.mu.logs)
     101            0 :         for i := 0; i < count; i++ {
     102            0 :                 size += r.mu.logs[i].FileSize
     103            0 :         }
     104            0 :         return count, size
     105              : }
     106              : 
     107              : // Pop removes the log number at the head of the recycling queue, enforcing
     108              : // that it matches the specified logNum. An error is returned of the recycling
     109              : // queue is empty or the head log number does not match the specified one.
     110            0 : func (r *LogRecycler) Pop(logNum base.DiskFileNum) error {
     111            0 :         r.mu.Lock()
     112            0 :         defer r.mu.Unlock()
     113            0 : 
     114            0 :         if len(r.mu.logs) == 0 {
     115            0 :                 return errors.New("pebble: log recycler empty")
     116            0 :         }
     117            0 :         if r.mu.logs[0].FileNum != logNum {
     118            0 :                 return errors.Errorf("pebble: log recycler invalid %d vs %v", logNum, errors.Safe(fileInfoNums(r.mu.logs)))
     119            0 :         }
     120            0 :         r.mu.logs = r.mu.logs[1:]
     121            0 :         return nil
     122              : }
     123              : 
     124              : // LogNumsForTesting returns the current set of recyclable logs.
     125            0 : func (r *LogRecycler) LogNumsForTesting() []base.DiskFileNum {
     126            0 :         r.mu.Lock()
     127            0 :         defer r.mu.Unlock()
     128            0 :         return fileInfoNums(r.mu.logs)
     129            0 : }
     130              : 
     131            0 : func (r *LogRecycler) maxLogNumForTesting() base.DiskFileNum {
     132            0 :         r.mu.Lock()
     133            0 :         defer r.mu.Unlock()
     134            0 :         return r.mu.maxLogNum
     135            0 : }
     136              : 
     137            0 : func fileInfoNums(finfos []base.FileInfo) []base.DiskFileNum {
     138            0 :         if len(finfos) == 0 {
     139            0 :                 return nil
     140            0 :         }
     141            0 :         nums := make([]base.DiskFileNum, len(finfos))
     142            0 :         for i := range finfos {
     143            0 :                 nums[i] = finfos[i].FileNum
     144            0 :         }
     145            0 :         return nums
     146              : }
        

Generated by: LCOV version 2.0-1