Миграции и контроль версий БД с помощью Flyway

Feb 21, 2020 07:57 · 847 words · 4 minute read flyway postgres docker

Процесс преобразования одной структуры базы данных в другой без потери косистентности, при котором меняется схема (таблицы и их колонки, индексы и пр.) называется миграцией. В данной статье рассмотрим использование инструмента flyway для работы с миграциями в БД postgres - давайте разберемся!

Можно ли обойтись без миграций? Да, если это:

  • совсем маленький pet-проект;
  • stateless приложение;
  • данные не важны и могут быть легко удалены/восстановлены;
  • приложение не развивается и/или не меняет структуру хранения данных.

В остальных случаях стоит использовать миграции - хотя бы исходя из правил “хорошего тона”.

В терминогии flyway любые изменения в базе данных называются миграциями. Миграции могут быть версионированными (versioned) и повторяемыми (repeatable).

Версионированные миграции имеют версию (очевидно же), описание и контрольную сумму. Версия должна быть уникальна, а описание достаточно информативным - при виде описания вы должны понимать, для чего предназначена эта миграция на самом деле. Контрольная сумма используется для обнаружения (и блокирования) случайных изменений. Версионнированные миграции - это наиболее частый тип миграций (их мы и будем подробно рассматривать), которые применяются к БД единожды.

Повторяемые миграции имеют описание и контрольную сумму, но у них нет версии. Таким образом, вместо того, чтобы применяться единожды, эти миграции будут переприменяться каждый раз, когда меняется их контрольная сумма. В процессе применения миграций, повторяемые миграции будут выполняться ПОСЛЕ всех версионированных и в алфавитном порядке их описания.

По умолчанию, миграции могут быть написаны на Java или SQL (наш случай) и содержать в себе несколько утверждений. Для контроля работы с миграциями (какие миграции уже применены, кем и когда), утилита flyway добавляет в базу данных таблицу flyway_schema_history.

Пример версионированной миграции:

CREATE TABLE hero_data.hero
(
    id BIGSERIAL NOT NULL,
    name VARCHAR(250) NOT NULL,
    description TEXT NOT NULL,
    debut_year INT NOT NULL,
    appearances INT NOT NULL,
    special_powers INT NOT NULL,
    cunning INT NOT NULL,
    strength INT NOT NULL,
    technology INT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL,
    updated_at TIMESTAMPTZ NOT NULL
);

ALTER TABLE hero_data.hero ADD CONSTRAINT pk_hero_id PRIMARY KEY (id);

Пример повторяемой миграции:

CREATE OR REPLACE VIEW blue_cars AS 
    SELECT id, license_plate FROM cars WHERE color='blue';

Как правило, миграции проще всего писать в формате SQL - так легче начать работу, использовать существующие скрипты/навыки/инструменты. Опять же, такой подход сразу дает доступ ко всем возможностям используемой базы данных и не требует понимания “промежуточных” уровней транслирования sql-выражений. SQL-миграции хорошо подходят для:

  • DDL-изменений (CREATE/ALTER/DROP утверждения для TABLES,VIEWS,TRIGGERS,SEQUENCES,…);
  • Несложные изменения справочных данных (CRUD in reference data tables);
  • Несложные и массовые изменения данных (CRUD in regular data tables).

SQL-миграции должны соответствовать определенному шаблону именования для того, чтобы flyway мог работать с ними. Имя файла должно содержать:

  • Префикс: V для версионированных и R для повторяемых миграций (можно изменить);
  • Версию: Версия может содержать точки и/или нижние подчеркивания (только не для повторяемых миграций). Я бы рекомендовал использовать для версии timestamp в формате 20200214141830. Чтобы получить отформатированный timestamp выполните команду date '+%Y%m%d%H%M%S';
  • Разделитель: __ (два нижних подчеркивания, можно изменить);
  • Описание: слова разделяются пробелами или нижними подчеркиваниями;
  • Суффикс: .sql (настраивается).

Пример правильно названного файла с миграциями - V20200214141830__Create_hero_schema.sql.

Миграции могут быть применены ко всем необходимым базам данных, но для простоты рассмотрим пример работы только с одной БД.

Все взаимодействие утилиты flyway с БД будет осуществляться из docker-контейнера, дабы не захламлять систему. Для сборки образа воспользуемся следующим Dockerfile:

FROM adoptopenjdk/openjdk13:jdk-13.0.2_8-ubuntu-slim

LABEL maintainer="Yevhen Lebid <ealebed@gmail.com>"

WORKDIR /flyway

ARG FLYWAY_VERSION
ENV FLYWAY_VERSION=${FLYWAY_VERSION}

RUN apt-get update && \
    apt-get install -y \
      curl && \
    apt-get -y autoremove && \
    apt-get -y clean && \
    rm -rf /var/lib/apt/lists/*

RUN curl -L https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/${FLYWAY_VERSION}/flyway-commandline-${FLYWAY_VERSION}.tar.gz -o flyway-commandline-${FLYWAY_VERSION}.tar.gz \
  && tar -xzf flyway-commandline-${FLYWAY_VERSION}.tar.gz --strip-components=1 \
  && rm flyway-commandline-${FLYWAY_VERSION}.tar.gz \
  && ln -s /flyway/flyway /usr/local/bin/flyway

Соберем образ flyway следующей командой:

docker build \
  -f Dockerfile \
  --build-arg FLYWAY_VERSION=6.2.4 \
  -t ealebed/flyway:1.0.0 .

Предположим, что у нас уже существует БД. Чтобы начать с ней работать используя миграции, необходимо:

  • создать sql-скрипт, содержащий текущее состоянии БД (DDL, Data Definition Language) со всеми индексами, триггерами, процедурами и т.д. (например, используя pg_dump -s ...);
  • создать версионированную миграцию из данного скрипта, используя корректную версию и описание (например, V20190814141830__Base_version.sql);
  • назначить (зафиксировать) базе данных базовую версию, используя версию и описание с предыдущего шага. Чтобы выполнить данную операцию в docker-контейнере запустите:
docker run \
  --rm
  -e FLYWAY_USER=${FLYWAY_USER} \
  -e FLYWAY_PASSWORD=${FLYWAY_PASSWORD} \
  -e FLYWAY_URL=${FLYWAY_URL} \
  ealebed/flyway:1.0.0 flyway baseline -baselineVersion=20190814141830 -baselineDescription="Base version"

Здесь ${FLYWAY_USER}, ${FLYWAY_PASSWORD} и ${FLYWAY_URL} - необходимые параметры для авторизованного подключения к интересующей вас БД. Полный список параметров можно найти здесь.

Теперь можно приступать к написанию миграций. Если вы работаете в IntelliJ IDEA, то рекомендую использовать плагин Flyway Migration Creation.

Для того, чтобы применить одну или несколько миграций (помним о версионировании), необходимо смонтировать внутрь docker-контейнера каталог с sql-миграциями и выполнить команду migrate:

docker run \
  --rm \
  -v <path_to_database_directory>:/flyway/sql \
  -e FLYWAY_USER=${FLYWAY_USER} \
  -e FLYWAY_PASSWORD=${FLYWAY_PASSWORD} \
  -e FLYWAY_URL=${FLYWAY_URL} \
  ealebed/flyway:1.0.0 flyway migrate

Просмотреть детальную информацию и статус всех миграций можно с помощью команды info:

docker run \
  --rm \
  -e FLYWAY_USER=${FLYWAY_USER} \
  -e FLYWAY_PASSWORD=${FLYWAY_PASSWORD} \
  -e FLYWAY_URL=${FLYWAY_URL} \
  ealebed/flyway:1.0.0 flyway info

Стоит отметить, что в платной версии flyway есть возможность выполнять проверку применения миграций с помощью так называемых Dry Runs - вещь очень полезная (иногда и жизненно необходимая). В одной из следующих статей я расскажу как можно организовать аналогичные проверки и валидацию миграций еще до их применения к БД, причем совершенно бесплатно, без смс и регистрации!

tweet Share