From 3b6c988dd93adc7ad4a35232b9a13c9f312e68e6 Mon Sep 17 00:00:00 2001 From: Philip O'Toole Date: Sun, 10 Sep 2023 10:13:04 -0400 Subject: [PATCH] Do some syncing and closing --- db/db.go | 32 +++++++++++++++++++++++++++++++- db/db_common_test.go | 22 ++++++++++++++++++++++ snapshot/snapshot.go | 9 +++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/db/db.go b/db/db.go index d0a8e46f..bb2e3fe1 100644 --- a/db/db.go +++ b/db/db.go @@ -268,6 +268,9 @@ func ReplayWAL(path string, wals []string, deleteMode bool) error { if err != nil { return err } + if db.SetSynchronousMode("FULL"); err != nil { + return err + } if err := db.Checkpoint(); err != nil { return fmt.Errorf("checkpoint WAL %s: %s", wal, err.Error()) } @@ -288,7 +291,16 @@ func ReplayWAL(path string, wals []string, deleteMode bool) error { return err } } - return nil + + // Ensure the database file is sync'ed to disk. + fd, err := os.OpenFile(path, os.O_RDWR, 0666) + if err != nil { + return err + } + if err := fd.Sync(); err != nil { + return err + } + return fd.Close() } // Open opens a file-based database, creating it if it does not exist. After this @@ -526,6 +538,24 @@ func (db *DB) GetCheckpointing() (int, error) { return n, err } +// SetSynchronousMode sets the synchronous mode of the database. +func (db *DB) SetSynchronousMode(mode string) error { + if mode != "OFF" && mode != "NORMAL" && mode != "FULL" && mode != "EXTRA" { + return fmt.Errorf("invalid synchronous mode %s", mode) + } + if _, err := db.rwDB.Exec(fmt.Sprintf("PRAGMA synchronous=%s", mode)); err != nil { + return fmt.Errorf("failed to set synchronous mode to %s: %s", mode, err.Error()) + } + return nil +} + +// GetSynchronousMode returns the current synchronous mode. +func (db *DB) GetSynchronousMode() (int, error) { + var n int + err := db.rwDB.QueryRow("PRAGMA synchronous").Scan(&n) + return n, err +} + // FKEnabled returns whether Foreign Key constraints are enabled. func (db *DB) FKEnabled() bool { return db.fkEnabled diff --git a/db/db_common_test.go b/db/db_common_test.go index 03fdf985..15dca5f2 100644 --- a/db/db_common_test.go +++ b/db/db_common_test.go @@ -18,6 +18,27 @@ func testCompileOptions(t *testing.T, db *DB) { } } +func testSetSynchronousMode(t *testing.T, db *DB) { + modes := map[string]int{ + "OFF": 0, + "NORMAL": 1, + "FULL": 2, + "EXTRA": 3, + } + for m, i := range modes { + if db.SetSynchronousMode(m) != nil { + t.Fatalf("failed to set synchronous mode to %s", m) + } + mm, err := db.GetSynchronousMode() + if err != nil { + t.Fatalf("failed to get synchronous mode: %s", err.Error()) + } + if mm != i { + t.Fatalf("synchonous mode not set to %s", m) + } + } +} + func testTableNotExist(t *testing.T, db *DB) { q, err := db.QueryStringStmt("SELECT * FROM foo") if err != nil { @@ -1468,6 +1489,7 @@ func Test_DatabaseCommonOperations(t *testing.T) { name string testFunc func(*testing.T, *DB) }{ + {"SetSynchronousMode", testSetSynchronousMode}, {"CompileOptions", testCompileOptions}, {"TableNotExist", testTableNotExist}, {"TableCreation", testTableCreation}, diff --git a/snapshot/snapshot.go b/snapshot/snapshot.go index 54e0316d..6327370f 100644 --- a/snapshot/snapshot.go +++ b/snapshot/snapshot.go @@ -78,6 +78,12 @@ func ReplayDB(fullSnap *FullSnapshot, r io.Reader, path string) error { if _, err := io.CopyN(sqliteBaseFD, r, dbInfo.Size); err != nil { return fmt.Errorf("error writing SQLite file data: %v", err) } + if sqliteBaseFD.Sync() != nil { + return fmt.Errorf("error syncing SQLite file: %v", err) + } + if err := sqliteBaseFD.Close(); err != nil { + return fmt.Errorf("error closing SQLite file: %v", err) + } // Write out any WALs. var walFiles []string @@ -96,6 +102,9 @@ func ReplayDB(fullSnap *FullSnapshot, r io.Reader, path string) error { if _, err := io.CopyN(walFD, r, wal.Size); err != nil { return fmt.Errorf("error writing WAL file data: %v", err) } + if walFD.Sync() != nil { + return fmt.Errorf("error syncing WAL file: %v", err) + } walFiles = append(walFiles, walName) return nil }(); err != nil {