fix(vfs): fail fast when write buffer initialization fails (#974)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Cory LaNou
2026-01-08 13:50:44 -06:00
committed by GitHub
parent 9be0eb116d
commit 7a38f8b5d9
2 changed files with 54 additions and 13 deletions

15
vfs.go
View File

@@ -591,7 +591,7 @@ func (f *VFSFile) Open() error {
// Initialize write buffer file for durability (discards any existing buffer)
if err := f.initWriteBuffer(); err != nil {
f.logger.Warn("failed to initialize write buffer", "error", err)
return fmt.Errorf("initialize write buffer: %w", err)
}
}
@@ -1256,11 +1256,8 @@ func (f *VFSFile) createLTXFromDirty() io.Reader {
// initWriteBuffer initializes the write buffer file for durability.
// Any existing buffer content is discarded since unsync'd changes are lost on restart.
// This function is only called when writeEnabled is true, which guarantees bufferPath is set.
func (f *VFSFile) initWriteBuffer() error {
if f.bufferPath == "" {
return nil // No buffer configured
}
// Ensure parent directory exists
if err := os.MkdirAll(filepath.Dir(f.bufferPath), 0755); err != nil {
return fmt.Errorf("create buffer directory: %w", err)
@@ -1282,10 +1279,6 @@ func (f *VFSFile) initWriteBuffer() error {
// Otherwise, it appends to the end of the file.
// Must be called with f.mu held.
func (f *VFSFile) writeToBuffer(pgno uint32, data []byte) error {
if f.bufferFile == nil {
return nil // No buffer configured
}
var writeOffset int64
if existingOff, ok := f.dirty[pgno]; ok {
// Page already exists - overwrite at same offset
@@ -1309,10 +1302,6 @@ func (f *VFSFile) writeToBuffer(pgno uint32, data []byte) error {
// clearWriteBuffer clears and resets the write buffer after successful sync.
func (f *VFSFile) clearWriteBuffer() error {
if f.bufferFile == nil {
return nil
}
// Truncate file to zero
if err := f.bufferFile.Truncate(0); err != nil {
return fmt.Errorf("truncate buffer: %w", err)

View File

@@ -229,11 +229,15 @@ func TestVFSFile_WriteEnabled(t *testing.T) {
createTestLTXFile(t, client, 1, pageSize, 1, map[uint32][]byte{1: initialPage})
// Create VFSFile directly with write enabled
tmpDir := t.TempDir()
bufferPath := tmpDir + "/write-buffer"
logger := slog.Default()
f := NewVFSFile(client, "test.db", logger)
f.writeEnabled = true
f.dirty = make(map[uint32]int64)
f.syncInterval = 0
f.bufferPath = bufferPath
if err := f.Open(); err != nil {
t.Fatal(err)
@@ -639,6 +643,54 @@ func TestVFSFile_WriteBufferClearAfterSync(t *testing.T) {
}
}
func TestVFSFile_OpenFailsWithInvalidBufferPath(t *testing.T) {
client := newWriteTestReplicaClient()
pageSize := uint32(4096)
initialPage := make([]byte, pageSize)
createTestLTXFile(t, client, 1, pageSize, 1, map[uint32][]byte{1: initialPage})
logger := slog.Default()
f := NewVFSFile(client, "test.db", logger)
f.writeEnabled = true
f.dirty = make(map[uint32]int64)
f.syncInterval = 0
f.bufferPath = "/nonexistent/path/that/cannot/be/created/buffer"
err := f.Open()
if err == nil {
f.Close()
t.Fatal("expected Open to fail with invalid buffer path")
}
}
func TestVFSFile_BufferFileAlwaysCreatedWhenWriteEnabled(t *testing.T) {
client := newWriteTestReplicaClient()
pageSize := uint32(4096)
initialPage := make([]byte, pageSize)
createTestLTXFile(t, client, 1, pageSize, 1, map[uint32][]byte{1: initialPage})
tmpDir := t.TempDir()
bufferPath := tmpDir + "/write-buffer"
logger := slog.Default()
f := NewVFSFile(client, "test.db", logger)
f.writeEnabled = true
f.dirty = make(map[uint32]int64)
f.syncInterval = 0
f.bufferPath = bufferPath
if err := f.Open(); err != nil {
t.Fatal(err)
}
defer f.Close()
if f.bufferFile == nil {
t.Fatal("bufferFile should never be nil when writeEnabled is true")
}
}
func TestVFSFile_OpenNewDatabase(t *testing.T) {
// Test opening a VFSFile with write mode enabled when no LTX files exist (new database)
client := newWriteTestReplicaClient()