migrate icon indicating copy to clipboard operation
migrate copied to clipboard

Check if migration is necessary

Open olivere opened this issue 7 years ago • 2 comments

What is the correct way of testing whether a migration is necessary? Some applications might want to exit instead of auto-migrate if the schema isn't up to date.

Also: Thanks for this package. We are planning to introduce it as a replacement for a home-baked solution.

olivere avatar May 10 '17 08:05 olivere

It's not possible right now, but I think it would be a great feature addition.

mattes avatar May 10 '17 17:05 mattes

We recently needed to do something similar. We use go-bindata to embed the migrations into the executable and try to write backwards compatible migrations to avoid issues with older code running during a deployment.

We found that it's relatively simple to check if a migration is required. First, get the current migration version and then try to get the next migration.

We terminate the application if the running commit has the latest migration and the database is dirty as we can assume this migration version previously failed.

if the database has a newer migration version, we continue in the hope that we made the later migrations backwards compatible ;) (usually the code is only 1 migration behind during a deployment, we just prefer not to turn the application completely off as we deploy)

func IsMigrationRequired(s source.Driver, m *migrate.Migrate) (required bool, dirty bool, err error) {
	version, dirty, err := m.Version()
	if err != nil {
		return false, false, errors.Wrap(err, "error getting current migration version")
	}

	next, err := s.Next(version)
	if os.IsNotExist(err) {
		// no up migrations exist for the current database version
		return false, dirty, nil
	}
	if err != nil {
		return false, dirty, errors.Wrap(err, "error getting next migration")
	}

	// failed migrations leave the dirty flag set. if the running commit has the
	// latest migration, the likelihood of the code not failing is... undetermined.
	// we'll want to exit early, let the deploy pause and continue running on old
	// code until it's fixed. old code doesn't have the latest migration, so it
	// won't think a migration is required
	required = (next > version) || (next == version && dirty)
	return required, dirty, nil
}

jon-walton avatar Aug 18 '17 15:08 jon-walton