From 05b5c2b528c15ee625ab8083645c346fc77f478b Mon Sep 17 00:00:00 2001 From: sneak Date: Thu, 12 Feb 2026 13:40:58 -0800 Subject: [PATCH] fix SQLite database locking issues - Use tx.QueryRow instead of db.QueryRow within transaction for media table check - Switch to WAL mode instead of OFF for better concurrency handling - Add busy_timeout pragma to wait up to 5 seconds if database is locked - Clear transaction variable after commit to prevent defer rollback issues --- internal/bsdaily/extract.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/bsdaily/extract.go b/internal/bsdaily/extract.go index 6947f17..920f096 100644 --- a/internal/bsdaily/extract.go +++ b/internal/bsdaily/extract.go @@ -23,7 +23,8 @@ func ExtractDay(srcDBPath, dstDBPath string, targetDay time.Time) error { slog.Info("extracting day", "from", dayStart, "until", dayEnd) // Maximum performance pragmas - we don't care about crash safety for temp files - pragmas := fmt.Sprintf("?_pragma=journal_mode(OFF)&_pragma=synchronous(OFF)&_pragma=cache_size(%d)&_pragma=foreign_keys(OFF)&_pragma=locking_mode(EXCLUSIVE)&_pragma=temp_store(MEMORY)", sqliteCacheSizeKB) + // Use WAL mode for the source attachment to avoid locking issues + pragmas := fmt.Sprintf("?_pragma=journal_mode(WAL)&_pragma=synchronous(OFF)&_pragma=cache_size(%d)&_pragma=foreign_keys(OFF)&_pragma=temp_store(MEMORY)&_pragma=busy_timeout(5000)", sqliteCacheSizeKB) db, err := sql.Open("sqlite", dstDBPath+pragmas) if err != nil { return fmt.Errorf("opening destination database: %w", err) @@ -109,7 +110,7 @@ func ExtractDay(srcDBPath, dstDBPath string, targetDay time.Time) error { // Check if media table exists in source and copy if present var mediaTableExists int - if err := db.QueryRow("SELECT COUNT(*) FROM src.sqlite_master WHERE type='table' AND name='media'").Scan(&mediaTableExists); err != nil { + if err := tx.QueryRow("SELECT COUNT(*) FROM src.sqlite_master WHERE type='table' AND name='media'").Scan(&mediaTableExists); err != nil { slog.Warn("checking for media table", "error", err) } else if mediaTableExists > 0 { slog.Info("inserting media entries") @@ -119,10 +120,11 @@ func ExtractDay(srcDBPath, dstDBPath string, targetDay time.Time) error { } } - // Commit the transaction + // Commit the transaction before any further database operations if err := tx.Commit(); err != nil { return fmt.Errorf("committing transaction: %w", err) } + tx = nil // Clear tx to ensure defer doesn't try to rollback // Create indexes after bulk insert for speed slog.Info("creating indexes")