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