github.com/cockroachdb/pebble@v1.1.1-0.20240513155919-3622ade60459/filenames_test.go (about) 1 // Copyright 2020 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/errors" 11 "github.com/cockroachdb/pebble/internal/base" 12 "github.com/cockroachdb/pebble/vfs" 13 "github.com/stretchr/testify/require" 14 ) 15 16 // TestSetCurrentFileCrash tests a crash that occurs during 17 // a MANIFEST roll, leaving the temporary CURRENT file on 18 // the filesystem. These temporary files should be cleaned 19 // up on Open. 20 func TestSetCurrentFileCrash(t *testing.T) { 21 mem := vfs.NewMem() 22 23 // Initialize a fresh database to write the initial MANIFEST. 24 { 25 d, err := Open("", &Options{FS: mem}) 26 require.NoError(t, err) 27 require.NoError(t, d.Close()) 28 } 29 30 // Open the database again, this time with a FS that 31 // errors on Rename and a tiny max manifest file size 32 // to force manifest rolls. 33 { 34 wantErr := errors.New("rename error") 35 _, err := Open("", &Options{ 36 FS: renameErrorFS{FS: mem, err: wantErr}, 37 Logger: noFatalLogger{t: t}, 38 MaxManifestFileSize: 1, 39 L0CompactionThreshold: 10, 40 }) 41 // Open should fail during a manifest roll, 42 // leaving a temp dir on the filesystem. 43 if !errors.Is(err, wantErr) { 44 t.Fatal(err) 45 } 46 } 47 48 // A temp file should be left on the filesystem 49 // from the failed Rename of the CURRENT file. 50 if temps := allTempFiles(t, mem); len(temps) == 0 { 51 t.Fatal("no temp files on the filesystem") 52 } 53 54 // Open the database a third time with a normal 55 // filesystem again. It should clean up any temp 56 // files on Open. 57 { 58 d, err := Open("", &Options{ 59 FS: mem, 60 MaxManifestFileSize: 1, 61 L0CompactionThreshold: 10, 62 }) 63 require.NoError(t, err) 64 require.NoError(t, d.Close()) 65 if temps := allTempFiles(t, mem); len(temps) > 0 { 66 t.Fatalf("temporary files still on disk: %#v\n", temps) 67 } 68 } 69 } 70 71 func allTempFiles(t *testing.T, fs vfs.FS) []string { 72 var files []string 73 ls, err := fs.List("") 74 require.NoError(t, err) 75 for _, f := range ls { 76 ft, _, ok := base.ParseFilename(fs, f) 77 if ok && ft == fileTypeTemp { 78 files = append(files, f) 79 } 80 } 81 return files 82 } 83 84 type renameErrorFS struct { 85 vfs.FS 86 err error 87 } 88 89 func (fs renameErrorFS) Rename(oldname string, newname string) error { 90 return fs.err 91 } 92 93 // noFatalLogger implements Logger, logging to the contained 94 // *testing.T. Notably it does not panic on calls to Fatalf 95 // to enable unit tests of fatal logic. 96 type noFatalLogger struct { 97 t *testing.T 98 } 99 100 func (l noFatalLogger) Infof(format string, args ...interface{}) { 101 l.t.Logf(format, args...) 102 } 103 104 func (l noFatalLogger) Fatalf(format string, args ...interface{}) { 105 l.t.Logf(format, args...) 106 }