diff --git a/internal/manager/task/migrate.go b/internal/manager/task/migrate.go index 37062329e..95798d301 100644 --- a/internal/manager/task/migrate.go +++ b/internal/manager/task/migrate.go @@ -42,8 +42,8 @@ func (s *MigrateJob) Execute(ctx context.Context, progress *job.Progress) error logger.Infof("Migrating database from %d to %d", schemaInfo.CurrentSchemaVersion, schemaInfo.RequiredSchemaVersion) - // set the number of tasks = required steps + optimise - progress.SetTotal(int(schemaInfo.StepsRequired + 1)) + // set the number of tasks = backup + required steps + optimise + progress.SetTotal(int(schemaInfo.StepsRequired + 2)) database := s.Database @@ -61,12 +61,20 @@ func (s *MigrateJob) Execute(ctx context.Context, progress *job.Progress) error } } - // perform database backup - if err := database.Backup(backupPath); err != nil { + progress.ExecuteTask("Backing up database", func() { + defer progress.Increment() + + // perform database backup + err = database.Backup(backupPath) + }) + + if err != nil { return fmt.Errorf("error backing up database: %s", err) } - if err := s.runMigrations(ctx, progress); err != nil { + err = s.runMigrations(ctx, progress) + + if err != nil { errStr := fmt.Sprintf("error performing migration: %s", err) // roll back to the backed up version @@ -87,6 +95,11 @@ func (s *MigrateJob) Execute(ctx context.Context, progress *job.Progress) error } } + // reinitialise the database + if err := database.ReInitialise(); err != nil { + return fmt.Errorf("error reinitialising database: %s", err) + } + logger.Infof("Database migration complete") return nil @@ -124,6 +137,8 @@ func (s *MigrateJob) runMigrations(ctx context.Context, progress *job.Progress) defer m.Close() + logger.Info("Running migrations") + for { currentSchemaVersion := m.CurrentSchemaVersion() targetSchemaVersion := m.RequiredSchemaVersion() @@ -144,21 +159,15 @@ func (s *MigrateJob) runMigrations(ctx context.Context, progress *job.Progress) progress.Increment() } - // reinitialise the database - if err := database.ReInitialise(); err != nil { - return fmt.Errorf("error reinitialising database: %s", err) - } - - // optimise the database + // perform post-migrate analyze using the migrator connection progress.ExecuteTask("Optimising database", func() { - err = database.Optimise(ctx) + err = m.PostMigrate(ctx) + progress.Increment() }) if err != nil { return fmt.Errorf("error optimising database: %s", err) } - progress.Increment() - return nil } diff --git a/pkg/sqlite/database.go b/pkg/sqlite/database.go index 4b7c23552..fce8190d8 100644 --- a/pkg/sqlite/database.go +++ b/pkg/sqlite/database.go @@ -430,7 +430,19 @@ func (db *Database) Vacuum(ctx context.Context) error { // Analyze runs an ANALYZE on the database to improve query performance. func (db *Database) Analyze(ctx context.Context) error { - _, err := db.writeDB.ExecContext(ctx, "ANALYZE") + return analyze(ctx, db.writeDB) +} + +// analyze runs an ANALYZE on the database to improve query performance. +func analyze(ctx context.Context, db *sqlx.DB) error { + _, err := db.ExecContext(ctx, "ANALYZE") + return err +} + +// flushWAL flushes the Write-Ahead Log (WAL) to the main database file. +// It also truncates the WAL file to 0 bytes. +func flushWAL(ctx context.Context, db *sqlx.DB) error { + _, err := db.ExecContext(ctx, "PRAGMA wal_checkpoint(TRUNCATE)") return err } diff --git a/pkg/sqlite/migrate.go b/pkg/sqlite/migrate.go index ba4754458..d362bffa7 100644 --- a/pkg/sqlite/migrate.go +++ b/pkg/sqlite/migrate.go @@ -39,6 +39,12 @@ func NewMigrator(db *Database) (*Migrator, error) { m.conn.SetConnMaxIdleTime(dbConnTimeout) m.m, err = m.getMigrate() + + // if error encountered, close the connection + if err != nil { + m.Close() + } + return m, err } @@ -124,6 +130,27 @@ func (m *Migrator) runCustomMigration(ctx context.Context, fn customMigrationFun return nil } +func (m *Migrator) PostMigrate(ctx context.Context) error { + // optimise the database + var err error + logger.Info("Running database analyze") + + // don't use Optimize/vacuum as this adds a significant amount of time + // to the migration + err = analyze(ctx, m.conn) + + if err == nil { + logger.Debug("Flushing WAL") + err = flushWAL(ctx, m.conn) + } + + if err != nil { + return fmt.Errorf("error optimising database: %s", err) + } + + return nil +} + func (db *Database) getDatabaseSchemaVersion() (uint, error) { m, err := NewMigrator(db) if err != nil {