feat(sqlite): implement query planner (#1174)

* feat(sqlite): implement query planner

* implement Close on SQLite
This commit is contained in:
Kyle Sanderson 2023-11-01 10:07:16 -07:00 committed by GitHub
parent 5cf5d16050
commit 8c89481d88
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 0 deletions

View file

@ -83,6 +83,14 @@ func (db *DB) Open() error {
} }
func (db *DB) Close() error { func (db *DB) Close() error {
switch db.Driver {
case "sqlite":
if err := db.closingSQLite(); err != nil {
db.log.Fatal().Err(err).Msg("could not run sqlite shutdown tasks")
}
case "postgres":
}
// cancel background context // cancel background context
db.cancel() db.cancel()

View file

@ -37,6 +37,15 @@ func (db *DB) openSQLite() error {
return errors.Wrap(err, "enable wal") return errors.Wrap(err, "enable wal")
} }
// SQLite has a query planner that uses lifecycle stats to fund optimizations.
// This restricts the SQLite query planner optimizer to only run if sufficient
// information has been gathered over the lifecycle of the connection.
// The SQLite documentation is inconsistent in this regard,
// suggestions of 400 and 1000 are both "recommended", so lets use the lower bound.
if _, err = db.handler.Exec(`PRAGMA analysis_limit = 400;`); err != nil {
return errors.Wrap(err, "analysis_limit")
}
// When Autobrr does not cleanly shutdown, the WAL will still be present and not committed. // When Autobrr does not cleanly shutdown, the WAL will still be present and not committed.
// This is a no-op if the WAL is empty, and a commit when the WAL is not to start fresh. // This is a no-op if the WAL is empty, and a commit when the WAL is not to start fresh.
// When commits hit 1000, PRAGMA wal_checkpoint(PASSIVE); is invoked which tries its best // When commits hit 1000, PRAGMA wal_checkpoint(PASSIVE); is invoked which tries its best
@ -63,6 +72,21 @@ func (db *DB) openSQLite() error {
return nil return nil
} }
func (db *DB) closingSQLite() error {
if db.handler == nil {
return nil
}
// SQLite has a query planner that uses lifecycle stats to fund optimizations.
// Based on the limit defined at connection time, run optimize to
// help tweak the performance of the database on the next run.
if _, err := db.handler.Exec(`PRAGMA optimize;`); err != nil {
return errors.Wrap(err, "query planner optimization")
}
return nil
}
func (db *DB) migrateSQLite() error { func (db *DB) migrateSQLite() error {
db.lock.Lock() db.lock.Lock()
defer db.lock.Unlock() defer db.lock.Unlock()