db

package
v0.26.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package db is nexus's driver-agnostic GORM manager. It mirrors the shape of oats_admin_backend/database.DBManager (Start/Stop/GetDB/IsConnected) and keeps the same failsafe-go retry + circuit-breaker behavior, but handles PostgreSQL, MySQL, and SQLite behind a single Config.Driver field.

Typical usage:

m, err := db.Open(db.Config{
    Driver:   db.SQLite,
    Database: ":memory:",
})
if err != nil { return err }
m.Start()                     // background reconnect loop
defer m.Stop()

gdb := m.GetDB()              // *gorm.DB — chain real queries
gdb.AutoMigrate(&User{})
gdb.Create(&User{Name: "A"})

Multi-DB? Wrap multiple Managers in a multi.Registry[*db.Manager] and call .Using(name).GetDB() from resolvers — see examples/graphapp.

Index

Constants

This section is empty.

Variables

View Source
var DefaultEnvNames = EnvNames{
	Host:     "DB_HOSTNAME",
	Port:     "DB_PORT",
	User:     "DB_USERNAME",
	Password: "DB_PASSWORD",
	Database: "DB_NAME",
	SSLMode:  "DB_SSLMODE",
	TimeZone: "DB_TIMEZONE",
}

DefaultEnvNames is the framework convention. Apps wiring db.Module without overrides get these names automatically.

View Source
var Module = fx.Module("nexus-db",
	fx.Provide(Provide),
)

Module is the fx wiring for an app that wants a single Manager with auto-manifest declarations. Compose with fx.Supply to feed the per-app ProvideOptions:

nexus.Run(cfg,
    fx.Supply(db.ProvideOptions{
        Driver: db.Postgres,
        EnvNames: db.EnvNames{
            User: "DB_USERNAME", Password: "PASSWORD",
            Database: "DATABASE_NAME",
        },
        Defaults: db.Config{SSLMode: "disable", TimeZone: "UTC"},
    }),
    db.Module,
    // ...other modules...
)

Apps that need multiple Managers don't use this Module — they wire each Manager via nexus.Provide(NewMyDBA, NewMyDBB, ...) and rely on the *App auto-walk in nexus.Provide to register both.

Functions

This section is empty.

Types

type Config

type Config struct {
	Driver   Driver
	Host     string
	Port     string
	User     string
	Password string
	Database string
	SSLMode  string // "disable" / "require" / ... — postgres only
	TimeZone string // IANA TZ — postgres only
}

Config is the full connection spec. Host/Port/User/Password/SSLMode/TimeZone apply to postgres+mysql; Database is the db name for pg/mysql and the file path (":memory:" for in-memory) for sqlite.

func LoadConfig added in v0.22.2

func LoadConfig(driver Driver, names EnvNames, defaults Config) Config

LoadConfig builds a Config by reading os.Getenv against the effective name set. defaults supplies values for fields whose resolved env is empty — the runtime fallback chain. Driver is taken straight (env-derived drivers haven't proved useful in practice).

Side-effecting; called once from app constructors.

func (Config) DSN

func (c Config) DSN() string

DSN returns the connection string for the configured Driver.

func (Config) Dialector

func (c Config) Dialector() gorm.Dialector

Dialector returns the gorm dialector matching Driver.

type Driver

type Driver string

Driver picks the backing dialect. Each value has a corresponding Dialector that Config.Dialector() returns.

const (
	Postgres Driver = "postgres"
	MySQL    Driver = "mysql"
	SQLite   Driver = "sqlite"
)

type EnvNames added in v0.22.2

type EnvNames struct {
	Host, Port, User, Password, Database, SSLMode, TimeZone string
}

EnvNames maps each Config field that's normally env-driven to the env-var name that supplies its value. Same string serves two callers: Config.LoadEnv reads it (runtime); NexusEnv / NexusServices declare it (manifest). One signal, two consumers — no drift.

Zero-value fields fall back to DefaultEnvNames, so apps that follow the framework convention only override the names that differ.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager owns one *gorm.DB, reconnects in the background, and exposes the same method set as oats_admin_backend/database.DatabaseManager so it can drop in wherever that interface is expected.

func NewManager

func NewManager(cfg Config, opts ...Option) *Manager

NewManager builds a Manager without connecting. Call Open or Start next.

func Open

func Open(cfg Config, opts ...Option) (*Manager, error)

Open constructs a Manager and establishes the initial connection synchronously. Call Start() afterwards to enable the background reconnect + health-check loop.

func Provide added in v0.22.2

func Provide(lc fx.Lifecycle, opts ProvideOptions, logger *zap.Logger, reg manifest.Registrar) *Manager

Provide is the fx provider: builds a Manager from ProvideOptions, ties Start/Stop to the fx lifecycle, AND self-registers as a manifest.EnvProvider + ServiceDependencyProvider so the orchestration manifest reflects this DB's connection surface without app code calling DeclareEnv / DeclareService.

reg comes from the fx graph — *nexus.App satisfies the interface via its Declare* methods. Auto-supplied by fxEarlyOptions.

func (*Manager) ConnectionString added in v0.4.1

func (m *Manager) ConnectionString() string

ConnectionString returns the DSN for this manager's configured driver (same string the Dialector is built from). Useful for diagnostics and for tooling that needs to reconnect outside GORM — migration CLIs, ad-hoc shell scripts, etc. Contains credentials in the Postgres/MySQL forms; don't log it in production.

func (*Manager) Driver

func (m *Manager) Driver() Driver

Driver returns the configured driver (useful for dashboards).

func (*Manager) GetCtx added in v0.4.2

func (m *Manager) GetCtx() context.Context

GetCtx returns the manager's internal context. It's canceled by Stop(), so goroutines that should die alongside the manager can bind to it:

go func() {
    <-mgr.GetCtx().Done()
    // manager has stopped; unwind our side here
}()

func (*Manager) GetDB

func (m *Manager) GetDB() *gorm.DB

GetDB returns the current *gorm.DB, or nil if disconnected.

func (*Manager) IsConnected

func (m *Manager) IsConnected() bool

IsConnected returns true if a live connection is currently held.

func (*Manager) NexusEnv added in v0.22.2

func (m *Manager) NexusEnv() []manifest.EnvVar

NexusEnv lists the env vars this Manager reads, with required / secret / boundTo flags so the orchestration platform can render the deploy form correctly. Binding name is the manager's configured name (defaults to "main" — see NewManager / FromEnv).

func (*Manager) NexusServices added in v0.22.2

func (m *Manager) NexusServices() []manifest.ServiceNeed

NexusServices declares the backing database the orchestration platform should provision (or external-bind). Kind matches the Driver, so a postgres Manager declares a postgres need; a mysql Manager declares mysql.

func (*Manager) Ping

func (m *Manager) Ping(ctx context.Context) error

Ping runs a bounded health check against the current connection.

func (*Manager) Start

func (m *Manager) Start()

Start begins the background maintenance loop (5s health check + reconnect). Idempotent-ish: calling Start twice starts two loops, which is a bug but rarely fatal since they both probe the same state.

func (*Manager) Stop

func (m *Manager) Stop()

Stop cancels the maintenance loop and closes the underlying *sql.DB.

type Option

type Option func(*Manager)

Option tweaks a Manager at construction time.

func WithBindName added in v0.22.2

func WithBindName(name string) Option

WithBindName names this Manager in the manifest. Defaults to "main"; multi-DB apps override per Manager so each shows up as a distinct slot in the orchestration canvas.

func WithEnvNames added in v0.22.2

func WithEnvNames(names EnvNames) Option

WithEnvNames stamps the env-var names this Manager's Config came from onto the Manager so NexusEnv / NexusServices can declare them in the manifest. Empty fields fall back to DefaultEnvNames.

Use when the app reads from env-var names that don't follow the framework convention (e.g. oats reads PASSWORD instead of DB_PASSWORD). Apps that build their Config via LoadConfig pass the same EnvNames here.

func WithExecutor

func WithExecutor(e failsafe.Executor[*gorm.DB]) Option

WithExecutor swaps in a custom failsafe executor. Useful if you want a different retry/circuit-breaker profile (the defaults match oats exactly).

func WithLogger

func WithLogger(l *zap.Logger) Option

WithLogger attaches a zap logger. Without one, Manager runs silently.

func WithPool

func WithPool(p PoolConfig) Option

WithPool overrides the default connection-pool sizing.

type PoolConfig

type PoolConfig struct {
	MaxIdle     int
	MaxOpen     int
	ConnMaxLife time.Duration
	ConnMaxIdle time.Duration
}

PoolConfig tunes the underlying *sql.DB pool. Defaults match oats's values for postgres/mysql; sqlite :memory: gets MaxOpen=1 to keep one in-memory database shared across goroutines.

type ProvideOptions added in v0.22.2

type ProvideOptions struct {
	Driver Driver

	// EnvNames overrides the framework defaults for env-var names.
	// Empty fields inherit DefaultEnvNames so apps only override
	// what differs.
	EnvNames EnvNames

	// Defaults supplies fallback values for fields whose env var
	// resolves empty. SSLMode and TimeZone are the typical ones —
	// most apps hard-code them rather than expose env knobs.
	Defaults Config

	// BindName names this Manager in the manifest. Defaults to
	// "main". Multi-DB apps build multiple ProvideOptions with
	// distinct names + sub-modules.
	BindName string
}

ProvideOptions packages the inputs for the optional fx-graph constructor. Apps that want db.Module's auto-wiring populate this struct with their driver + per-app overrides; everything else falls back to defaults.

Why a struct (and not free-form options): fx wants concrete typed inputs in its graph. A single ProvideOptions value supplied via fx.Supply is the cleanest way to thread per-app config into a shared module without per-app fx wiring.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL