Add checkpoint tests
This commit is contained in:
19
db.go
19
db.go
@@ -34,13 +34,12 @@ const (
|
|||||||
|
|
||||||
// DB represents a managed instance of a SQLite database in the file system.
|
// DB represents a managed instance of a SQLite database in the file system.
|
||||||
type DB struct {
|
type DB struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
path string // part to database
|
path string // part to database
|
||||||
db *sql.DB // target database
|
db *sql.DB // target database
|
||||||
rtx *sql.Tx // long running read transaction
|
rtx *sql.Tx // long running read transaction
|
||||||
pageSize int // page size, in bytes
|
pageSize int // page size, in bytes
|
||||||
notify chan struct{} // closes on WAL change
|
notify chan struct{} // closes on WAL change
|
||||||
lastCheckpointAt time.Time // last checkpoint time
|
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel func()
|
cancel func()
|
||||||
@@ -682,7 +681,7 @@ func (db *DB) Sync() (err error) {
|
|||||||
checkpoint = true
|
checkpoint = true
|
||||||
} else if db.MaxCheckpointPageN > 0 && newWALSize >= calcWALSize(db.pageSize, db.MaxCheckpointPageN) {
|
} else if db.MaxCheckpointPageN > 0 && newWALSize >= calcWALSize(db.pageSize, db.MaxCheckpointPageN) {
|
||||||
checkpoint, checkpointMode = true, CheckpointModeRestart
|
checkpoint, checkpointMode = true, CheckpointModeRestart
|
||||||
} else if db.CheckpointInterval > 0 && !db.lastCheckpointAt.IsZero() && time.Since(db.lastCheckpointAt) > db.CheckpointInterval && newWALSize > calcWALSize(db.pageSize, 1) {
|
} else if db.CheckpointInterval > 0 && !info.dbModTime.IsZero() && time.Since(info.dbModTime) > db.CheckpointInterval && newWALSize > calcWALSize(db.pageSize, 1) {
|
||||||
checkpoint = true
|
checkpoint = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -750,6 +749,7 @@ func (db *DB) verify() (info syncInfo, err error) {
|
|||||||
if fi, err := os.Stat(db.Path()); err != nil {
|
if fi, err := os.Stat(db.Path()); err != nil {
|
||||||
return info, err
|
return info, err
|
||||||
} else {
|
} else {
|
||||||
|
info.dbModTime = fi.ModTime()
|
||||||
db.dbSizeGauge.Set(float64(fi.Size()))
|
db.dbSizeGauge.Set(float64(fi.Size()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -826,6 +826,7 @@ func (db *DB) verify() (info syncInfo, err error) {
|
|||||||
|
|
||||||
type syncInfo struct {
|
type syncInfo struct {
|
||||||
generation string // generation name
|
generation string // generation name
|
||||||
|
dbModTime time.Time // last modified date of real DB file
|
||||||
walSize int64 // size of real WAL file
|
walSize int64 // size of real WAL file
|
||||||
walModTime time.Time // last modified date of real WAL file
|
walModTime time.Time // last modified date of real WAL file
|
||||||
shadowWALPath string // name of last shadow WAL file
|
shadowWALPath string // name of last shadow WAL file
|
||||||
@@ -1166,8 +1167,6 @@ func (db *DB) checkpoint(mode string) (err error) {
|
|||||||
return fmt.Errorf("release read lock: %w", err)
|
return fmt.Errorf("release read lock: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
db.lastCheckpointAt = time.Now()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
59
db_test.go
59
db_test.go
@@ -519,17 +519,64 @@ func TestDB_Sync(t *testing.T) {
|
|||||||
|
|
||||||
// Ensure DB checkpoints after minimum number of pages.
|
// Ensure DB checkpoints after minimum number of pages.
|
||||||
t.Run("MinCheckpointPageN", func(t *testing.T) {
|
t.Run("MinCheckpointPageN", func(t *testing.T) {
|
||||||
t.Skip()
|
db, sqldb := MustOpenDBs(t)
|
||||||
})
|
defer MustCloseDBs(t, db, sqldb)
|
||||||
|
|
||||||
// Ensure DB forces checkpoint after maximum number of pages.
|
// Execute a query to force a write to the WAL and then sync.
|
||||||
t.Run("MaxCheckpointPageN", func(t *testing.T) {
|
if _, err := sqldb.Exec(`CREATE TABLE foo (bar TEXT);`); err != nil {
|
||||||
t.Skip()
|
t.Fatal(err)
|
||||||
|
} else if err := db.Sync(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write at least minimum number of pages to trigger rollover.
|
||||||
|
for i := 0; i < db.MinCheckpointPageN; i++ {
|
||||||
|
if _, err := sqldb.Exec(`INSERT INTO foo (bar) VALUES ('baz');`); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync to shadow WAL.
|
||||||
|
if err := db.Sync(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure position is now on the second index.
|
||||||
|
if pos, err := db.Pos(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if got, want := pos.Index, 1; got != want {
|
||||||
|
t.Fatalf("Index=%v, want %v", got, want)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Ensure DB checkpoints after interval.
|
// Ensure DB checkpoints after interval.
|
||||||
t.Run("CheckpointInterval", func(t *testing.T) {
|
t.Run("CheckpointInterval", func(t *testing.T) {
|
||||||
t.Skip()
|
db, sqldb := MustOpenDBs(t)
|
||||||
|
defer MustCloseDBs(t, db, sqldb)
|
||||||
|
|
||||||
|
// Execute a query to force a write to the WAL and then sync.
|
||||||
|
if _, err := sqldb.Exec(`CREATE TABLE foo (bar TEXT);`); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := db.Sync(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce checkpoint interval to ensure a rollover is triggered.
|
||||||
|
db.CheckpointInterval = 1 * time.Nanosecond
|
||||||
|
|
||||||
|
// Write to WAL & sync.
|
||||||
|
if _, err := sqldb.Exec(`INSERT INTO foo (bar) VALUES ('baz');`); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if err := db.Sync(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure position is now on the second index.
|
||||||
|
if pos, err := db.Pos(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if got, want := pos.Index, 1; got != want {
|
||||||
|
t.Fatalf("Index=%v, want %v", got, want)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user