diff --git a/migrate/README.md b/migrate/README.md index 1a6faf5..69a1f61 100644 --- a/migrate/README.md +++ b/migrate/README.md @@ -5,6 +5,14 @@ It returns `*goose.Provider` directly: Goose owns execution, version tracking, status, and errors. There is no lifecycle dependency or background work. +Consumers must create migration files with Goose's +[`create` command](https://pressly.github.io/goose/documentation/cli-commands/#create). +For timestamp-to-sequential reconciliation before deployment, use its +[`fix` command](https://pressly.github.io/goose/documentation/cli-commands/#fix) +as described in [hybrid versioning](https://github.com/pressly/goose#hybrid-versioning). +These commands edit source files before embedding; `fix` does not update database +history. Never renumber migrations already applied to shared databases. + Embed migrations in the application's binary and run them before serving traffic: ```go @@ -40,65 +48,28 @@ not defer it when sharing the pool with the application. Use `"."` for SQL files at the embedded root. Files in the selected directory are discovered directly, without recursively searching subdirectories. -Use unique positive versions and Goose's migration creator naming convention: -zero-padded sequential names such as `00007_users.sql` and `00010_events.sql`, or -fixed-width timestamps such as `20261027114500_create_users.sql`. Filenames must -sort lexically in numeric version order, including when mixing naming schemes; -choose padding wide enough for the complete history. Goose v3.29.0 preserves -lexical source order for SQL-only providers, so arbitrary unpadded names such as -`7_users.sql` and `10_events.sql` are unsupported by this house convention: they -can produce incorrect latest versions and source/status ordering. This package -does not rename or normalize migration files. +Preserve Goose's creator/fix filenames and ship the complete migration history. +The pinned Goose SQL-only provider relies on lexical filename order matching +numeric version order. Timestamp versions and gaps are supported; never derive +versions from file counts. For operations, errors, and SQL annotations, use the +[Provider API](https://pkg.go.dev/github.com/pressly/goose/v3#Provider) and +[SQL migration docs](https://pressly.github.io/goose/documentation/annotations/). -Never derive a version from a file count or add one to the current version. -Keep applied migrations immutable and ship the complete history. Goose rejects -newly introduced migrations below the current version by default. - -```sql --- +goose Up -CREATE TABLE users (id BIGINT PRIMARY KEY); - --- +goose Down -DROP TABLE users; -``` - -Goose runs each migration in a transaction by default. Its annotations support -statement blocks and explicit `NO TRANSACTION` where required; such migrations -and nontransactional database DDL can leave partial effects on failure. Down -migrations execute application-authored SQL and may delete data. - -| Operation | Provider method | -| --- | --- | -| Apply all pending migrations | `Up(ctx)` | -| Apply pending versions through a target, inclusive | `UpTo(ctx, version)` | -| Roll back versions above a target; zero removes all | `DownTo(ctx, version)` | -| Inspect each migration's state | `Status(ctx)` | -| Current database version | `GetDBVersion(ctx)` | -| Current and latest available versions | `GetVersions(ctx)` | -| Detect pending migrations | `HasPending(ctx)` | - -Targets are numeric version bounds, so gaps and timestamps work without special -handling. Up on a fully migrated database succeeds with no results. Operations -may create Goose's `goose_db_version` table even when only inspecting state. -Execution errors retain Goose's details; use `errors.As` with -`*goose.PartialError` to inspect applied and failed migrations. Transactional -failure leaves the database at the last committed migration version. Operation -logs use the injected slog logger; callers handle and log returned errors. - -For Go functions, pass migrations created with `goose.NewGoMigration(version, -up, down)` as the trailing arguments to `migrate.New`. Use `goose.GoFunc.RunTx` -for transactional work or `RunDB` for work requiring its own connections. Each -provider should receive its own migration instances. Versions must be unique -across SQL and Go migrations. Global registration is disabled; no `init` or -`goose.AddMigration` calls are needed. +For Go functions, use Goose's +[provider-local registration](https://pressly.github.io/goose/documentation/provider/#withgomigrations): +pass fresh `goose.NewGoMigration` values as the trailing arguments to `migrate.New`. +The creator's default Go template instead calls `goose.AddMigrationContext` from +`init`; replace that registration with provider-local construction. Global +registration is disabled to keep each provider's migration set isolated and avoid +import side effects. If `fix` renames Go migrations, update their explicit +registration versions to match. Versions must be unique across SQL and Go files. Postgres providers automatically use Goose's default session advisory locker. Concurrent startup calls from separate providers/processes serialize migration execution and recheck applied versions under the lock. The default lock ID serializes Goose migrations throughout the same database, including different -schemas; lock acquisition retries for about five minutes and unlock retries for -about one minute, subject to the operation context. Use a direct connection or -a session-pooling proxy: transaction pooling cannot preserve session locks. +schemas. Use a direct connection or a session-pooling proxy: transaction pooling +cannot preserve session locks. `RunDB` Go migrations need spare pool connections while the locked session is held; avoid a one-connection pool for them. diff --git a/migrate/migrate.go b/migrate/migrate.go index ca99821..a0778c2 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -24,9 +24,9 @@ import ( // affinity (transaction-pooling proxies cannot safely hold session locks). // // Use the returned Provider's Up, UpTo, DownTo, Status, GetDBVersion, GetVersions, -// and HasPending methods directly. Filenames must sort lexically in numeric -// version order: use zero-padded sequential or fixed-width timestamp prefixes, -// as produced by Goose's migration creator. Global Go registration is ignored. +// and HasPending methods directly. Create migration files with Goose's create +// command; lexical filename order must match numeric version order. Go migrations +// are registered per provider; global Go registration is ignored. func New(db *sql.DB, dialect goose.Dialect, files embed.FS, dir string, logger *slog.Logger, migrations ...*goose.Migration) (*goose.Provider, error) { for i, migration := range migrations { if migration == nil {