From e0493f979a8269a53b83b35939d0820f0a3a4fc1 Mon Sep 17 00:00:00 2001 From: Toni Spets Date: Tue, 8 Aug 2023 20:40:43 +0300 Subject: [PATCH] Copy WAL frames through temp file to shadow (#474) --- db.go | 51 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/db.go b/db.go index 9a443a4d..c5c4de75 100644 --- a/db.go +++ b/db.go @@ -1024,6 +1024,16 @@ func (db *DB) copyToShadowWAL(filename string) (newSize int64, err error) { return 0, fmt.Errorf("last checksum: %w", err) } + // Write to a temporary shadow file. + tempFilename := filename + ".tmp" + defer os.Remove(tempFilename) + + f, err := internal.CreateFile(tempFilename, db.fileInfo) + if err != nil { + return 0, fmt.Errorf("create temp file: %w", err) + } + defer f.Close() + // Seek to correct position on real wal. if _, err := r.Seek(origSize, io.SeekStart); err != nil { return 0, fmt.Errorf("real wal seek: %w", err) @@ -1034,7 +1044,6 @@ func (db *DB) copyToShadowWAL(filename string) (newSize int64, err error) { // Read through WAL from last position to find the page of the last // committed transaction. frame := make([]byte, db.pageSize+WALFrameHeaderSize) - var buf bytes.Buffer offset := origSize lastCommitSize := origSize for { @@ -1064,24 +1073,46 @@ func (db *DB) copyToShadowWAL(filename string) (newSize int64, err error) { break } - // Add page to the new size of the shadow WAL. - buf.Write(frame) + // Write page to temporary WAL file. + if _, err := f.Write(frame); err != nil { + return 0, fmt.Errorf("write temp shadow wal: %w", err) + } Tracef("%s: copy-shadow: ok %s offset=%d salt=%x %x", db.path, filename, offset, salt0, salt1) offset += int64(len(frame)) - // Flush to shadow WAL if commit record. + // Update new size if written frame was a commit record. newDBSize := binary.BigEndian.Uint32(frame[4:]) if newDBSize != 0 { - if _, err := buf.WriteTo(w); err != nil { - return 0, fmt.Errorf("write shadow wal: %w", err) - } - buf.Reset() lastCommitSize = offset } } - // Sync & close. + // If no WAL writes found, exit. + if origSize == lastCommitSize { + return origSize, nil + } + + walByteN := lastCommitSize - origSize + + // Move to beginning of temporary file. + if _, err := f.Seek(0, io.SeekStart); err != nil { + return 0, fmt.Errorf("temp file seek: %w", err) + } + + // Copy from temporary file to shadow WAL. + if _, err := io.Copy(w, &io.LimitedReader{R: f, N: walByteN}); err != nil { + return 0, fmt.Errorf("write shadow file: %w", err) + } + + // Close & remove temporary file. + if err := f.Close(); err != nil { + return 0, err + } else if err := os.Remove(tempFilename); err != nil { + return 0, err + } + + // Sync & close shadow WAL. if err := w.Sync(); err != nil { return 0, err } else if err := w.Close(); err != nil { @@ -1089,7 +1120,7 @@ func (db *DB) copyToShadowWAL(filename string) (newSize int64, err error) { } // Track total number of bytes written to WAL. - db.totalWALBytesCounter.Add(float64(lastCommitSize - origSize)) + db.totalWALBytesCounter.Add(float64(walByteN)) return lastCommitSize, nil }