github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/log_recycler_test.go (about)

     1  // Copyright 2019 The LevelDB-Go and Pebble and Bitalostored 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 bitalostable
     6  
     7  import (
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/require"
    11  	"github.com/zuoyebang/bitalostable/vfs"
    12  )
    13  
    14  func (r *logRecycler) logNums() []FileNum {
    15  	r.mu.Lock()
    16  	defer r.mu.Unlock()
    17  	return fileInfoNums(r.mu.logs)
    18  }
    19  
    20  func (r *logRecycler) maxLogNum() FileNum {
    21  	r.mu.Lock()
    22  	defer r.mu.Unlock()
    23  	return r.mu.maxLogNum
    24  }
    25  
    26  func TestLogRecycler(t *testing.T) {
    27  	r := logRecycler{limit: 3, minRecycleLogNum: 4}
    28  
    29  	// Logs below the min-recycle number are not recycled.
    30  	require.False(t, r.add(fileInfo{1, 0}))
    31  	require.False(t, r.add(fileInfo{2, 0}))
    32  	require.False(t, r.add(fileInfo{3, 0}))
    33  
    34  	// Logs are recycled up to the limit.
    35  	require.True(t, r.add(fileInfo{4, 0}))
    36  	require.EqualValues(t, []FileNum{4}, r.logNums())
    37  	require.EqualValues(t, 4, r.maxLogNum())
    38  	fi, ok := r.peek()
    39  	require.True(t, ok)
    40  	require.EqualValues(t, 4, fi.fileNum)
    41  	require.True(t, r.add(fileInfo{5, 0}))
    42  	require.EqualValues(t, []FileNum{4, 5}, r.logNums())
    43  	require.EqualValues(t, 5, r.maxLogNum())
    44  	require.True(t, r.add(fileInfo{6, 0}))
    45  	require.EqualValues(t, []FileNum{4, 5, 6}, r.logNums())
    46  	require.EqualValues(t, 6, r.maxLogNum())
    47  
    48  	// Trying to add a file past the limit fails.
    49  	require.False(t, r.add(fileInfo{7, 0}))
    50  	require.EqualValues(t, []FileNum{4, 5, 6}, r.logNums())
    51  	require.EqualValues(t, 7, r.maxLogNum())
    52  
    53  	// Trying to add a previously recycled file returns success, but the internal
    54  	// state is unchanged.
    55  	require.True(t, r.add(fileInfo{4, 0}))
    56  	require.EqualValues(t, []FileNum{4, 5, 6}, r.logNums())
    57  	require.EqualValues(t, 7, r.maxLogNum())
    58  
    59  	// An error is returned if we try to pop an element other than the first.
    60  	require.Regexp(t, `invalid 5 vs \[4 5 6\]`, r.pop(5))
    61  
    62  	require.NoError(t, r.pop(4))
    63  	require.EqualValues(t, []FileNum{5, 6}, r.logNums())
    64  
    65  	// Log number 7 was already considered, so it won't be recycled.
    66  	require.True(t, r.add(fileInfo{7, 0}))
    67  	require.EqualValues(t, []FileNum{5, 6}, r.logNums())
    68  
    69  	require.True(t, r.add(fileInfo{8, 0}))
    70  	require.EqualValues(t, []FileNum{5, 6, 8}, r.logNums())
    71  	require.EqualValues(t, 8, r.maxLogNum())
    72  
    73  	require.NoError(t, r.pop(5))
    74  	require.EqualValues(t, []FileNum{6, 8}, r.logNums())
    75  	require.NoError(t, r.pop(6))
    76  	require.EqualValues(t, []FileNum{8}, r.logNums())
    77  	require.NoError(t, r.pop(8))
    78  	require.EqualValues(t, []FileNum(nil), r.logNums())
    79  
    80  	require.Regexp(t, `empty`, r.pop(9))
    81  }
    82  
    83  func TestRecycleLogs(t *testing.T) {
    84  	mem := vfs.NewMem()
    85  	d, err := Open("", &Options{
    86  		FS: mem,
    87  	})
    88  	require.NoError(t, err)
    89  
    90  	logNum := func() FileNum {
    91  		d.mu.Lock()
    92  		defer d.mu.Unlock()
    93  		return d.mu.log.queue[len(d.mu.log.queue)-1].fileNum
    94  	}
    95  	logCount := func() int {
    96  		d.mu.Lock()
    97  		defer d.mu.Unlock()
    98  		return len(d.mu.log.queue)
    99  	}
   100  
   101  	// Flush the memtable a few times, forcing rotation of the WAL. We should see
   102  	// the recycled logs change as expected.
   103  	require.EqualValues(t, []FileNum(nil), d.logRecycler.logNums())
   104  	curLog := logNum()
   105  
   106  	require.NoError(t, d.Flush())
   107  
   108  	require.EqualValues(t, []FileNum{curLog}, d.logRecycler.logNums())
   109  	curLog = logNum()
   110  
   111  	require.NoError(t, d.Flush())
   112  
   113  	require.EqualValues(t, []FileNum{curLog}, d.logRecycler.logNums())
   114  
   115  	require.NoError(t, d.Close())
   116  
   117  	d, err = Open("", &Options{
   118  		FS: mem,
   119  	})
   120  	require.NoError(t, err)
   121  	metrics := d.Metrics()
   122  	if n := logCount(); n != int(metrics.WAL.Files) {
   123  		t.Fatalf("expected %d WAL files, but found %d", n, metrics.WAL.Files)
   124  	}
   125  	if n, sz := d.logRecycler.stats(); n != int(metrics.WAL.ObsoleteFiles) {
   126  		t.Fatalf("expected %d obsolete WAL files, but found %d", n, metrics.WAL.ObsoleteFiles)
   127  	} else if sz != metrics.WAL.ObsoletePhysicalSize {
   128  		t.Fatalf("expected %d obsolete physical WAL size, but found %d", sz, metrics.WAL.ObsoletePhysicalSize)
   129  	}
   130  	if recycled := d.logRecycler.logNums(); len(recycled) != 0 {
   131  		t.Fatalf("expected no recycled WAL files after recovery, but found %d", recycled)
   132  	}
   133  	require.NoError(t, d.Close())
   134  }