diff --git a/internal/database/database.go b/internal/database/database.go index 1152aef..0fd7b49 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -36,26 +36,17 @@ type DB struct { } // New creates a new database connection at the specified path. -// It automatically handles database recovery, creates the schema if needed, -// and configures SQLite with appropriate settings for performance and reliability. -// The database uses WAL mode for better concurrency and sets a busy timeout -// to handle concurrent access gracefully. -// -// If the database appears locked, it will attempt recovery by removing stale -// lock files and switching temporarily to TRUNCATE journal mode. -// -// New creates a new database connection at the specified path. -// It automatically handles recovery from stale locks, creates the schema if needed, -// and configures SQLite with WAL mode for better concurrency. +// It creates the schema if needed and configures SQLite with WAL mode for +// better concurrency. SQLite handles crash recovery automatically when +// opening a database with journal/WAL files present. // The path parameter can be a file path for persistent storage or ":memory:" // for an in-memory database (useful for testing). func New(ctx context.Context, path string) (*DB, error) { log.Debug("Opening database connection", "path", path) - // First, try to recover from any stale locks - if err := recoverDatabase(ctx, path); err != nil { - log.Warn("Failed to recover database", "error", err) - } + // Note: We do NOT delete journal/WAL files before opening. + // SQLite handles crash recovery automatically when the database is opened. + // Deleting these files would corrupt the database after an unclean shutdown. // First attempt with standard WAL mode log.Debug("Attempting to open database with WAL mode", "path", path) @@ -156,62 +147,6 @@ func (db *DB) Close() error { return nil } -// recoverDatabase attempts to recover a locked database -func recoverDatabase(ctx context.Context, path string) error { - // Check if database file exists - if _, err := os.Stat(path); os.IsNotExist(err) { - // No database file, nothing to recover - return nil - } - - // Remove stale lock files - // SQLite creates -wal and -shm files for WAL mode - walPath := path + "-wal" - shmPath := path + "-shm" - journalPath := path + "-journal" - - log.Info("Attempting database recovery", "path", path) - - // Always remove lock files on startup to ensure clean state - removed := false - - // Check for and remove journal file (from non-WAL mode) - if _, err := os.Stat(journalPath); err == nil { - log.Info("Found journal file, removing", "path", journalPath) - if err := os.Remove(journalPath); err != nil { - log.Warn("Failed to remove journal file", "error", err) - } else { - removed = true - } - } - - // Remove WAL file - if _, err := os.Stat(walPath); err == nil { - log.Info("Found WAL file, removing", "path", walPath) - if err := os.Remove(walPath); err != nil { - log.Warn("Failed to remove WAL file", "error", err) - } else { - removed = true - } - } - - // Remove SHM file - if _, err := os.Stat(shmPath); err == nil { - log.Info("Found shared memory file, removing", "path", shmPath) - if err := os.Remove(shmPath); err != nil { - log.Warn("Failed to remove shared memory file", "error", err) - } else { - removed = true - } - } - - if removed { - log.Info("Database lock files removed") - } - - return nil -} - // Conn returns the underlying *sql.DB connection. // This should be used sparingly and primarily for read operations. // For write operations, prefer using the ExecWithLog method.