This commit is contained in:
2026-08-10 21:15:19 +01:00
parent d7ca591b76
commit 4da942786e
97 changed files with 105039 additions and 3370 deletions
+23 -11
View File
@@ -43,23 +43,35 @@ func Open(driver, dsn string) (*DB, error) {
sqlDriverName = name
}
if d == "sqlite" {
// Set these as mattn/go-sqlite3 DSN params, not a post-open PRAGMA
// Exec: with more than one pooled connection, each new physical
// connection database/sql opens is a fresh SQLite connection that
// does NOT inherit a PRAGMA set on a different one (journal_mode is
// the one exception — it's persisted in the DB file itself).
// _foreign_keys and _busy_timeout must be per-connection, so they
// have to ride in the DSN to apply to every connection the pool
// ever opens, not just the first.
sep := "?"
if strings.Contains(dsn, "?") {
sep = "&"
}
dsn += sep + "_journal_mode=WAL&_foreign_keys=on&_busy_timeout=5000"
}
sqlDB, err := sql.Open(sqlDriverName, dsn)
if err != nil {
return nil, fmt.Errorf("opening database: %w", err)
}
if d == "sqlite" {
// SQLite doesn't handle concurrent writers well — serialize via single conn.
sqlDB.SetMaxOpenConns(1)
if _, err := sqlDB.Exec("PRAGMA journal_mode=WAL"); err != nil {
return nil, fmt.Errorf("enabling WAL mode: %w", err)
}
if _, err := sqlDB.Exec("PRAGMA foreign_keys=ON"); err != nil {
return nil, fmt.Errorf("enabling foreign keys: %w", err)
}
if _, err := sqlDB.Exec("PRAGMA busy_timeout=5000"); err != nil {
return nil, fmt.Errorf("setting busy timeout: %w", err)
}
// WAL mode already lets SQLite itself handle concurrent readers +
// one writer (with _busy_timeout above covering writer contention) —
// a small pool, not a single shared connection, so concurrent
// requests across SMTP/IMAP/webmail/admin/etc. aren't all serialized
// through one connection for no reason.
sqlDB.SetMaxOpenConns(4)
sqlDB.SetMaxIdleConns(4)
}
if err := sqlDB.Ping(); err != nil {