Files
Charon/backend/internal/database/database.go
GitHub Actions f094123123 fix: add SQLite database recovery and WAL mode for corruption resilience
- Add scripts/db-recovery.sh for database integrity check and recovery
- Enable WAL mode verification with logging on startup
- Add structured error logging to uptime handlers with monitor context
- Add comprehensive database maintenance documentation

Fixes heartbeat history showing "No History Available" due to database
corruption affecting 6 out of 14 monitors.
2025-12-17 14:51:20 +00:00

68 lines
2.1 KiB
Go

// Package database handles database connections and migrations.
package database
import (
"database/sql"
"fmt"
"strings"
"github.com/Wikid82/charon/backend/internal/logger"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// Connect opens a SQLite database connection with optimized settings.
// Uses WAL mode for better concurrent read/write performance.
func Connect(dbPath string) (*gorm.DB, error) {
// Add SQLite performance pragmas if not already present
dsn := dbPath
if !strings.Contains(dsn, "?") {
dsn += "?"
} else {
dsn += "&"
}
// WAL mode: better concurrent access, faster writes
// busy_timeout: wait up to 5s instead of failing immediately on lock
// cache: shared cache for better memory usage
// synchronous=NORMAL: good balance of safety and speed
dsn += "_journal_mode=WAL&_busy_timeout=5000&_synchronous=NORMAL&_cache_size=-64000"
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
// Skip default transaction for single operations (faster)
SkipDefaultTransaction: true,
// Prepare statements for reuse
PrepareStmt: true,
})
if err != nil {
return nil, fmt.Errorf("open database: %w", err)
}
// Configure connection pool
sqlDB, err := db.DB()
if err != nil {
return nil, fmt.Errorf("get underlying db: %w", err)
}
configurePool(sqlDB)
// Verify WAL mode is enabled and log confirmation
var journalMode string
if err := db.Raw("PRAGMA journal_mode").Scan(&journalMode).Error; err != nil {
logger.Log().WithError(err).Warn("Failed to verify SQLite journal mode")
} else {
logger.Log().WithField("journal_mode", journalMode).Info("SQLite database connected with WAL mode enabled")
}
return db, nil
}
// configurePool sets connection pool settings for SQLite.
// SQLite handles concurrency differently than server databases,
// so we use conservative settings.
func configurePool(sqlDB *sql.DB) {
// SQLite is file-based, so we limit connections
// but keep some idle for reuse
sqlDB.SetMaxOpenConns(1) // SQLite only allows one writer at a time
sqlDB.SetMaxIdleConns(1) // Keep one connection ready
sqlDB.SetConnMaxLifetime(0) // Don't close idle connections
}