Remove dangerous database recovery that deleted journal/WAL files
SQLite handles crash recovery automatically when opening a database. The previous recoverDatabase() function was deleting journal and WAL files BEFORE opening the database, which prevented SQLite from recovering incomplete transactions and caused database corruption after Ctrl+C or crashes. This was causing "database disk image is malformed" errors after interrupting a backup operation.
This commit is contained in:
parent
43a69c2cfb
commit
bbe09ec5b5
@ -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.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user