Worker: enable write-ahead logging on the database
Now the Worker and the Manager share the same database initialisation code (enabling foreign key constraints + write-ahead logging). The foreign key constraints were already enabled before, but now it's done with (a copy of) the same code as the Manager.
This commit is contained in:
parent
70c88a95e3
commit
d260a308bd
@ -71,34 +71,52 @@ func openDBWithConfig(dsn string, config *gorm.Config) (*DB, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db := &DB{
|
||||||
|
gormDB: gormDB,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the database connection if there was some error. This prevents
|
||||||
|
// leaking database connections & should remove any write-ahead-log files.
|
||||||
|
closeConnOnReturn := true
|
||||||
|
defer func() {
|
||||||
|
if !closeConnOnReturn {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := db.Close(); err != nil {
|
||||||
|
log.Debug().AnErr("cause", err).Msg("cannot close database connection")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Use the generic sql.DB interface to set some connection pool options.
|
// Use the generic sql.DB interface to set some connection pool options.
|
||||||
sqlDB, err := gormDB.DB()
|
sqlDB, err := gormDB.DB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only allow a single database connection, to avoid SQLITE_BUSY errors.
|
// Only allow a single database connection, to avoid SQLITE_BUSY errors.
|
||||||
// It's not certain that this'll improve the situation, but it's worth a try.
|
// It's not certain that this'll improve the situation, but it's worth a try.
|
||||||
sqlDB.SetMaxIdleConns(1) // Max num of connections in the idle connection pool.
|
sqlDB.SetMaxIdleConns(1) // Max num of connections in the idle connection pool.
|
||||||
sqlDB.SetMaxOpenConns(1) // Max num of open connections to the database.
|
sqlDB.SetMaxOpenConns(1) // Max num of open connections to the database.
|
||||||
|
|
||||||
// Enable foreign key checks.
|
// Always enable foreign key checks, to make SQLite behave like a real database.
|
||||||
log.Trace().Msg("enabling SQLite foreign key checks")
|
log.Trace().Msg("enabling SQLite foreign key checks")
|
||||||
if tx := gormDB.Exec("PRAGMA foreign_keys = 1"); tx.Error != nil {
|
if err := db.pragmaForeignKeys(true); err != nil {
|
||||||
return nil, fmt.Errorf("enabling foreign keys: %w", tx.Error)
|
return nil, err
|
||||||
}
|
|
||||||
var fkEnabled int
|
|
||||||
if tx := gormDB.Raw("PRAGMA foreign_keys").Scan(&fkEnabled); tx.Error != nil {
|
|
||||||
return nil, fmt.Errorf("checking whether the database has foreign key checks enabled: %w", tx.Error)
|
|
||||||
}
|
|
||||||
if fkEnabled == 0 {
|
|
||||||
log.Error().Msg("SQLite database does not want to enable foreign keys, this may cause data loss")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
db := DB{
|
// Write-ahead-log journal may improve writing speed.
|
||||||
gormDB: gormDB,
|
log.Trace().Msg("enabling SQLite write-ahead-log journal mode")
|
||||||
|
if tx := gormDB.Exec("PRAGMA journal_mode = WAL"); tx.Error != nil {
|
||||||
|
return nil, fmt.Errorf("enabling SQLite write-ahead-log journal mode: %w", tx.Error)
|
||||||
|
}
|
||||||
|
// Switching from 'full' (default) to 'normal' sync may improve writing speed.
|
||||||
|
log.Trace().Msg("enabling SQLite 'normal' synchronisation")
|
||||||
|
if tx := gormDB.Exec("PRAGMA synchronous = normal"); tx.Error != nil {
|
||||||
|
return nil, fmt.Errorf("enabling SQLite 'normal' sync mode: %w", tx.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &db, nil
|
closeConnOnReturn = false
|
||||||
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nowFunc returns 'now' in UTC, so that GORM-managed times (createdAt,
|
// nowFunc returns 'now' in UTC, so that GORM-managed times (createdAt,
|
||||||
@ -126,3 +144,46 @@ func (db *DB) Close() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *DB) pragmaForeignKeys(enabled bool) error {
|
||||||
|
var (
|
||||||
|
value int
|
||||||
|
noun string
|
||||||
|
)
|
||||||
|
switch enabled {
|
||||||
|
case false:
|
||||||
|
value = 0
|
||||||
|
noun = "disabl"
|
||||||
|
case true:
|
||||||
|
value = 1
|
||||||
|
noun = "enabl"
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Trace().Msgf("%sing SQLite foreign key checks", noun)
|
||||||
|
|
||||||
|
// SQLite doesn't seem to like SQL parameters for `PRAGMA`, so `PRAGMA foreign_keys = ?` doesn't work.
|
||||||
|
sql := fmt.Sprintf("PRAGMA foreign_keys = %d", value)
|
||||||
|
|
||||||
|
if tx := db.gormDB.Exec(sql); tx.Error != nil {
|
||||||
|
return fmt.Errorf("%sing foreign keys: %w", noun, tx.Error)
|
||||||
|
}
|
||||||
|
fkEnabled, err := db.areForeignKeysEnabled()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if fkEnabled != enabled {
|
||||||
|
return fmt.Errorf("SQLite database does not want to %se foreign keys, this may cause data loss", noun)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) areForeignKeysEnabled() (bool, error) {
|
||||||
|
log.Trace().Msg("checking whether SQLite foreign key checks are enabled")
|
||||||
|
|
||||||
|
var fkEnabled int
|
||||||
|
if tx := db.gormDB.Raw("PRAGMA foreign_keys").Scan(&fkEnabled); tx.Error != nil {
|
||||||
|
return false, fmt.Errorf("checking whether the database has foreign key checks are enabled: %w", tx.Error)
|
||||||
|
}
|
||||||
|
return fkEnabled != 0, nil
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user