Skip to main content

How to Execute Database Migrations using Go Migrate

A database migration, also known as a schema migration, is a set of changes to be made to a structure of objects within a relational database 😏.

The process of migration helps to change the database schema from its current state to a new desired state, whether it involves adding tables and columns, removing elements, splitting fields, or changing types and constraints.

By managing these changes in a programmatic way, it becomes easier to maintain consistency and accuracy in the database, as well as keep track of the history of modifications made to it.

Setup and Installation

migrate is a CLI tool that you can use to run migrations. You can easily install it on various operating systems such as Linux, Mac and Windows by using package managers like curl, brew, and scoop, respectively.

For more information on how to install and use the tool, you can refer to the official documentation.

To install the migrate CLI tool using scoop on Windows, you can follow these steps:
$ scoop install migrate
To install the migrate CLI tool using curl on Linux, you can follow these steps:
$ curl -L https://packagecloud.io/golang-migrate/migrate/gpgkey| apt-key add - 
$ echo "deb https://packagecloud.io/golang-migrate/migrate/ubuntu/ $ (lsb_release -sc) main" > /etc/apt/sources.list.d/migrate.list
$ apt-get update
$ apt-get install -y migrate
To install the migrate CLI tool using on Mac, you can follow these steps:
$ brew install golang-migrate


How to Create a New Migration

Create a folder like database/migration to store all the migration files.
Migrate gives us several commands:
The first one is create, which we can use to create new migration files.
The 2nd one is goto, which will migrate the schema to a specific version.
Then the up or down commands to apply all or N up or down migrations.


Next, create migration files using the following command:
migrate create -ext sql -dir database/migration/ -seq init_mg
You use -seq to generate a sequential version and init_mg is the name of the migration.


A migration typically consists of two distinct files, one for moving the database to a new state (referred to as "up") and another for reverting the changes made to the previous state (referred to as "down").

The "up" file is used to implement the desired changes to the database, while the "down" file is used to undo those changes and return the database to its previous state.

The format of those files for SQL are:
{version}_{title}.down.sql 
{version}_{title}.up.sql
When you create migration files, they will be empty by default. To implement the changes you want, you will need to fill them with the appropriate SQL queries.


How to Run Migration Up

In order to execute the SQL statements in the migration files, migrate requires a valid connection to a Postgres database.
To accomplish this, you will need to provide a connection string in the proper format.
$ migrate -path database/migration/ -database "postgresql://username:secretkey@localhost:5432/database_name?sslmode=disable" -verbose up
Now in your Postgres shell, you can check newly created tables by using the following commands:
\d+
 \d+ table_name DESCRIBE TABLE


How to Rollback Migrations

If you want to revert back the migration, you can do that using the following down tag:
$ migrate -path database/migration/ -database "postgresql://username:secretkey@localhost:5432/database_name?sslmode=disable" -verbose down

It will delete the email column from both tables as mentioned in the 000002_init_mg.up.sql file.
Now, let's check the database and see if email has been deleted or not:


How to Resolve Migration Errors

If a migration contains an error and is executed, migrate will prevent any further migrations from being run on the same database.

An error message like Dirty database version 1. Fix and force version will be displayed, even after the error in the migration is fixed. This indicates that the database is "dirty" and needs to be investigated.

It is necessary to determine if the migration was applied partially or not at all. Once you've determined this, the database version should be forced to reflect its true state using the force command.
$ migrate -path database/migration/ -database "postgresql://username:secretkey@localhost:5432/database_name?sslmode=disable" force <VERSION>

How to Add Commands in a Makefile : 

migration_up: migrate -path database/migration/ -database "postgresql://username:secretkey@localhost:5432/database_name?sslmode=disable" -verbose up
migration_down: migrate -path database/migration/ -database "postgresql://username:secretkey@localhost:5432/database_name?sslmode=disable" -verbose down 
migration_fix: migrate -path database/migration/ -database "postgresql://username:secretkey@localhost:5432/database_name?sslmode=disable" force VERSION

Now, you can run $ make migration_up for 'up', $ make migration_down for 'down', and $ make migration_fix to fix the migration issue.

Before running the makefile, ensure that the correct version number is included in the migration_fix command.

MySQL

mysql://user:password@tcp(host:port)/dbname?query

URL QueryWithInstance ConfigDescription
x-migrations-tableMigrationsTableName of the migrations table
x-no-lockNoLockSet true to skip GET_LOCK/RELEASE_LOCK statements.Useful for multi-master MySQL flavors. Only run migrations from one host when this is enabled.
x-statement-timeoutStatementTimeoutAbort any statement that takes more than the specified number of milliseconds, functionally similar to Server-side SELECT statement timeouts but enforced by the client. Available for all versions of MySQL, not just >=5.7.
dbnameDatabaseNameThe name of the database to connect to
user The user to sign in as
passwordThe user's password
hostThe host to connect to.


Cassandra

cassandra://host:port/keyspace?param1=value&param2=value2

URL QueryDefault value Description
x-migrations-tableschema_migrationsName of the migrations table
x-multi-statementfalseEnable multiple statements to be ran in a single migration (See note above)
port9042The port to bind to
consistencyALLMigration consistency
protocol Cassandra protocol version (3 or 4)
timeout1 minuteMigration timeout
connect-timeout 600ms Initial connection timeout to the cluster
usernamenilUsername to use when authenticating.
passwordnilPassword to use when authenticating.
sslcert
Cert file location. The file must contain PEM encoded data.
sslkey
Key file location. The file must contain PEM encoded data.
sslrootcert
The location of the root certificate file. The file must contain PEM encoded data.
sslmode
Whether or not to use SSL (disable|require|verify-ca|verify-full)
disable-host-lookupfalseDisable initial host lookup.


MongoDB

mongodb://user:password@host:port/dbname?query
URL QueryWithInstance ConfigDescription
x-migrations-collectionMigrationsCollectionName of the migrations collection
x-transaction-modeTransactionModeIf set to true wrap commands in transaction. Available
only for replica set. Driver is using strconv.ParseBool for parsing
x-advisory-lockingtrueFeature flag for advisory locking, if set to false disable
advisory locking
x-advisory-lock-collectionmigrate_advisory_lockThe name of the collection to use for advisory locking.
x-advisory-lock-timeout15The max time in seconds that migrate will wait to
acquire a lock before failing.
x-advisory-lock-timeout-interval10The max time in seconds between attempts to acquire the advisory lock the
lock is attempted to be acquired using an exponential backoff algorithm.
dbnameDatabaseNameThe name of the database to connect to
userThe user to sign in as. Can be omitted
passwordThe user's password. Can be omitted
hostThe host to connect to
portThe port to bind to


sqlite

sqlite://path/to/database?query
URL QueryWithInstance ConfigDescription
x-migrations-tableMigrationsTableName of the migrations table. Defaults to schema_migrations.
x-no-tx-wrapNoTxWrapDisable implicit transactions when true.
Migrations may and should, contain explicit BEGIN and COMMIT statements.


neo4j

neo4j://user:password@host:port/
URL Query WithInstance Config Description
x-multi-statementMultiStatementEnable multiple statements to be ran in a single migration (See note above)
user Contained within AuthConfigThe user to sign in as
passwordContained within AuthConfigThe user's password
hostThe host to connect to. Values that start with / are for unix domain sockets. (default is localhost)
portThe port to bind to. (default is 7687)
MigrationsLabelName of the migrations node label

Comments

Popular posts from this blog

Java Currency Formatter Solution

Given a  double-precision  number,  , denoting an amount of money, use the  NumberFormat  class'  getCurrencyInstance  method to convert   into the US, Indian, Chinese, and French currency formats. Then print the formatted values as follows: US: formattedPayment India: formattedPayment China: formattedPayment France: formattedPayment where   is   formatted according to the appropriate  Locale 's currency. Note:  India does not have a built-in Locale, so you must  construct one  where the language is  en  (i.e., English). Input Format A single double-precision number denoting  . Constraints Output Format On the first line, print  US: u  where   is   formatted for US currency.  On the second line, print  India: i  where   is   formatted for Indian currency.  On the third line...

Java Loops II print each element of our series as a single line of space-separated values.

We use the integers  ,  , and   to create the following series: You are given   queries in the form of  ,  , and  . For each query, print the series corresponding to the given  ,  , and   values as a single line of   space-separated integers. Input Format The first line contains an integer,  , denoting the number of queries.  Each line   of the   subsequent lines contains three space-separated integers describing the respective  ,  , and   values for that query. Constraints Output Format For each query, print the corresponding series on a new line. Each series must be printed in order as a single line of   space-separated integers. Sample Input 2 0 2 10 5 3 5 Sample Output 2 6 14 30 62 126 254 510 1022 2046 8 14 26 50 98 Explanation We have two queries: We use  ...

Java Static Initializer Block

Static initialization blocks are executed when the class is loaded, and you can initialize static variables in those blocks. It's time to test your knowledge of  Static initialization blocks . You can read about it  here. You are given a class  Solution  with a  main  method. Complete the given code so that it outputs the area of a parallelogram with breadth   and height  . You should read the variables from the standard input. If   or    , the output should be  "java.lang.Exception: Breadth and height must be positive"  without quotes. Input Format There are two lines of input. The first line contains  : the breadth of the parallelogram. The next line contains  : the height of the parallelogram. Constraints Output Format If both values are greater than zero, then the  main  method must output the area of the  parallelogram . Otherwise, pri...