Использование Liquibase без головной боли. 10 советов из опыта реальной разработки

в 6:57, , рубрики: database migrations, java, tips and tricks, Администрирование баз данных, миграция бд, совет, метки: , , , ,

kdpvLiquibase — это система управления миграциями базы данных. Эта вторая статья о Liquibase, на этот раз содержащая советы «боевого» использования системы. Для получения базовых сведений подойдет первая статья-перевод «Управление миграциями БД с Liquibase» (ссылка).

Как и многие инструменты, служащие для облегчения жизни разработчиков программного обеспечения, Liquibase имеет «обратную сторону медали», с которой приходится рано или поздно столкнуться.

Вот 10 вещей, которые в определенный момент работы с Liquibase были для меня открытием.

1. Версионность приложения должна быть отражена в структуре папок миграций

Если вы не будете следовать этому правилу, файлы чейнджлогов быстро украсят папку миграций своим количеством и необычными именами.
На данный момент для себя я выработал оптимальную стратегию именования файлов и папок. Вот она:

/db-migrations
    /v-1.0
        /2013-03-02--01-initial-schema-import.xml
        /2013-03-02--02-core-data.xml
        /2013-03-04--01-notifications.xml
        /changelog-v.1.0-cumulative.xml
    /v-2.0
        ...
        /changelog-v.2.0-cumulative.xml
    /changelog.xml

Подробнее:

a. Чейнджлоги изменений базы данных содержатся в отдельных папках и соответсвуют версиям приложения.

Плюсы:

  • Всегда можно легко сказать, какие изменения схемы данных произошли при переходе с версии на версию приложения
  • Поддерживается аккуратность в папках, не позволяет расслабляться.
b. Кумулятивные чейнджлоги для каждой версии

Основным файлом миграций является /db-migrations/changelog.xml. В него включаются (тэг «include») только кумулятивные файлы чейнджлогов каждой из версий.

<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
    xmlns="http://www.liquibase.org/xml/ns/dbchangelog">
    <!-- Should be only links to per-version cumulative changelog files. -->
    <include file="v-1.0/changelog-v.1.0-cumulative.xml" relativeToChangelogFile="true"/>
    <include file="v-2.0/changelog-v.2.0-cumulative.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

В свою очередь, в кумулятивные чейнджлоги версий приложений включаются конкретные файлы с чейнджсетами.
Таким образом удается не замусорить главный файл чейнджлогов.

c. Единое правило именования файлов

Правило именования файлов позволяет без просмотра кумулятивных чейнджлогов файлов понять, что за чем следовало.

<DATE>-<DATE_INCR>-<DESCR>.xml
DATE – дата чейнджлога
DATE_INCR – инкремент файла в конкретную дату (когда несколько файлов добавлены в один день.)
DESCR – описание.
Пример:
2013-03-02--01-initial-schema-import.xml

2. Писать чейнджсеты непросто, поймите это

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

Если у вас есть несколько сред развертывания, и если у вашего приложения уже есть PROD-среда, требуется двойная концентрация внимания, усидчивость, тестирование на среде с похожими данными, тестирование откатов изменений.

3. Лучше много маленьких чейнджсетов, чем один большой

Один чейнджсет — одна транзакция. Если данных меняется много, лучше, чтобы транзакции были поменьше.
Работайте над ними изолированно, не добавляйте несколько изменений в один чейнджлог, думайте над содержимым тэга rollback. Лучше много простых и даже однотипных чем один сложный и невнятный.

4. Чейнджлоги, изменяющие схему БД, должны быть атомарными

Не могу сказать про многие базы данных, но по крайней мере в Oracle и MySQL DDL-операторы не включаются в транзакцию.
Это значит, что если при запуске чейнджсета произошка ошибка, DDL-операторы, выполненные до ошибки останутся в базе.
Пример (так неправильно):

<changeSet id="2013-03-02-initial-schema-import-1" author="eg">
    <createTable tableName="HOUSES">
        <column name="ID" type="BIGINT" />
    </createTable>
    <addPrimaryKey tableName="HOUSES" columnNames="ID" />
</changeSet>

Если при добавлении индекса произойдет ошибка, таблица HOUSES не будет удалена!
Как избежать боли? Каждое изменение схемы БД должно жить в своем чейнджлоге.

Пример (так правильно):

<changeSet id="2013-03-02-initial-schema-import-1" author="eg">
    <createTable tableName="HOUSES">
        <column name="ID" type="BIGINT" />
    </createTable>
</changeSet>
    
<changeSet id="2013-03-02-initial-schema-import-2" author="eg">
    <addPrimaryKey tableName="HOUSES" columnNames="ID" />
</changeSet>

5. Путь запуска скрипта миграции имеет значение

Посмотрите на две команды запуска скрипта миграции схемы БД

cd c:/mychangelogs/
liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password="" --changeLogFile=db.changelog-0.1.0.xml update

liquibase --url=jdbc:mysql://localhost:3306/liquiblog --driver=com.mysql.jdbc.Driver --username=root --password="" --changeLogFile=c:/mychangelogs/db.changelog-0.1.0.xml update

Кажется, что вторая команда не должна накатывать никаких изменений. Но это не так, Liquibase хранит путь к файлу чейнджлога в столбце FILENAME таблицы DATABASECHANGELOG.
В связи с этим фактом, при втором запуске Liquibase будет считать все чейнджсеты новыми. Это может сломать вашу схему данных!

Как избежать боли? Всегда запускайте чейнджлоги с использованием относительных путей, из одной и той же директории.

6. Условия precondition не влияют на контрольную сумму чейнджлога

Это хорошая новость для любителей ошибиться (то есть всех нас, рано или поздно). Иногда это позволяет изменить условия применения чейнджсета, таким образом, «исправив прошлое».

7. Liquibase не так уж хорош для часто меняющихся данных

Однажды мы попытались мигрировать много однотипных, но меняющихся от версии к версии данных конфигурации через чейнджлоги. Получилось очень грустно – постоянно кто-то из команды ошибался, ошибку уезжала на тестовую среду, откатывать ее надо было полностью вручную (помните контрольную сумму чейнджсетов?).
Безрадостную картину пришлось исправлять трезвым подходом к конкретной ситуации. Liquibase был оставлен для изменений схемы данных, а конфигурационные данные с каждой версией наливались Groovy-скриптом, имеющим properties-файл в качестве исходника.

8. Не верьте скриптам автоматической генерации чейнджсетов

Liquibase умеет генерировать

  • чейнджсеты по имеющейся схеме базы данных,
  • разницу, произошедшие в базе данных по сравнению с имеющимися чейнджсетами (применимо для Grails).

К этим удобствам надо относиться с осторожностью. Это просто небольшое избавление от рутины, не более. Все чейнджсеты надо просматривать, понимать и проверять. Вы не хотите исправлять логическую ошибку (нагенеренную глупой утилитой) уехавшую в PROD, правда?

9. Используйте XML, а не DSL

Иногда хочется «облегчить» жизнь и отказаться от XML, начав использовать более краткий DSL (groovy, yaml, json)
Все это очень хорошо до тех пор, пока вам не захочется иметь:

  • Автодополнение в IDE
  • Автоматическую проверку формальной верности документа по схеме данных

Ну и зачем отказываться от таких полезных вещей в пользу краткости? Видимо, я не настолько хороший программист, чтобы отказаться от автодополнения ради немногим лучшей читаемости кода.

10. Изучайте чужой опыт

Хотя большую часть пунктов этой статьи я познал на собственном опыте, официальный гайд по лучшим практикам использования Liquibase улучшает восприятие инструмента (ссылка)

Успешных миграций!

Автор: Snowindy

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js