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 }