package core import ( "database/sql" "fmt" "net/url" "strings" "time" // Pure-Go SQLite driver. No cgo means CGO_ENABLED=0 static binaries and // trivial cross-compilation for arm64. _ "modernc.org/sqlite" ) // pragmas applied to every pooled connection, via the DSN. // // These are passed in the DSN rather than run as statements after connecting // because database/sql pools connections: a one-off PRAGMA would configure // only whichever single connection happened to serve it. var sqlitePragmas = []string{ "journal_mode(WAL)", // concurrent readers during a scan // busy_timeout is deliberately generous. Chunked scans hold the single // write lock in short bursts, so a concurrent write is never blocked for // long — but it is blocked, and measured tail latency on a first scan of // 15,000 new files reached 3.3s. At the old 5s ceiling that left almost no // margin, and on slower storage a progress save would fail outright with // SQLITE_BUSY and the user would lose their place. Waiting is strictly // better than failing: the write still succeeds, just later. "busy_timeout(15000)", "foreign_keys(1)", // enforce the schema's referential integrity "synchronous(NORMAL)", // safe under WAL, far faster than FULL "cache_size(-32000)", // ~32 MB page cache } // OpenDB opens the SQLite database with the pragmas Vellum depends on. func OpenDB(path string) (*sql.DB, error) { db, err := sql.Open("sqlite", buildDSN(path, sqlitePragmas)) if err != nil { return nil, fmt.Errorf("open sqlite %s: %w", path, err) } // SQLite is a single-writer database. A large pool just produces // SQLITE_BUSY contention; a small pool with a busy_timeout is faster. db.SetMaxOpenConns(8) db.SetMaxIdleConns(8) db.SetConnMaxLifetime(0) db.SetConnMaxIdleTime(5 * time.Minute) if err := db.Ping(); err != nil { _ = db.Close() return nil, fmt.Errorf("ping sqlite %s: %w", path, err) } return db, nil } // buildDSN assembles a modernc.org/sqlite DSN with repeated _pragma params. func buildDSN(path string, pragmas []string) string { var b strings.Builder b.WriteString("file:") b.WriteString(path) b.WriteString("?_time_format=sqlite") for _, p := range pragmas { b.WriteString("&_pragma=") b.WriteString(url.QueryEscape(p)) } return b.String() }