migrations

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Jun 9, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

README

The Migrations README

This document collects notes around migrations, and the choices we've had in the process.

version notes
v0.1.0 first release with migrations, initial setup and some additions
v0.2.0 minor changes
v0.3.0 multi-tenancy, named constraints

The early days (pre-v0.1.0)

Initially, all we had was the sqlTable array, and it was applied on startup. That works fine with in-memory SQLite instances, or with fresh PG/MySQL installs, but not wouldn't be a workable approach if someone deploys OCP (say, in K8s), and tries to update it without throwing away all database contents. So we've introduced a migrations framework: https://github.com/golang-migrate/migrate

We're still using the sqlTable array, but it's rendered into an in-memory fs.FS for the database dialect that is needed.

Smaller migrations are put into fs.FS instances with files containing just single SQL statements.

Changes to the migrations that are contained in released versions (v0.1.0 onwards) are to be handled with care, since they would only run on new installs, not on upgrades.

Multi-Tenancy

When we decided that using unique human-friendly names for our entities was a problem for a likely multi-tenant future of OCP, we went all-in and

  1. made all relation tables (bundles_requirements etc) refer to their relatees via id (not name),
  2. referring to a tenants table via tenant_id,
  3. re-created the entity tables (like bundles) without uniquness on names,
  4. but with uniqueness on (tenant_id, name).

So far, only a "default" tenant exists. It's ID depends on the database system, because we're using auto-incrementing integers, and MySQL starts with 1 while PG uses 0 (or vice-versa).

The migration for this is pretty huge: based on a second array of sqlTable instances, all old contents are moved into the new tables. This is because we couldn't predict constraint names across all three supported databases.

CockroachDB, named constraints

When we added support for CockroachDB, the constraint name problem came up again! The approach taken for the huge multi-tenant migration, i.e.

  1. RENAME existing -> old,
  2. CREATE new,
  3. COPY old -> new,
  4. REMOVE old

would not work for CockroachDB because of its special ways to handle schema changes. The only robust way to do it was to drop and recreate the constraints, which we -- see earlier -- can't do uniformly across all our database systems.

As a way out, the introduction of CockroachDB in our migrations took a special entry: It's "fast forwarding" -- since all tables are recreated in the multi-tenant change (see above), a CockroachDB migration run will SKIP anything before that, and only create the latest iteration of our tables as we want them to be. This required creating a table just for CRDB, "tokens", the only table not migrated for multi-tenancy.

To ensure that we're not painting ourselves into a corner, the migrations have been adapted in such a way that going forward, all constraints have names we control.

Documentation

Overview

Package migrations is for database migrations! This is not related to the `opactl migrate` command at all.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Migrations

func Migrations(dialect string) (fs.FS, error)

func Var

func Var(fs *pflag.FlagSet, yes *bool)

Types

type Migrator

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

func New

func New() *Migrator

func (*Migrator) Run

func (m *Migrator) Run(ctx context.Context) (*database.Database, error)

func (*Migrator) WithAuthorizer added in v0.6.0

func (m *Migrator) WithAuthorizer(a ext_authz.Authorizer) *Migrator

func (*Migrator) WithConfig

func (m *Migrator) WithConfig(db *config.Database) *Migrator

func (*Migrator) WithLogger

func (m *Migrator) WithLogger(log *logging.Logger) *Migrator

func (*Migrator) WithMigrate

func (m *Migrator) WithMigrate(yes bool) *Migrator

WithMigrate causes migrations to be applied. Without this, we will log (INFO) the number of pending migrations only.

Jump to

Keyboard shortcuts

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