migrate

package
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: MIT Imports: 10 Imported by: 62

Documentation

Overview

Package migrate provides a database-agnostic migration system with multi-module support, dependency ordering, and distributed locking.

Migrations are Go functions (not SQL files). Any Go module can register migrations into a shared, ordered, dependency-aware migration plan. Forge extensions ship their own migrations that compose with the host app.

Index

Constants

View Source
const (
	MigrationTable     = "grove_migrations"
	MigrationLockTable = "grove_migration_locks"
)

Schema constants for the migration tracking tables.

View Source
const DefaultLockTimeout = 5 * time.Minute

DefaultLockTimeout is the default maximum time Migrate/Rollback will wait to acquire the migration lock before giving up. Raised from the original 30s so that several extensions migrating the same database in one process serialize and wait instead of failing the boot.

Variables

View Source
var DefaultRegistry = NewMigrationRegistry()

DefaultRegistry is the global migration registry. Modules register their groups here via init() functions.

View Source
var ErrLockHeld = errors.New("migrate: lock is held by another process")

ErrLockHeld is returned when a migration lock is already held.

Functions

func Executors

func Executors() []string

Executors returns the names of all registered executor factories.

func IsLockError

func IsLockError(err error) bool

IsLockError returns true if the error indicates a lock conflict.

func MigrationLockTableSchema

func MigrationLockTableSchema() string

MigrationLockTableSchema returns the CREATE TABLE SQL for the lock table.

func MigrationTableSchema

func MigrationTableSchema() string

MigrationTableSchema returns the CREATE TABLE SQL for the migration tracking table.

func RegisterExecutor

func RegisterExecutor(driverName string, factory ExecutorFactory)

RegisterExecutor registers a migration executor factory for a given driver name. It is typically called from a driver's migrate package init() function for auto-registration, or explicitly by user code during setup.

Subsequent calls with the same name overwrite the previous registration.

Auto-registration example (in pgmigrate package):

func init() {
    migrate.RegisterExecutor("pg", func(drv any) migrate.Executor {
        return New(drv.(driver.Driver))
    })
}

Explicit registration example:

migrate.RegisterExecutor("pg", func(drv any) migrate.Executor {
    return pgmigrate.New(drv.(driver.Driver))
})

Types

type AppliedMigration

type AppliedMigration struct {
	ID         int64
	Version    string
	Name       string
	Group      string
	MigratedAt string // ISO 8601 timestamp
}

AppliedMigration records that a migration has been applied.

type Executor

type Executor interface {
	// Exec executes a SQL statement that does not return rows.
	Exec(ctx context.Context, query string, args ...any) (driver.Result, error)

	// Query executes a SQL statement that returns rows.
	Query(ctx context.Context, query string, args ...any) (driver.Rows, error)

	// EnsureMigrationTable creates the migration tracking table if it doesn't exist.
	EnsureMigrationTable(ctx context.Context) error

	// EnsureLockTable creates the migration lock table if it doesn't exist.
	EnsureLockTable(ctx context.Context) error

	// AcquireLock attempts to acquire the distributed migration lock.
	// Returns an error if the lock is held by another process.
	AcquireLock(ctx context.Context, lockedBy string) error

	// ReleaseLock releases the distributed migration lock.
	ReleaseLock(ctx context.Context) error

	// ListApplied returns all migrations that have been applied.
	ListApplied(ctx context.Context) ([]*AppliedMigration, error)

	// RecordApplied records that a migration was successfully applied.
	RecordApplied(ctx context.Context, m *Migration) error

	// RemoveApplied removes the record of an applied migration (for rollback).
	RemoveApplied(ctx context.Context, m *Migration) error
}

Executor is the interface that driver-specific migration executors implement. It provides methods for running DDL/DML within migrations and managing the migration version table and lock.

func NewExecutorFor

func NewExecutorFor(drv any) (Executor, error)

NewExecutorFor creates a migration Executor for the given driver using the registered factory. The driver must implement a Name() string method (satisfied by both grove.GroveDriver and driver.Driver). Returns an error if no factory is registered for the driver's name.

type ExecutorFactory

type ExecutorFactory func(drv any) Executor

ExecutorFactory is a function that creates a migration Executor from a driver. The driver parameter is typed as any to support both SQL-based drivers (driver.Driver) and non-SQL drivers (e.g., *mongodriver.MongoDB). Each factory performs its own type assertion on the driver.

type Group

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

Group represents a collection of migrations owned by a module or extension. Each group has a unique name (e.g., "core", "forge.billing") and can declare dependencies on other groups.

func NewGroup

func NewGroup(name string, opts ...GroupOption) *Group

NewGroup creates a new migration group with the given name and options.

var Migrations = migrate.NewGroup("core")
var Migrations = migrate.NewGroup("forge.billing", migrate.DependsOn("core"))

func (*Group) DependsOnGroups

func (g *Group) DependsOnGroups() []string

DependsOnGroups returns the list of group names this group depends on.

func (*Group) Migrations

func (g *Group) Migrations() []*Migration

Migrations returns a copy of the group's migrations sorted by version.

func (*Group) MustRegister

func (g *Group) MustRegister(migrations ...*Migration)

MustRegister is like Register but panics on error.

func (*Group) Name

func (g *Group) Name() string

Name returns the group name.

func (*Group) Register

func (g *Group) Register(migrations ...*Migration) error

Register adds migrations to the group. The group name is automatically set on each migration. Returns an error if any migration has a duplicate version within this group.

type GroupOption

type GroupOption func(*Group)

GroupOption configures a migration group.

func DependsOn

func DependsOn(groups ...string) GroupOption

DependsOn declares that this group's migrations must run after the specified groups have completed.

type GroupStatus

type GroupStatus struct {
	Name    string
	Applied []*MigrationStatus
	Pending []*MigrationStatus
}

GroupStatus describes the state of all migrations in a group.

type LockInfo

type LockInfo struct {
	Held     bool
	LockedBy string
	LockedAt string
}

LockInfo describes the current migration lock state.

type LockInspector

type LockInspector interface {
	LockInfo(ctx context.Context) (*LockInfo, error)
}

LockInspector is an optional capability: executors that can report who holds the migration lock implement it, letting the orchestrator produce a diagnosable error when the wait budget is exhausted.

type MigrateFunc

type MigrateFunc func(ctx context.Context, exec Executor) error //nolint:revive // MigrateFunc is the established public API name

MigrateFunc is a function that performs a migration step. It receives a context and an Executor for running DDL/DML statements.

type MigrateResult

type MigrateResult struct {
	Applied  []*Migration // Migrations that were applied
	Rollback []*Migration // Migrations that were rolled back (for Rollback only)
}

MigrateResult holds the result of a Migrate or Rollback operation.

type Migration

type Migration struct {
	// Name is a human-readable identifier (e.g., "create_users").
	Name string

	// Version is a timestamp-based version string (e.g., "20240115120000").
	// Migrations run in version order within dependency constraints.
	Version string

	// Group identifies the module/extension that owns this migration.
	// Set automatically when registered with a Group.
	Group string

	// Up runs the forward migration.
	Up MigrateFunc

	// Down runs the rollback.
	Down MigrateFunc

	// Comment is an optional description.
	Comment string
}

Migration represents a single versioned migration.

type MigrationRegistry

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

MigrationRegistry holds registered migration groups.

func NewMigrationRegistry

func NewMigrationRegistry() *MigrationRegistry

NewMigrationRegistry creates an empty MigrationRegistry.

func (*MigrationRegistry) Groups

func (r *MigrationRegistry) Groups() []*Group

Groups returns a copy of all registered migration groups.

func (*MigrationRegistry) Register

func (r *MigrationRegistry) Register(groups ...*Group)

Register adds one or more migration groups to the registry.

type MigrationStatus

type MigrationStatus struct {
	Migration *Migration
	Applied   bool
	AppliedAt string // empty if not applied
}

MigrationStatus describes the state of a single migration.

type Orchestrator

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

Orchestrator manages migration execution across multiple groups.

func NewOrchestrator

func NewOrchestrator(executor Executor, groups ...*Group) *Orchestrator

NewOrchestrator creates a new migration orchestrator.

func (*Orchestrator) Migrate

func (o *Orchestrator) Migrate(ctx context.Context) (*MigrateResult, error)

Migrate runs all pending migrations in dependency-resolved order.

Steps:

  1. Ensure migration and lock tables exist
  2. Acquire distributed lock
  3. Load already-applied migrations
  4. Topologically sort groups by dependencies
  5. Execute pending migrations in order
  6. Release lock

func (*Orchestrator) Rollback

func (o *Orchestrator) Rollback(ctx context.Context) (*MigrateResult, error)

Rollback rolls back the last batch of applied migrations (one per group, most recently applied first).

func (*Orchestrator) SetLockTimeout

func (o *Orchestrator) SetLockTimeout(d time.Duration) *Orchestrator

SetLockTimeout overrides the lock-wait budget. 0 = wait until ctx deadline.

func (*Orchestrator) Status

func (o *Orchestrator) Status(ctx context.Context) ([]*GroupStatus, error)

Status returns the status of all migrations across all groups.

Jump to

Keyboard shortcuts

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