LCOV - code coverage report
Current view: top level - pebble/wal - log_recycler.go (source / functions) Hit Total Coverage
Test: 2024-04-15 08:15Z c34894c4 - tests + meta.lcov Lines: 79 79 100.0 %
Date: 2024-04-15 08:16:52 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           2 : func (r *LogRecycler) Init(maxNumLogFiles int) {
      40           2 :         r.limit = maxNumLogFiles
      41           2 : }
      42             : 
      43             : // MinRecycleLogNum returns the current minimum log number that is allowed to
      44             : // be recycled.
      45           2 : func (r *LogRecycler) MinRecycleLogNum() NumWAL {
      46           2 :         return NumWAL(r.minRecycleLogNum)
      47           2 : }
      48             : 
      49             : // SetMinRecycleLogNum sets the minimum log number that is allowed to be
      50             : // recycled.
      51           2 : func (r *LogRecycler) SetMinRecycleLogNum(n NumWAL) {
      52           2 :         r.minRecycleLogNum = base.DiskFileNum(n)
      53           2 : }
      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           1 : func (r *LogRecycler) Add(logInfo base.FileInfo) bool {
      59           1 :         if logInfo.FileNum < r.minRecycleLogNum {
      60           1 :                 return false
      61           1 :         }
      62             : 
      63           1 :         r.mu.Lock()
      64           1 :         defer r.mu.Unlock()
      65           1 : 
      66           1 :         if logInfo.FileNum <= r.mu.maxLogNum {
      67           1 :                 // The log file number was already considered for recycling. Don't consider
      68           1 :                 // it again. This avoids a race between adding the same log file for
      69           1 :                 // recycling multiple times, and removing the log file for actual
      70           1 :                 // reuse. Note that we return true because the log was already considered
      71           1 :                 // for recycling and either it was deleted on the previous attempt (which
      72           1 :                 // means we shouldn't get here) or it was recycled and thus the file
      73           1 :                 // shouldn't be deleted.
      74           1 :                 return true
      75           1 :         }
      76           1 :         r.mu.maxLogNum = logInfo.FileNum
      77           1 :         if len(r.mu.logs) >= r.limit {
      78           1 :                 return false
      79           1 :         }
      80           1 :         r.mu.logs = append(r.mu.logs, logInfo)
      81           1 :         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           2 : func (r *LogRecycler) Peek() (base.FileInfo, bool) {
      87           2 :         r.mu.Lock()
      88           2 :         defer r.mu.Unlock()
      89           2 : 
      90           2 :         if len(r.mu.logs) == 0 {
      91           2 :                 return base.FileInfo{}, false
      92           2 :         }
      93           1 :         return r.mu.logs[0], true
      94             : }
      95             : 
      96             : // Stats return current stats.
      97           2 : func (r *LogRecycler) Stats() (count int, size uint64) {
      98           2 :         r.mu.Lock()
      99           2 :         defer r.mu.Unlock()
     100           2 :         count = len(r.mu.logs)
     101           2 :         for i := 0; i < count; i++ {
     102           1 :                 size += r.mu.logs[i].FileSize
     103           1 :         }
     104           2 :         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           1 : func (r *LogRecycler) Pop(logNum base.DiskFileNum) error {
     111           1 :         r.mu.Lock()
     112           1 :         defer r.mu.Unlock()
     113           1 : 
     114           1 :         if len(r.mu.logs) == 0 {
     115           1 :                 return errors.New("pebble: log recycler empty")
     116           1 :         }
     117           1 :         if r.mu.logs[0].FileNum != logNum {
     118           1 :                 return errors.Errorf("pebble: log recycler invalid %d vs %v", logNum, errors.Safe(fileInfoNums(r.mu.logs)))
     119           1 :         }
     120           1 :         r.mu.logs = r.mu.logs[1:]
     121           1 :         return nil
     122             : }
     123             : 
     124             : // LogNumsForTesting returns the current set of recyclable logs.
     125           1 : func (r *LogRecycler) LogNumsForTesting() []base.DiskFileNum {
     126           1 :         r.mu.Lock()
     127           1 :         defer r.mu.Unlock()
     128           1 :         return fileInfoNums(r.mu.logs)
     129           1 : }
     130             : 
     131           1 : func (r *LogRecycler) maxLogNumForTesting() base.DiskFileNum {
     132           1 :         r.mu.Lock()
     133           1 :         defer r.mu.Unlock()
     134           1 :         return r.mu.maxLogNum
     135           1 : }
     136             : 
     137           1 : func fileInfoNums(finfos []base.FileInfo) []base.DiskFileNum {
     138           1 :         if len(finfos) == 0 {
     139           1 :                 return nil
     140           1 :         }
     141           1 :         nums := make([]base.DiskFileNum, len(finfos))
     142           1 :         for i := range finfos {
     143           1 :                 nums[i] = finfos[i].FileNum
     144           1 :         }
     145           1 :         return nums
     146             : }

Generated by: LCOV version 1.14