Documentation
¶
Index ¶
Constants ¶
const PrimaryName = config.PrimaryDataSourceName
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 ¶
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.