fix(db): prevent "database is locked" errors by improving SQLite usage

- Add explicit rows.Close() calls inside loops to avoid holding locks too long
- Add SQLite busy timeout (5s) to connection string to wait for locks instead of failing immediately
This commit is contained in:
KaNaDaAT 2025-05-15 20:53:16 +02:00
parent 1daf682062
commit ab5be2f50e

View file

@ -15,6 +15,15 @@ type SQLiteClient struct {
} }
func NewSQLiteClient(dataSourceName string) (*SQLiteClient, error) { func NewSQLiteClient(dataSourceName string) (*SQLiteClient, error) {
// Add busy timeout param to DSN (milliseconds)
if !strings.Contains(dataSourceName, "_busy_timeout") {
if strings.Contains(dataSourceName, "?") {
dataSourceName += "&_busy_timeout=5000" // 5 seconds
} else {
dataSourceName += "?_busy_timeout=5000"
}
}
db, err := sql.Open("sqlite3", dataSourceName) db, err := sql.Open("sqlite3", dataSourceName)
if err != nil { if err != nil {
return nil, fmt.Errorf("error connecting to SQLite: %s", err) return nil, fmt.Errorf("error connecting to SQLite: %s", err)
@ -28,6 +37,7 @@ func NewSQLiteClient(dataSourceName string) (*SQLiteClient, error) {
return &SQLiteClient{db: db}, nil return &SQLiteClient{db: db}, nil
} }
// createTables creates the required tables if they don't exist // createTables creates the required tables if they don't exist
func createTables(db *sql.DB) error { func createTables(db *sql.DB) error {
createSongsTable := ` createSongsTable := `
@ -100,22 +110,26 @@ func (db *SQLiteClient) GetCouples(addresses []uint32) (map[uint32][]models.Coup
if err != nil { if err != nil {
return nil, fmt.Errorf("error querying database: %s", err) return nil, fmt.Errorf("error querying database: %s", err)
} }
defer rows.Close()
var docCouples []models.Couple var docCouples []models.Couple
for rows.Next() { for rows.Next() {
var couple models.Couple var couple models.Couple
if err := rows.Scan(&couple.AnchorTimeMs, &couple.SongID); err != nil { if err := rows.Scan(&couple.AnchorTimeMs, &couple.SongID); err != nil {
rows.Close() // close before returning error
return nil, fmt.Errorf("error scanning row: %s", err) return nil, fmt.Errorf("error scanning row: %s", err)
} }
docCouples = append(docCouples, couple) docCouples = append(docCouples, couple)
} }
rows.Close() // close explicitly after reading
couples[address] = docCouples couples[address] = docCouples
} }
return couples, nil return couples, nil
} }
func (db *SQLiteClient) TotalSongs() (int, error) { func (db *SQLiteClient) TotalSongs() (int, error) {
var count int var count int
err := db.db.QueryRow("SELECT COUNT(*) FROM songs").Scan(&count) err := db.db.QueryRow("SELECT COUNT(*) FROM songs").Scan(&count)