database

package module
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

README

go-database

SQL infrastructure library for Standards Lab's Go Elemental standard: the connection pool with its configuration, lifecycle, and readiness, the database admin service over the sqlate library, and the Postgres provider.

github.com/standards-lab/go-database is the base module, holding the database package at its root and admin beside it. github.com/standards-lab/go-database/postgres is a nested sub-module that pins the driver and is released on its own tags.

Standard

go-database is an infrastructure library of Go Elemental, the minimal-dependency Go standard, and its design is documented on the standard's go-database page. Its repository-level principles:

  • The base module depends on the standard library, go-core, and sqlate; the driver enters a consumer's graph only through the postgres sub-module, imported once at the composition root.
  • Statements, sessions, transactions, and the dialect are sqlate's: the composition root wraps the pool this library constructs with sqlate.Wrap and the engine's dialect from sqlate/postgres. The provider's native API stays reachable through DB.Conn() and the Options map.
  • Swapping the SQL provider is a port, not a configuration change: the consumer owns its schema, its migrations, and its domain SQL, and those are what a swap rewrites.

Packages

  • database: the infrastructure service, a lifecycle-integrated wrapper over a database/sql pool, the service's configuration block, and the connectivity error sentinels.
  • admin: the database admin service, schema verification, migration, seeding, named states, and diagnostics as operations over sqlate's functions, run at startup and on demand.
  • postgres: the PostgreSQL provider, which constructs the pool over pgx's database/sql adapter from the configuration block.

Development

The repository uses a Go workspace and mise:

mise run build   # build every module standalone, with the workspace off
mise run test    # test every module

License

Apache License 2.0.

Documentation

Overview

Package database is the SQL infrastructure service: a lifecycle-integrated wrapper over a database/sql connection pool and the configuration block that sizes it. The package depends on the standard library and go-core's config package alone; every driver lives in a provider sub-module (postgres) that constructs the pool, so a consumer imports its provider once, at the composition root. Statements, sessions, transactions, and the dialect are the sqlate library's: the composition root wraps DB.Conn with sqlate.Wrap and the engine's dialect, and the admin package administers the schema over both.

Wrapper

New wraps the provider-constructed *sql.DB with a finalized Config, applying the config's pool settings, the half of construction every provider shares. It performs no I/O: an unfinalized Config or a nil conn panics with the fix named, and everything else waits for Start. DB.Conn exposes the pool.

The panic follows the package's wiring rule. A defect the composition root wires, such as an unfinalized config or a missing pool, panics, because no runtime condition produces it and no caller can recover from it sensibly. A defect in configuration content, such as a reserved connection option, returns an error from the provider's constructor, because configuration is input.

Lifecycle wiring

The package registers no lifecycle hooks of its own. DB.Start and DB.Shutdown carry the lifecycle package's hook signature, so a composition root wires the database as bare method values:

lc.OnStartup(db.Start)
lc.OnShutdown(db.Shutdown)

Start establishes connectivity with a ping bounded by the configured conn_timeout, so a dead database fails the coordinator's startup rather than serving traffic unready. Shutdown closes the pool and returns the close error; after a failed startup the drain passes through it cleanly.

Readiness

DB.Ready satisfies lifecycle.ReadinessChecker structurally and reports live connectivity: false before Start or after Shutdown, and otherwise a ping bounded by conn_timeout. A readiness probe aggregating the database therefore reflects it now, 503 during an outage and healed when the database returns, at the cost of one bounded round trip per probe. DB.Ping is the same verification under the caller's context and bound.

Configuration

Config holds the connection identity, pool sizing, and timeouts, and implements the config package's Merge and Finalize contract, so it loads as part of an application's configuration. The numeric and duration fields are pointers: nil is unset and takes the default, while an explicit zero survives the load and means what it says. Port defaults in the provider, User and Password stay optional (requiredness varies by provider and auth mode), and Options passes engine-specific connection keys through to the provider. Finalize composes the standard override names from the prefix it receives (via NewEnv, recorded on Env for introspection); an empty prefix disables the overrides.

Errors

ErrNotReady and ErrConnectionFailed classify the service conditions, wrapped in the dual form fmt.Errorf("%w: %w", sentinel, err) so errors.Is classifies while the driver's error stays recoverable. Errors a statement raises, the constraint classes among them, are classified by the sqlate dialect inside the session and are sqlate's sentinels.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotReady reports a call against a [DB] before a successful Start
	// or after Shutdown.
	ErrNotReady = errors.New("database not ready")

	// ErrConnectionFailed classifies a connectivity failure. It is wrapped
	// alongside the driver's error in the dual form
	// fmt.Errorf("%w: %w", ErrConnectionFailed, err), so errors.Is matches
	// the class while the cause stays recoverable.
	ErrConnectionFailed = errors.New("database connection failed")
)

Functions

This section is empty.

Types

type Config

type Config struct {
	Host            string            `json:"host"`
	Name            string            `json:"name"`
	User            string            `json:"user"`
	Password        string            `json:"password"`
	Port            *int              `json:"port"`
	MaxOpenConns    *int              `json:"max_open_conns"`
	MaxIdleConns    *int              `json:"max_idle_conns"`
	ConnMaxLifetime *config.Duration  `json:"conn_max_lifetime"`
	ConnMaxIdleTime *config.Duration  `json:"conn_max_idle_time"`
	ConnTimeout     *config.Duration  `json:"conn_timeout"`
	Options         map[string]string `json:"options"`
	Env             Env               `json:"-"`
}

Config holds the connection identity, pool sizing, and timeouts for a SQL database. The numeric and duration fields are tri-state pointers: nil is unset and takes the default, while an explicit zero survives the load and means what it says — an unlimited pool, or no idle connections. Port has no default here because the default port is a provider fact; each provider supplies its own. User and Password are optional because their requiredness varies by provider and auth mode; the password rides the secrets layer of config.Load rather than a committed file. Options carries dialect-specific connection keys (postgres: sslmode) passed through to the provider. Env records the environment-variable names Finalize composed and read; it is excluded from JSON.

func (*Config) Finalize

func (c *Config) Finalize(envPrefix string) error

Finalize composes the environment override names from envPrefix (an empty prefix disables overrides), applies defaults, applies the overrides, and validates. Name is the one required field; a malformed override fails with an error naming its variable.

func (*Config) Merge

func (c *Config) Merge(src *Config)

Merge overlays src's set fields onto the receiver. Options merges key-wise, so an overlay can set one connection option without dropping the rest.

type DB

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

DB wraps a provider-constructed connection pool with lifecycle integration. It owns one pool for the process: construction performs no I/O, Start establishes connectivity, and Ready reports it live. Start and Shutdown carry the lifecycle hook signature, so the composition root registers the bare method values, and Ready satisfies lifecycle.ReadinessChecker structurally; the package registers no hooks of its own.

func New

func New(conn *sql.DB, cfg Config) *DB

New wraps a provider-constructed pool and applies cfg's pool settings, the half of construction every provider shares. It panics if cfg was not finalized or on a nil conn: each is a wiring defect at the composition root, not a runtime condition.

func (*DB) Conn

func (d *DB) Conn() *sql.DB

Conn returns the underlying connection pool. The SQL layer wraps it (sqlate.Wrap with the engine's dialect) and the admin service reads its statistics; nothing in this package runs statements on it.

func (*DB) Ping

func (d *DB) Ping(ctx context.Context) error

Ping verifies connectivity under the caller's context: ErrNotReady before Start or after Shutdown, and otherwise a ping whose failure wraps ErrConnectionFailed. Unlike DB.Ready it applies no timeout of its own.

func (*DB) Ready

func (d *DB) Ready() bool

Ready reports live connectivity: false before Start or after Shutdown, and otherwise the result of a ping bounded by the configured conn_timeout. The probe reflects the database now — readiness drops during an outage and recovers with it, at the cost of one bounded round trip per call.

func (*DB) Shutdown

func (d *DB) Shutdown(ctx context.Context) error

Shutdown clears readiness and closes the pool, returning the close error. Closing a pool that never connected is a clean no-op, so the drain-after-failed-startup path is safe. The context exists for the lifecycle hook signature; sql.DB.Close is synchronous.

func (*DB) Start

func (d *DB) Start(ctx context.Context) error

Start establishes connectivity with a ping bounded by the configured conn_timeout and marks the database started. A failure wraps ErrConnectionFailed around the driver's error. There is no started guard: a repeat Start just pings again.

type Env

type Env struct {
	Host            string
	Name            string
	User            string
	Password        string
	Port            string
	MaxOpenConns    string
	MaxIdleConns    string
	ConnMaxLifetime string
	ConnMaxIdleTime string
	ConnTimeout     string
}

Env names the environment variables Config.Finalize reads, composed from the prefix it receives under the "database" segment: DATABASE_HOST, DATABASE_PORT, and the rest, prefixed by whatever config.EnvName produces. An empty name disables that one override; the zero value — an empty prefix — disables all of them. Populated by Finalize and exposed for introspection.

func NewEnv

func NewEnv(prefix string) Env

NewEnv composes the standard override names from a prefix under the "database" segment: DATABASE_HOST, DATABASE_PORT, and the rest, prefixed by whatever config.EnvName produces. An empty prefix returns the zero Env, disabling the overrides.

Directories

Path Synopsis
Package admin is the database admin service: schema state, verification, correction, seeding, named states, and diagnostics as operations over the sqlate library's functions, run once at startup and on demand from an administrative surface.
Package admin is the database admin service: schema state, verification, correction, seeding, named states, and diagnostics as operations over the sqlate library's functions, run once at startup and on demand from an administrative surface.
postgres module

Jump to

Keyboard shortcuts

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