All Projects → ThomWright → Postgres Migrations

ThomWright / Postgres Migrations

Licence: mit
🐦 A Stack Overflow-inspired PostgreSQL migration library with strict ordering and immutable migrations

Programming Languages

javascript
184084 projects - #8 most used programming language
typescript
32286 projects
js
455 projects

Projects that are alternatives of or similar to Postgres Migrations

Pg chameleon
MySQL to PostgreSQL replica system
Stars: ✭ 274 (+70.19%)
Mutual labels:  database-migrations, database, postgresql, postgres
Node Pg Migrate
Node.js database migration management for Postgresql
Stars: ✭ 838 (+420.5%)
Mutual labels:  migrations, database, postgresql, postgres
Goose
A database migration tool. Supports SQL migrations and Go functions.
Stars: ✭ 2,112 (+1211.8%)
Mutual labels:  database, postgres, migrations, database-migrations
Prisma
Next-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite & MongoDB (Preview)
Stars: ✭ 18,168 (+11184.47%)
Mutual labels:  database, postgresql, postgres
Postgres Meta
A RESTful API for managing your Postgres. Fetch tables, add roles, and run queries
Stars: ✭ 146 (-9.32%)
Mutual labels:  database, postgresql, postgres
Plv8
V8 Engine Javascript Procedural Language add-on for PostgreSQL
Stars: ✭ 1,195 (+642.24%)
Mutual labels:  database, postgresql, postgres
Niklick
Rails Versioned API solution template for hipsters! (Ruby, Ruby on Rails, REST API, GraphQL, Docker, RSpec, Devise, Postgress DB)
Stars: ✭ 39 (-75.78%)
Mutual labels:  database, postgresql, postgres
Activerecord Clean Db Structure
Automatic cleanup for the Rails db/structure.sql file (ActiveRecord/PostgreSQL)
Stars: ✭ 101 (-37.27%)
Mutual labels:  database, postgresql, postgres
Lol dba
lol_dba is a small package of rake tasks that scan your application models and displays a list of columns that probably should be indexed. Also, it can generate .sql migration scripts.
Stars: ✭ 1,363 (+746.58%)
Mutual labels:  migrations, database-migrations, database
Pg flame
A flamegraph generator for Postgres EXPLAIN ANALYZE output.
Stars: ✭ 1,391 (+763.98%)
Mutual labels:  database, postgresql, postgres
Dbmate
🚀 A lightweight, framework-agnostic database migration tool.
Stars: ✭ 2,228 (+1283.85%)
Mutual labels:  migrations, database-migrations, postgresql
Rpg Boilerplate
Relay (React), Postgres, and Graphile (GraphQL): A Modern Frontend and API Boilerplate
Stars: ✭ 62 (-61.49%)
Mutual labels:  migrations, postgresql, postgres
Squid
🦑 Provides SQL tagged template strings and schema definition functions.
Stars: ✭ 57 (-64.6%)
Mutual labels:  database, postgresql, postgres
Electrocrud
Database CRUD Application Built on Electron | MySQL, Postgres, SQLite
Stars: ✭ 1,267 (+686.96%)
Mutual labels:  database, postgresql, postgres
Postgresclientkit
A PostgreSQL client library for Swift. Does not require libpq.
Stars: ✭ 49 (-69.57%)
Mutual labels:  database, postgresql, postgres
Pgcli
Postgres CLI with autocompletion and syntax highlighting
Stars: ✭ 9,985 (+6101.86%)
Mutual labels:  database, postgresql, postgres
Ship Hold
data access framework for Postgresql on nodejs
Stars: ✭ 110 (-31.68%)
Mutual labels:  database, postgresql, postgres
Postgres Operator
Production PostgreSQL for Kubernetes, from high availability Postgres clusters to full-scale database-as-a-service.
Stars: ✭ 2,166 (+1245.34%)
Mutual labels:  database, postgresql, postgres
Awesome Postgres
A curated list of awesome PostgreSQL software, libraries, tools and resources, inspired by awesome-mysql
Stars: ✭ 7,468 (+4538.51%)
Mutual labels:  database, postgresql, postgres
Goqu
SQL builder and query library for golang
Stars: ✭ 984 (+511.18%)
Mutual labels:  database, postgresql, postgres

Postgres migrations

Travis npm David David

A PostgreSQL migration library inspired by the Stack Overflow system described in Nick Craver's blog.

Requires Node 10.17.0+

Supports PostgreSQL 9.4+

API

There are two ways to use the API.

Either, pass a database connection config object:

import {createDb, migrate} from "postgres-migrations"

async function() {
  const dbConfig = {
    database: "database-name",
    user: "postgres",
    password: "password",
    host: "localhost",
    port: 5432,
  }

  await createDb(databaseName, {
    ...dbConfig,
    defaultDatabase: "postgres", // defaults to "postgres"
  })
  await migrate(dbConfig, "path/to/migration/files")
}

Or, pass a pg client:

import {createDb, migrate} from "postgres-migrations"

async function() {
  const dbConfig = {
    database: "database-name",
    user: "postgres",
    password: "password",
    host: "localhost",
    port: 5432,
  }

  {
    const client = new pg.Client({
      ...dbConfig,
      database: "postgres",
    })
    await client.connect()
    try {
      await createDb(databaseName, {client})
    } finally {
      await client.end()
    }
  }

  {
    const client = new pg.Client(dbConfig) // or a Pool, or a PoolClient
    await client.connect()
    try {
      await migrate({client}, "path/to/migration/files")
    } finally {
      await client.end()
    }
  }
}

Validating migration files

Occasionally, if two people are working on the same codebase independently, they might both create a migration at the same time. For example, 5_add-table.sql and 5_add-column.sql. If these both get pushed, there will be a conflict.

While the migration system will notice this and refuse to apply the migrations, it can be useful to catch this as early as possible.

The loadMigrationFiles function can be used to check if the migration files satisfy the rules.

Alternatively, use the pg-validate-migrations bin script: pg-validate-migrations "path/to/migration/files".

Design decisions

No down migrations

There is deliberately no concept of a 'down' migration. In the words of Nick Craver:

If we needed to reverse something, we could just push another migration negating whatever we did that went boom ... Why roll back when you can roll forward?

Simple ordering

Migrations are guaranteed to run in the same order every time, on every system.

Some migration systems use timestamps for ordering migrations, where the timestamp represents when the migration file was created. This doesn't guarantee that the migrations will be run in the same order on every system.

For example, imagine Developer A creates a migration file in a branch. The next day, Developer B creates a migration in master, and deploys it to production. On day three Developer A merges in their branch and deploys to production.

The production database sees the migrations applied out of order with respect to their creation time. Any new development database will run the migrations in the timestamp order.

The migrations table

A migrations table is created as the first migration, before any user-supplied migrations. This keeps track of all the migrations which have already been run.

Hash checks for previous migrations

Previously run migration scripts shouldn't be modified, since we want the process to be repeated in the same way for every new environment.

This is enforced by hashing the file contents of a migration script and storing this in migrations table. Before running a migration, the previously run scripts are hashed and checked against the database to ensure they haven't changed.

Each migration run in a transaction

Ensures each migration is atomic. Either it completes successfully, or it is rolled back and the process is aborted.

An exception is made when -- postgres-migrations disable-transaction is included at the top of the migration file. This allows migrations such as CREATE INDEX CONCURRENTLY which cannot be run inside a transaction.

Abort on errors

If anything fails, the migration in progress is rolled back and an exception is thrown.

Concurrency

As of v4, advisory locks are used to control concurrency. If two migration runs are kicked off concurrently, one will wait for the other to finish before starting. Once a process has acquired a lock, it will run each of the pending migrations before releasing the lock again.

Logs from two processes A and B running concurrently should look something like the following.

B Connected to database
B Acquiring advisory lock...
A Connected to database
A Acquiring advisory lock...
B ... aquired advisory lock
B Starting migrations
B Starting migration: 2 migration-name
B Finished migration: 2 migration-name
B Starting migration: 3 another-migration-name
B Finished migration: 3 another-migration-name
B Successfully applied migrations: migration-name, another-migration-name
B Finished migrations
B Releasing advisory lock...
B ... released advisory lock
A ... aquired advisory lock
A Starting migrations
A No migrations applied
A Finished migrations
A Releasing advisory lock...
A ... released advisory lock

Warning: the use of advisory locks will cause problems when using transaction pooling or statement pooling in PgBouncer. A similar system is used in Rails, see this for an explanation of the problem.

Migration rules

Make migrations idempotent

Migrations should only be run once, but this is a good principle to follow regardless.

Migrations are immutable

Once applied (to production), a migration cannot be changed.

This is enforced by storing a hash of the file contents for each migration in the migrations table.

These hashes are checked when running migrations.

Migrations should be backwards compatible

Backwards incompatible changes can usually be made in a few stages.

For an example, see this blog post.

File name

A migration file must match the following pattern:

[id][separator][name][extension]

Section Accepted Values Description
id Any integer or left zero integers Consecutive integer ID.
Must start from 1 and be consecutive, e.g. if you have migrations 1-4, the next one must be 5.
separator _ or - or nothing
name Any length text
extension .sql or .js File extensions supported not case sensitive

Example:

migrations
├ 1_create-initial-tables.sql
├ 2-alter-initial-tables.SQL
└ 3-alter-initial-tables-again.js

Or, if you want better ordering in your filesystem:

migrations
├ 00001_create-initial-tables.sql
├ 00002-alter-initial-tables.sql
└ 00003_alter-initial-tables-again.js

Migrations will be performed in the order of the ids. If ids are not consecutive or if multiple migrations have the same id, the migration run will fail.

Note that file names cannot be changed later.

Javascript Migrations

By using .js extension on your migration file you gain access to all NodeJS features and only need to export a method called generateSql that returns a string literal like:

// ./migrations/helpers/create-main-table.js
module.exports = `
CREATE TABLE main (
    id int primary key
);`

// ./migrations/helpers/create-secondary-table.js
module.exports = `
CREATE TABLE secondary (
    id int primary key
);`

// ./migrations/1-init.js
const createMainTable = require("./create-main-table")
const createSecondaryTable = require("./create-secondary-table")

module.exports.generateSql = () => `${createMainTable}
${createSecondaryTable}`

Tips

If you want sane date handling, it is recommended you use the following code snippet to fix a node-postgres bug:

const pg = require("pg")

const parseDate = val =>
  val === null ? null : moment(val).format("YYYY-MM-DD")
const DATATYPE_DATE = 1082
pg.types.setTypeParser(DATATYPE_DATE, val => {
  return val === null ? null : parseDate(val)
})

Useful resources

Stack Overflow: How We Do Deployment - 2016 Edition (Database Migrations)

Database Migrations Done Right

Database versioning best practices

Developing postgres-migrations

The tests require Docker to be installed. It probably helps to docker pull postgres:9.4.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].