Add config package with build-tag-switched dev/prod environments, structured logging via zerolog, Taskfile for dev workflow, golangci-lint config, testutil package, and improved DB setup with proper SQLite pragmas and cleanup. Rename sqlc output package from gen to repository. Switch to allowlist .gitignore, Alpine+UPX+scratch Dockerfile, and CI pipeline with test/lint gates before deploy.
71 lines
2.0 KiB
Go
71 lines
2.0 KiB
Go
// Package db handles SQLite database setup, pragma configuration, and
|
|
// goose migrations.
|
|
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"embed"
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/pressly/goose/v3"
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
//go:embed migrations/*.sql
|
|
var MigrationFS embed.FS
|
|
|
|
var DB *sql.DB
|
|
|
|
func Init(dbPath string) (func(), error) {
|
|
if err := os.MkdirAll(filepath.Dir(dbPath), 0o755); err != nil {
|
|
return nil, fmt.Errorf("creating data dir: %w", err)
|
|
}
|
|
|
|
// busy_timeout must be first because the connection needs to block on
|
|
// busy before WAL mode is set in case it hasn't been set already.
|
|
pragmas := "?_pragma=busy_timeout(10000)&_pragma=journal_mode(WAL)&_pragma=journal_size_limit(200000000)&_pragma=synchronous(NORMAL)&_pragma=foreign_keys(ON)&_pragma=temp_store(MEMORY)&_pragma=cache_size(-32000)"
|
|
var err error
|
|
DB, err = goose.OpenDBWithDriver("sqlite", dbPath+pragmas)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("opening database: %w", err)
|
|
}
|
|
|
|
if err := DB.Ping(); err != nil {
|
|
return nil, errors.Join(fmt.Errorf("pinging database: %w", err), DB.Close())
|
|
}
|
|
slog.Info("db connected", "db", dbPath)
|
|
|
|
sub, err := fs.Sub(MigrationFS, "migrations")
|
|
if err != nil {
|
|
return nil, errors.Join(fmt.Errorf("migrations sub fs: %w", err), DB.Close())
|
|
}
|
|
goose.SetBaseFS(sub)
|
|
|
|
if err := goose.SetDialect("sqlite3"); err != nil {
|
|
return nil, errors.Join(fmt.Errorf("setting goose dialect: %w", err), DB.Close())
|
|
}
|
|
if err := goose.Up(DB, "."); err != nil {
|
|
return nil, errors.Join(fmt.Errorf("running migrations: %w", err), DB.Close())
|
|
}
|
|
|
|
if _, err := DB.Exec("PRAGMA optimize"); err != nil {
|
|
return nil, errors.Join(fmt.Errorf("pragma optimize: %w", err), DB.Close())
|
|
}
|
|
|
|
cleanup := func() {
|
|
if _, err := DB.Exec("PRAGMA optimize(0x10002)"); err != nil {
|
|
slog.Error("pragma optimize at shutdown", "error", err)
|
|
}
|
|
if err := DB.Close(); err != nil {
|
|
slog.Error("closing database", "error", err)
|
|
}
|
|
}
|
|
|
|
return cleanup, nil
|
|
}
|