Help us shape the future of Go! Take the 2026 Go Developer Survey

db

package
v0.3.4 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 2026 License: AGPL-3.0 Imports: 27 Imported by: 0

Documentation

Overview

Package db opens the database and guards its schema: the connection pools, the goose migrations and the checks that a Major upgrade did not skip a release.

Index

Constants

View Source
const (
	PoolSQL   = "sql"
	PoolRiver = "river"
)

Pool label values: the only label on the pool gauges, so cardinality is bounded by the number of pools (two).

View Source
const UpgradeGuideURL = "https://getsphericon.com/docs/operations/upgrading"

UpgradeGuideURL is where every refusal sends the reader.

Variables

View Source
var Majors = []Major{
	{Major: 1, Floor: "v0.0.0"},

	{Major: 2, Floor: "v0.1.2"},
}

Majors lists every Major this binary knows, oldest first; the last is the Major of its own schema. A destructive migration adds an entry here and writes the same pair to the state row (a test compares the two). `sphericon upgrade` walks it: the last release of Major N is the Floor of Major N+1.

Functions

func ConfigurePool

func ConfigurePool(sqlDB *sql.DB, p config.DBPool)

ConfigurePool applies the DB_* limits to the database/sql pool so the app never opens unbounded Postgres connections.

func IsUnavailable

func IsUnavailable(err error) bool

IsUnavailable reports whether err means the database cannot be reached right now: a failed or dropped connection, a server shutting down or out of connections. The request that met it changed nothing and a retry can succeed, unlike any other error.

func IsUniqueViolation

func IsUniqueViolation(err error) bool

IsUniqueViolation reports whether err is a Postgres unique-constraint violation.

func Migrate

func Migrate(ctx context.Context, sqlDB *sql.DB) (int, error)

Migrate applies the embedded migrations to sqlDB with goose and reports how many it ran: the app schema, river's tables and the domain-event outbox all come from them. It is the one path the server (`sphericon migrate`) and the test databases share, so a test runs against the schema production gets.

It takes a database lock, so two migrations started together run one after the other and the second finds nothing to do. It has no time limit of its own: the wait for the lock and the run end when ctx does, so the caller chooses (`migrate` passes none).

func MigrateFS

func MigrateFS(ctx context.Context, sqlDB *sql.DB, files fs.FS) (int, error)

MigrateFS is Migrate over any migrations directory of SQL files.

func NewEntClient

func NewEntClient(sqlDB *sql.DB) *ent.Client

NewEntClient is the plain client: it refuses a change to the Workspace feed, which is made only inside a transaction (events.Bus.WithinTx).

func NewEntClientOnTx

func NewEntClientOnTx(sqlDB *sql.DB) *ent.Client

NewEntClientOnTx is a client over a sqlDB whose one connection is already a transaction (go-txdb in tests), so a change to the Workspace feed takes its position inside that transaction.

func OpenRiverDB

func OpenRiverDB(url string, p config.DBPool) (*sql.DB, error)

OpenRiverDB opens the river pool: a database/sql pool of its own, capped at SPHERICON_RIVER_DB_MAX_CONNS, so job workers can never starve the API's pool.

func RegisterPoolMetrics

func RegisterPoolMetrics(sqlDB, riverDB *sql.DB) (metric.Registration, error)

RegisterPoolMetrics exports both pools' statistics as OTel observable gauges on the global meter provider (so they surface wherever that provider is scraped), labelled only by pool name. Either pool may be nil.

Types

type Guard

type Guard struct {
	Build buildinfo.Info
	// AutoMigrate says a binary one Major ahead will migrate before it serves.
	AutoMigrate bool
	// Majors is the binary's table; nil means the package's Majors.
	Majors []Major
}

Guard decides at start whether this binary may run on the database (ADR 0033).

func (Guard) Check

func (g Guard) Check(ctx context.Context, sqlDB *sql.DB) error

Check reads the state row and refuses (with a *RefusalError) when the binary cannot run:

  • it is older than the floor (a rollback past a removal), whatever the Major;
  • the database is two or more Majors behind it (a skipped Major): install the last release of the database's Major first;
  • the database is one Major behind and AutoMigrate is off: run `sphericon migrate`.

A binary of the database's Major, or one Major behind it but not older than the floor (the last release of a Major), starts. A build with no release number is never older than a floor.

type Major

type Major struct {
	Major int
	Floor string
}

Major is one Major of the schema (ADR 0033): its number, a counter that starts at 1 and grows by one with each destructive migration, and its Floor, the last release of the previous Major, which is the oldest release that still runs on this schema.

type RefusalError

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

RefusalError is a start that the guard stops; its message names the remedy.

func (*RefusalError) Error

func (r *RefusalError) Error() string

type SchemaState

type SchemaState struct {
	Major int
	Floor string
}

SchemaState is the state row: the database's Major and the oldest release that still runs on it. Only a destructive migration changes it.

func ReadState

func ReadState(ctx context.Context, sqlDB *sql.DB) (SchemaState, error)

ReadState reads the state row. A database with no row yet (empty, or migrated before the row existed) is Major 1.

type SchemaStatus

type SchemaStatus struct {
	Current int64
	Newest  int64
	Pending []string
	// State is the state row (Major 1 when there is none yet) and BinaryMajor the Major of
	// this binary's own schema. Status fills them; StatusFS, which has no table of Majors, does not.
	State       SchemaState
	BinaryMajor int
}

SchemaStatus is what `sphericon migrate --status` prints: the newest migration the schema has (0 on an empty database), the newest this binary knows and the migrations that would run, oldest first.

func Status

func Status(ctx context.Context, sqlDB *sql.DB) (SchemaStatus, error)

Status reads the schema state and the embedded migrations without changing anything.

func StatusFS

func StatusFS(ctx context.Context, sqlDB *sql.DB, files fs.FS) (SchemaStatus, error)

StatusFS is Status over any migrations directory of SQL files.

type SchemaWatch

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

SchemaWatch answers, for a process that is already serving, how many of the migrations this binary embeds the database has not run (a reset or a restored backup under a running dev stack, after the start guard long passed). The answer is cached, so asking per request costs one lookup every few seconds.

func NewSchemaWatch

func NewSchemaWatch(sqlDB *sql.DB) *SchemaWatch

NewSchemaWatch watches sqlDB against the embedded migrations.

func WatchSchema

func WatchSchema(check func(ctx context.Context) (int, error), now func() time.Time) *SchemaWatch

WatchSchema is NewSchemaWatch over any count of pending migrations and any clock.

func (*SchemaWatch) Pending

func (w *SchemaWatch) Pending(ctx context.Context) (int, error)

Pending is the number of migrations the database has not run. An error is not cached: the next call asks again.

Jump to

Keyboard shortcuts

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