Skip to content

Commit

Permalink
sqlite: set connection attributes on open
Browse files Browse the repository at this point in the history
The symptoms in containers#17859 indicate that setting the PRAGMAs in individual
EXECs outside of a transaction can lead to concurrency issues and
failures when the DB is locked.  Hence set all PRAGMAs when opening
the connection.  Move them into individual constants to improve
documentation and readability.

Further make transactions exclusive as containers#17859 also mentions an error
that the DB is locked during a transaction.

[NO NEW TESTS NEEDED] - existing tests cover the code.

Fixes: containers#17859
Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
  • Loading branch information
vrothberg committed Mar 21, 2023
1 parent d8265f0 commit 58d4b15
Showing 1 changed file with 22 additions and 15 deletions.
37 changes: 22 additions & 15 deletions libpod/sqlite_state.go
Expand Up @@ -29,6 +29,27 @@ type SQLiteState struct {
runtime *Runtime
}

const (
// Deal with timezone automatically.
sqliteOptionLocation = "_loc=auto"
// Set the journal mode (https://www.sqlite.org/pragma.html#pragma_journal_mode).
sqliteOptionJournal = "&_journal=WAL"
// Force WAL mode to fsync after each transaction (https://www.sqlite.org/pragma.html#pragma_synchronous).
sqliteOptionSynchronous = "&_sync=FULL"
// Allow foreign keys (https://www.sqlite.org/pragma.html#pragma_foreign_keys).
sqliteOptionForeignKeys = "&_foreign_keys=1"
// Make sure that transactions happen exclusively.
sqliteOptionTXLock = "&_txlock=exclusive"

// Assembled sqlite options used when opening the database.
sqliteOptions = "db.sql?" +
sqliteOptionLocation +
sqliteOptionJournal +
sqliteOptionSynchronous +
sqliteOptionForeignKeys +
sqliteOptionTXLock
)

// NewSqliteState creates a new SQLite-backed state database.
func NewSqliteState(runtime *Runtime) (_ State, defErr error) {
state := new(SQLiteState)
Expand All @@ -45,7 +66,7 @@ func NewSqliteState(runtime *Runtime) (_ State, defErr error) {
return nil, fmt.Errorf("creating root directory: %w", err)
}

conn, err := sql.Open("sqlite3", filepath.Join(basePath, "db.sql?_loc=auto"))
conn, err := sql.Open("sqlite3", filepath.Join(basePath, sqliteOptions))
if err != nil {
return nil, fmt.Errorf("initializing sqlite database: %w", err)
}
Expand All @@ -63,20 +84,6 @@ func NewSqliteState(runtime *Runtime) (_ State, defErr error) {
return nil, fmt.Errorf("cannot connect to database: %w", err)
}

// Enable foreign keys constraints, which we use extensively in our
// tables.
if _, err := state.conn.Exec("PRAGMA foreign_keys = ON;"); err != nil {
return nil, fmt.Errorf("enabling foreign key support in database: %w", err)
}
// Enable WAL mode for performance - https://www.sqlite.org/wal.html
if _, err := state.conn.Exec("PRAGMA journal_mode = WAL;"); err != nil {
return nil, fmt.Errorf("switching journal to WAL mode: %w", err)
}
// Force WAL mode to fsync after every transaction, for reboot safety.
if _, err := state.conn.Exec("PRAGMA synchronous = FULL;"); err != nil {
return nil, fmt.Errorf("setting full fsync mode in db: %w", err)
}

// Set up tables
if err := sqliteInitTables(state.conn); err != nil {
return nil, fmt.Errorf("creating tables: %w", err)
Expand Down

0 comments on commit 58d4b15

Please sign in to comment.