datasource

package
v0.38.0 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Index

Constants

PrimaryName is the reserved name for the data source declared under vef.data_sources.primary. The primary source is constructed from the TOML configuration, exposed in the FX container as orm.DB, and cannot be mutated through Register/Update/Unregister. It aliases config.PrimaryDataSourceName (the lowest-layer canonical definition) so the two can never diverge.

Variables

View Source
var (
	// ErrNotFound is returned by Registry.Get/Update/Unregister/Kind when no data
	// source is currently registered under the requested name.
	ErrNotFound = errors.New("datasource: data source not found")

	// ErrExists is returned by Registry.Register when a data source with the same
	// name is already registered. Use Update to replace an existing
	// configuration, or Unregister + Register to fully recycle the entry.
	ErrExists = errors.New("datasource: data source already registered")

	// ErrPrimaryReserved is returned by Register/Update/Unregister when the caller
	// attempts to mutate the primary data source through the dynamic API. The
	// primary source is owned by the TOML configuration and lives for the lifetime
	// of the application.
	ErrPrimaryReserved = errors.New("datasource: primary data source is reserved")

	// ErrNameInvalid is returned by Register/Update when a name is empty or
	// contains whitespace or control characters.
	ErrNameInvalid = errors.New("datasource: data source name invalid")

	// ErrClosed is returned by Register/Update/Unregister once the registry has
	// begun shutting down. After shutdown the connection pools are drained and
	// closed, so accepting a mutation would either leak a freshly opened pool or
	// race the shutdown drain.
	ErrClosed = errors.New("datasource: registry is closed")
)

Functions

This section is empty.

Types

type ConnectionInfo

type ConnectionInfo struct {
	// Version is the server version string reported by the database — for
	// example "PostgreSQL 16.2 on x86_64-..." or an SQLite library version such
	// as "3.45.1". It is surfaced back to operators as confirmation of a
	// successful test.
	Version string
}

ConnectionInfo describes a data source reached by TestConnection. It is populated only on a successful probe.

type Provider

type Provider interface {
	// Name identifies the provider in error messages and logs. It does not need
	// to be globally unique but should be descriptive (for example "tenant-table"
	// or "vault-secrets").
	Name() string
	// Load returns the data sources this provider knows about. It runs once
	// during the FX startup phase and any error aborts boot.
	Load(ctx context.Context) ([]Spec, error)
}

Provider supplies additional data sources during application startup. The framework calls Load on every registered provider after the primary and static (TOML) data sources are already in the registry; each returned Spec is then Register'd. Provider order is undefined, and a name collision (with TOML or another provider) is reported as a startup failure.

type ReconcileOption

type ReconcileOption func(*ReconcileOptions)

ReconcileOption tunes a single Reconcile invocation.

func WithReconcileDryRun

func WithReconcileDryRun() ReconcileOption

WithReconcileDryRun returns a ReconcileOption that flips Reconcile into preview mode: the report still lists Added/Updated/Removed but no connections are opened or closed.

type ReconcileOptions

type ReconcileOptions struct {
	// DryRun makes Reconcile compute the diff and return it in the report without
	// performing Register/Update/Unregister. Useful for previewing what a
	// refresher job would do.
	DryRun bool
}

ReconcileOptions holds the tunables a Reconcile call honors.

type ReconcileReport

type ReconcileReport struct {
	Added   []string
	Updated []string
	Removed []string
	Errors  map[string]error
}

ReconcileReport summarizes the result of a Reconcile call. Errors is keyed by data source name and is nil when every action succeeded.

type RegisterOption

type RegisterOption func(*RegisterOptions)

RegisterOption tunes a single Update or Unregister call — the two operations that close an existing connection. Options compose; later options override earlier ones for the same field.

func WithCloseGrace

func WithCloseGrace(d time.Duration) RegisterOption

WithCloseGrace returns a RegisterOption that delays the asynchronous close of a replaced (Update) or removed (Unregister) connection by d. Use it to give in-flight queries some time to drain before the connection pool tears down.

type RegisterOptions

type RegisterOptions struct {
	// CloseGrace controls how long the registry waits before closing a replaced
	// or unregistered connection. Zero (the default) closes immediately on a
	// background goroutine.
	CloseGrace time.Duration
}

RegisterOptions holds the tunables an Update/Unregister call honors. It is exported so registry implementations can read it; user code should construct it via the RegisterOption helpers.

type Registry

type Registry interface {
	// Primary returns the orm.DB bound to the primary TOML configuration. It
	// is equivalent to Get(PrimaryName) but never returns an error.
	Primary() orm.DB

	// Get returns the orm.DB registered under name. It returns ErrNotFound when
	// no source is currently registered under name (including after it was
	// Unregister'd).
	Get(name string) (orm.DB, error)

	// Has reports whether name is currently registered and not closed.
	Has(name string) bool

	// Names returns every currently registered name (including primary) in
	// stable lexical order. Closed entries are excluded.
	Names() []string

	// Kind returns the dialect for the named data source. It returns ErrNotFound
	// for the same conditions as Get. Useful for sqlmigration / schema reflection
	// callers that need to branch on dialect without pulling out the full DB.
	Kind(name string) (config.DBKind, error)

	// Register opens a new data source and inserts it under name. It returns
	// ErrExists if the name is already registered, ErrPrimaryReserved if name
	// equals PrimaryName, ErrNameInvalid if name is empty or contains
	// whitespace/control characters, and ErrClosed once the registry has begun
	// shutting down. The newly opened connection is closed and not retained on
	// conflict. Register never closes an existing connection, so it takes no
	// RegisterOption.
	Register(ctx context.Context, name string, cfg config.DataSourceConfig) (orm.DB, error)

	// Update atomically replaces the connection for an existing data source with
	// one opened from cfg. The new connection must Open and Ping successfully
	// before the swap; on failure the existing entry is left untouched. The old
	// underlying *sql.DB is closed asynchronously so callers that already hold a
	// DB reference observe no downtime.
	//
	// Update returns ErrNotFound when name is not registered,
	// ErrPrimaryReserved when name is PrimaryName, and ErrClosed once the
	// registry has begun shutting down.
	Update(ctx context.Context, name string, cfg config.DataSourceConfig, opts ...RegisterOption) (orm.DB, error)

	// Unregister removes the named data source from the registry. Subsequent Get
	// calls return ErrNotFound. The underlying *sql.DB is closed asynchronously
	// (honoring WithCloseGrace), so callers already holding a DB reference can
	// finish in-flight queries before the connection pool tears down. Unregister
	// returns ErrPrimaryReserved for the primary source, ErrNotFound when name
	// is not registered, and ErrClosed once the registry has begun shutting down.
	Unregister(ctx context.Context, name string, opts ...RegisterOption) error

	// Reconcile drives the registry toward the supplied desired set of
	// non-primary sources. The implementation computes three buckets:
	//
	//   - specs has name but registry does not   → Register
	//   - both have name but cfg differs         → Update
	//   - registry has name but specs does not   → Unregister
	//
	// Reconciles are serialized (not concurrent) so two ticks of a refresher job
	// never race. Per-name failures populate ReconcileReport.Errors and do not
	// interrupt processing of the remaining names. specs entries referencing the
	// primary name are ignored.
	Reconcile(ctx context.Context, specs []Spec, opts ...ReconcileOption) (ReconcileReport, error)

	// TestConnection opens a throwaway connection from cfg, verifies it by
	// querying the server version, then closes it immediately. It never registers
	// anything and never mutates the registry — it is the connectivity probe
	// behind a UI "test connection" button, run before Register/Update. On success
	// it returns the reached server's ConnectionInfo; any error means the source
	// is unreachable or unusable.
	TestConnection(ctx context.Context, cfg config.DataSourceConfig) (ConnectionInfo, error)

	// HealthCheck pings every registered source in parallel and returns a
	// name -> error map. A nil error indicates the source is reachable.
	HealthCheck(ctx context.Context) map[string]error
}

Registry is the set of named orm.DB instances. Applications inject it whenever they need to reach a data source other than the primary one.

All read methods (Get/Has/Names/Kind/Primary) are safe for concurrent use. Register/Update/Unregister mutate the registry atomically; Reconcile is serialized so concurrent reconciles cannot fight each other.

type Spec

type Spec struct {
	// Name is the registry key. Must be non-empty and not equal to PrimaryName.
	Name string
	// Config is the connection configuration applied when opening the source.
	Config config.DataSourceConfig
}

Spec is the (name, config) pair produced by a Provider and consumed by Registry.Reconcile.

Jump to

Keyboard shortcuts

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