sqlconn

package
v1.148.2 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package sqlconn solves the boilerplate and lifecycle complexity of managing a database/sql connection in long-running Go services.

Problem

Opening a SQL connection in production is more than calling sql.Open: services must apply pool limits, verify connectivity, expose health checks, and close the connection gracefully on shutdown signals. When every service reimplements this flow, behavior drifts and shutdown/health edge cases become hard to reason about.

sqlconn provides a small, configurable connection manager that standardizes this pattern.

How It Works

  • New creates a connection from explicit `driver` and `dsn` values.
  • Connect accepts a URL-like string in the form `<DRIVER>://<DSN>` and delegates to New. If only the DSN is provided, a driver can be supplied via WithDefaultDriver.
  • The context passed to New/Connect bounds connection establishment only (dialing and the initial health check). It does NOT control the pool lifetime: a request- or timeout-scoped context will not close the pool when it ends. To close the pool on application shutdown, wire a long-lived context via WithLifetimeContext and/or a shutdown channel via WithShutdownSignalChan, or call SQLConn.Shutdown directly.
  • On successful connect, pool settings are applied (`max idle/open`, idle time, lifetime), and a goroutine waits for a shutdown signal channel, a canceled lifetime context, or a direct SQLConn.Shutdown call.
  • When shutdown is triggered, SQLConn.Shutdown closes the underlying database handle, updates the shared shutdown wait group, and prevents further use by setting the internal DB pointer to nil. Shutdown is idempotent and also stops the watcher goroutine, so the watcher and a deferred call can both fire safely without leaking the goroutine.

Key Features

Benefits

  • Consistent SQL connection behavior across services.
  • Safer startup/shutdown handling with fewer resource-leak risks.
  • Better testability through injectable open/connect/check functions.
  • Clear integration points for health endpoints and service orchestration.

Usage

Minimal: the deferred SQLConn.Shutdown closes the pool and stops the watcher. The context bounds establishment only, so it is safe to pass a short-lived one.

c, err := sqlconn.Connect(
    ctx,
    "mysql://user:pass@tcp(localhost:3306)/appdb",
    sqlconn.WithDefaultDriver("mysql"),
)
if err != nil {
    return err
}

if err := c.HealthCheck(ctx); err != nil {
    return err
}

defer c.Shutdown(ctx)

Service lifecycle: wire the connection into a shared shutdown channel and wait group so a central signal closes every pool and the process waits for them.

c, err := sqlconn.Connect(
    ctx,
    "mysql://user:pass@tcp(localhost:3306)/appdb",
    sqlconn.WithDefaultDriver("mysql"),
    sqlconn.WithShutdownSignalChan(shutdownCh), // close(shutdownCh) closes the pool
    sqlconn.WithShutdownWaitGroup(&shutdownWG), // shutdownWG.Wait() blocks until closed
)
if err != nil {
    return err
}

This package is ideal for Go applications that need a pragmatic, reusable database/sql connection lifecycle abstraction with health and shutdown support.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrDriverRequired is returned when no database driver is configured.
	ErrDriverRequired = errors.New("database driver must be set")

	// ErrDSNRequired is returned when no database DSN is configured.
	ErrDSNRequired = errors.New("database DSN must be set")

	// ErrNilConnectFunc is returned when the connect function is nil.
	ErrNilConnectFunc = errors.New("database connect function must be set")

	// ErrNilCheckConnectionFunc is returned when the check connection function is nil.
	ErrNilCheckConnectionFunc = errors.New("check connection function must be set")

	// ErrNilSQLOpenFunc is returned when the sql open function is nil.
	ErrNilSQLOpenFunc = errors.New("sql open function must be set")

	// ErrInvalidMaxIdleCount is returned when the pool max idle connection count is negative.
	ErrInvalidMaxIdleCount = errors.New("database pool max idle connections must not be negative")

	// ErrInvalidMaxIdleTime is returned when the connection max idle time is negative.
	ErrInvalidMaxIdleTime = errors.New("database connection max idle time must not be negative")

	// ErrInvalidMaxLifetime is returned when the connection max lifetime is negative.
	ErrInvalidMaxLifetime = errors.New("database connection max lifetime must not be negative")

	// ErrInvalidMaxOpenCount is returned when the pool max open connection count is negative.
	ErrInvalidMaxOpenCount = errors.New("database pool max open connections must not be negative")

	// ErrInvalidPingTimeout is returned when the ping timeout is below one second.
	ErrInvalidPingTimeout = errors.New("database ping timeout must be at least 1 second")

	// ErrNilLogger is returned when the logger is nil.
	ErrNilLogger = errors.New("logger is required")

	// ErrNilShutdownWaitGroup is returned when the shutdown wait group is nil.
	ErrNilShutdownWaitGroup = errors.New("shutdownWaitGroup is required")

	// ErrNilShutdownSignalChan is returned when the shutdown signal channel is nil.
	ErrNilShutdownSignalChan = errors.New("shutdownSignalChan is required")

	// ErrEmptyValidationQuery is returned when the health-check validation query is blank.
	ErrEmptyValidationQuery = errors.New("validation query must not be empty")
)

Exported sentinel errors returned by configuration validation and health checks so callers can match them with errors.Is.

View Source
var (
	// ErrNilContext is returned by [New]/[Connect] when the provided context is nil.
	ErrNilContext = errors.New("context is required")

	// ErrNilDB is returned when the configured connect (or sql open) function
	// yields a nil database handle without an error.
	ErrNilDB = errors.New("connect or sql open function returned a nil database handle")

	// ErrUnavailable is returned by [SQLConn.HealthCheck] when the connection has
	// already been shut down. Callers can match it with errors.Is to distinguish a
	// shut-down pool from a genuine connectivity failure.
	ErrUnavailable = errors.New("database is unavailable")
)

Exported sentinel errors returned by connection setup and health checks so callers can match them with errors.Is.

Functions

This section is empty.

Types

type CheckConnectionFunc

type CheckConnectionFunc func(ctx context.Context, db *sql.DB) error

CheckConnectionFunc is the type of function called to perform a DB connection check.

type ConnectFunc

type ConnectFunc func(ctx context.Context, cfg *config) (*sql.DB, error)

ConnectFunc is the type of function called to perform the actual DB connection.

type Option

type Option func(*config)

Option configures SQL connection behavior.

func WithCheckConnectionFunc

func WithCheckConnectionFunc(fn CheckConnectionFunc) Option

WithCheckConnectionFunc replaces default connection verification function. To change only the validation query of the built-in check (rather than replace the whole check), use WithValidationQuery instead.

func WithConnMaxIdleCount

func WithConnMaxIdleCount(maxIdle int) Option

WithConnMaxIdleCount sets the maximum number of idle database connections in the pool. Zero disables the idle connection pool. When greater than the max open count, database/sql silently caps it to the max open count.

func WithConnMaxIdleTime

func WithConnMaxIdleTime(t time.Duration) Option

WithConnMaxIdleTime sets the maximum time a connection may remain idle before being closed. Zero means connections are never closed due to idle time.

func WithConnMaxLifetime

func WithConnMaxLifetime(t time.Duration) Option

WithConnMaxLifetime sets the maximum time a connection may be reused before being closed. Zero means connections are never closed due to their age.

func WithConnMaxOpen

func WithConnMaxOpen(maxOpen int) Option

WithConnMaxOpen sets the maximum number of open connections in the pool. Zero means there is no limit on the number of open connections.

func WithConnectFunc

func WithConnectFunc(fn ConnectFunc) Option

WithConnectFunc replaces default connection function (e.g., for testing). The connection manager applies the pool settings (max idle/open, idle time, lifetime) to the returned handle after this function returns, so those limits take precedence over any pool tuning the custom function applied itself.

func WithDefaultDriver

func WithDefaultDriver(driver string) Option

WithDefaultDriver sets fallback driver if not included in DSN.

func WithLifetimeContext

func WithLifetimeContext(ctx context.Context) Option

WithLifetimeContext sets the context whose cancellation triggers a graceful shutdown of the connection. It is independent from the context passed to New/Connect, which bounds only connection establishment (dialing and the initial health check). When unset, the connection lifetime is governed solely by the shutdown signal channel (WithShutdownSignalChan) and explicit Shutdown calls, and a short-lived establishment context cannot tear the pool down.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger overrides default logger for connection lifecycle events.

func WithPingTimeout

func WithPingTimeout(t time.Duration) Option

WithPingTimeout sets context timeout for health check ping operations.

func WithSQLOpenFunc

func WithSQLOpenFunc(fn SQLOpenFunc) Option

WithSQLOpenFunc replaces default sql.Open wrapper (for testing only).

func WithShutdownSignalChan

func WithShutdownSignalChan(ch chan struct{}) Option

WithShutdownSignalChan sets channel to trigger graceful shutdown of connection.

func WithShutdownWaitGroup

func WithShutdownWaitGroup(wg *sync.WaitGroup) Option

WithShutdownWaitGroup sets external wait group to signal when connection closes.

func WithValidationQuery

func WithValidationQuery(query string) Option

WithValidationQuery overrides the query the built-in health check runs after the ping (default "SELECT 1"). Use it for engines where "SELECT 1" is invalid, e.g. Oracle requires "SELECT 1 FROM DUAL". The query must return at least one row with a single scannable column. It has no effect when the whole check is replaced via WithCheckConnectionFunc.

type SQLConn

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

SQLConn is the structure that helps to manage a SQL DB connection.

func Connect

func Connect(ctx context.Context, url string, opts ...Option) (*SQLConn, error)

Connect parses URL format "<DRIVER>://<DSN>" and delegates to New for connection creation.

func New

func New(ctx context.Context, driver, dsn string, opts ...Option) (*SQLConn, error)

New constructs a SQL connection with pool tuning, health checks, and graceful shutdown orchestration. The provided context bounds connection establishment only; use WithLifetimeContext and/or WithShutdownSignalChan to control the pool lifetime.

func (*SQLConn) DB

func (c *SQLConn) DB() *sql.DB

DB returns current database connection from pool; may be nil after Shutdown().

func (*SQLConn) HealthCheck

func (c *SQLConn) HealthCheck(ctx context.Context) error

HealthCheck verifies database connectivity with a ping and validation query, respecting the configured ping timeout. It returns ErrUnavailable when the connection has already been shut down.

The read lock is intentionally held for the full ping+query round-trip so the underlying handle cannot be closed by a concurrent Shutdown mid-check; a Shutdown may therefore block for up to the ping timeout behind an in-flight health check.

func (*SQLConn) Shutdown

func (c *SQLConn) Shutdown(_ context.Context) error

Shutdown gracefully closes database connection, preventing new queries and updating shutdown wait group. The context parameter is intentionally ignored: sql.DB.Close takes no context. Shutdown is idempotent; calling it more than once is a no-op and never drives the wait group negative.

type SQLOpenFunc

type SQLOpenFunc func(driverName, dataSourceName string) (*sql.DB, error)

SQLOpenFunc is the type of function called to open the DB. (Only for monkey patch testing).

Jump to

Keyboard shortcuts

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