54 lines
1.5 KiB
Go
54 lines
1.5 KiB
Go
//go:build auth
|
|
|
|
package auth
|
|
|
|
import (
|
|
"net/url"
|
|
"strings"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func openSQLiteDialector(path string) (gorm.Dialector, error) {
|
|
return sqlite.Open(buildSQLiteDSN(path)), nil
|
|
}
|
|
|
|
// buildSQLiteDSN augments a SQLite file path with connection pragmas that make
|
|
// the auth DB resilient on slow or contended storage.
|
|
//
|
|
// - _busy_timeout=5000 makes SQLite retry for up to 5s on SQLITE_BUSY instead
|
|
// of failing immediately. Network-backed storage (SMB/CIFS/NFS, e.g. Azure
|
|
// Files) is prone to transient lock contention during migration (see #10506).
|
|
// - _txlock=immediate takes the write lock at BEGIN, avoiding deadlocks when a
|
|
// read transaction later upgrades to a write during AutoMigrate.
|
|
//
|
|
// We deliberately do NOT set WAL journal mode: WAL relies on a shared-memory
|
|
// mmap that does not work over SMB/NFS, which is exactly the failing case here.
|
|
//
|
|
// Caller-supplied values for either pragma are preserved.
|
|
func buildSQLiteDSN(path string) string {
|
|
base := path
|
|
rawQuery := ""
|
|
if i := strings.IndexByte(path, '?'); i >= 0 {
|
|
base = path[:i]
|
|
rawQuery = path[i+1:]
|
|
}
|
|
|
|
values, err := url.ParseQuery(rawQuery)
|
|
if err != nil {
|
|
// An unparseable query string means a hand-crafted DSN we should not
|
|
// risk corrupting; leave it untouched.
|
|
return path
|
|
}
|
|
|
|
if values.Get("_busy_timeout") == "" {
|
|
values.Set("_busy_timeout", "5000")
|
|
}
|
|
if values.Get("_txlock") == "" {
|
|
values.Set("_txlock", "immediate")
|
|
}
|
|
|
|
return base + "?" + values.Encode()
|
|
}
|