Перейти к основному содержимому

Ремонт пролетного пути с помощью Spring Boot

· 7 мин. чтения

1. Обзор

Пролетные миграции не всегда идут по плану. В этом руководстве мы рассмотрим имеющиеся у нас варианты восстановления после неудачной миграции .

2. Настройка

Давайте начнем с базового проекта Spring Boot, настроенного для Flyway. Он имеет зависимости flyway-core , spring-boot-starter-jdbc и flyway - maven-plugin .

Дополнительные сведения о конфигурации см. в нашей статье, посвященной Flyway .

2.1. Конфигурация

Во-первых, давайте добавим два разных профиля. Это позволит нам легко запускать миграции для разных механизмов баз данных:

<profile>
<id>h2</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>postgre</id>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
</dependencies>
</profile>

Давайте также добавим файлы конфигурации базы данных Flyway для каждого из этих профилей.

Во-первых, мы создаем application-h2.properties :

flyway.url=jdbc:h2:file:./testdb;DB_CLOSE_ON_EXIT=FALSE;AUTO_RECONNECT=TRUE;MODE=MySQL;DATABASE_TO_UPPER=false;
flyway.user=testuser
flyway.password=password

И после этого создадим приложение PostgreSQL-postgre.properties :

flyway.url=jdbc:postgresql://127.0.0.1:5431/testdb
flyway.user=testuser
flyway.password=password

Примечание. Мы можем либо настроить конфигурацию PostgreSQL в соответствии с уже существующей базой данных, либо использовать файл docker-compose в примере кода `` .

2.2. Миграции

Давайте добавим наш первый файл миграции, V1_0__add_table.sql :

create table table_one (
id numeric primary key
);

Теперь добавим второй файл миграции, содержащий ошибку, V1_1__add_table.sql:

create table <span style="color: #ff0000">table_one</span> (
id numeric primary key
);

Мы намеренно сделали ошибку, используя одно и то же имя таблицы. Это должно привести к ошибке миграции Flyway.

3. Запустите миграцию

Теперь давайте запустим приложение и попробуем применить миграции.

Сначала для профиля h2 по умолчанию:

mvn spring-boot:run

Затем для профиля postgre :

mvn spring-boot:run -Ppostgre

Как и ожидалось, первая миграция прошла успешно, а вторая — нет:

Migration V1_1__add_table.sql failed
...
Message : Table "TABLE_ONE" already exists; SQL statement:

3.1. Проверка состояния

Прежде чем перейти к восстановлению базы данных, давайте проверим состояние миграции Flyway, выполнив:

mvn flyway:info -Ph2

Это возвращает, как и ожидалось:

+-----------+---------+-------------+------+---------------------+---------+
| Category | Version | Description | Type | Installed On | State |
+-----------+---------+-------------+------+---------------------+---------+
| Versioned | 1.0 | add table | SQL | 2020-07-17 12:57:35 | Success |
| Versioned | 1.1 | add table | SQL | 2020-07-17 12:57:35 | <span style="color: #ff0000">Failed</span> |
+-----------+---------+-------------+------+---------------------+---------+

Но когда мы проверяем состояние PostgreSQL с помощью:

mvn flyway:info -Ppostgre

Мы замечаем, что состояние второй миграции — Pending , а не Failed:

+-----------+---------+-------------+------+---------------------+---------+
| Category | Version | Description | Type | Installed On | State |
+-----------+---------+-------------+------+---------------------+---------+
| Versioned | 1.0 | add table | SQL | 2020-07-17 12:57:48 | Success |
| Versioned | 1.1 | add table | SQL | | <span style="color: #339966">Pending</span> |
+-----------+---------+-------------+------+---------------------+---------+

Разница заключается в том, что PostgreSQL поддерживает транзакции DDL, а другие, такие как H2 или MySQL, — нет. В результате PostgreSQL смог откатить транзакцию неудачной миграции . Давайте посмотрим, как эта разница влияет на вещи, когда мы пытаемся восстановить базу данных.

3.2. Исправьте ошибку и повторите миграцию

Давайте исправим файл миграции V1_1__add_table.sql , изменив имя таблицы с table_one на table_two.

Теперь давайте попробуем снова запустить приложение:

mvn spring-boot:run -Ph2

Теперь мы замечаем, что миграция H2 завершается с ошибкой:

Validate failed: 
Detected failed migration to version 1.1 (add table)

Flyway не будет повторно запускать миграцию версии 1.1 , если для этой версии существует уже неудачная миграция.

С другой стороны, профиль postgre успешно запустился. Как было сказано ранее, из-за отката состояние было чистым и готовым к применению исправленной миграции.

Действительно, запустив mvn flyway:info -Ppostgre , мы увидим, что обе миграции применены с Success . Итак, в заключение, для PostgreSQL все, что нам нужно было сделать, это исправить наш сценарий миграции и повторно запустить миграцию.

4. Вручную восстановить состояние базы данных

Первый подход к восстановлению состояния базы данных — вручную удалить запись Flyway из таблицы flyway_schema_history .

Давайте просто запустим этот оператор SQL для базы данных:

delete from flyway_schema_history where version = '1.1';

Теперь, когда мы снова запустим mvn spring-boot:run , мы увидим успешное применение миграции.

Однако прямое управление базой данных может быть не идеальным. Итак, давайте посмотрим, какие еще варианты у нас есть.

5. Ремонт пролетного пути

5.1. Восстановить неудачную миграцию

Давайте продолжим, добавив еще один неработающий файл миграции V1_2__add_table.sql , запустив приложение и вернувшись в состояние, в котором у нас возникла ошибка миграции.

Другой способ восстановить состояние базы данных — использовать инструмент flyway:repair . После исправления файла SQL вместо того, чтобы вручную трогать таблицу flyway_schema_history , мы можем запустить:

mvn flyway:repair

что приведет к:

Successfully repaired schema history table "PUBLIC"."flyway_schema_history"

Незаметно Flyway просто удаляет запись о неудачной миграции из таблицы flyway_schema_history .

Теперь мы можем снова запустить flyway:info и увидеть, что состояние последней миграции изменилось с Failed на Pending .

Давайте снова запустим приложение. Как мы видим, исправленная миграция теперь успешно применяется.

5.2. Выровнять контрольные суммы

Обычно рекомендуется никогда не изменять успешно примененные миграции. Но могут быть случаи, когда нет никакого способа обойти это.

Итак, в таком случае давайте изменим миграцию V1_1__add_table.sql , добавив комментарий в начало файла.

Запустив приложение сейчас, мы видим сообщение об ошибке «Несоответствие контрольной суммы миграции», например:

Migration checksum mismatch for migration version 1.1
-> Applied to database : 314944264
-> Resolved locally : 1304013179

Это происходит потому, что мы изменили уже примененную миграцию, а Flyway обнаруживает несоответствие.

Чтобы выровнять контрольные суммы, мы можем использовать ту же команду flyway :repair . Однако на этот раз миграция выполняться не будет. Только контрольная сумма записи версии 1.1 в таблице flyway_schema_history будет обновлена, чтобы отразить обновленный файл миграции.

Запустив приложение снова после восстановления, мы замечаем, что приложение теперь запускается успешно.

Обратите внимание, что в этом случае мы использовали flyway:repair через Maven. Другой способ — установить инструмент командной строки Flyway и запустить ремонт flyway . Эффект тот же: исправление пролетного пути удалит неудачные миграции из таблицы flyway_schema_history и перестроит контрольные суммы уже примененных миграций .

6. Обратные вызовы Flyway

Если мы не хотим вмешиваться вручную, мы могли бы рассмотреть подход к автоматической очистке неудачных записей из flyway_schema_history после неудачной миграции . Для этой цели мы можем использовать обратный вызов afterMigrateError Flyway .

Сначала мы создаем файл обратного вызова SQL db/callback/afterMigrateError__repair.sql :

DELETE FROM flyway_schema_history WHERE success=false;

Это автоматически удалит любую ошибочную запись из истории состояния Flyway при возникновении ошибки миграции.

Давайте создадим конфигурацию профиля application-callbacks.properties , которая будет включать папку db/callback в список местоположений Flyway:

spring.flyway.locations=classpath:db/migration,classpath:db/callback

И теперь, после добавления еще одной битой миграции V1_3__add_table.sql, мы запускаем приложение, включая профиль callbacks :

mvn spring-boot:run -Dspring-boot.run.profiles=h2,callbacks
...
Migrating schema "PUBLIC" to version 1.3 - add table
Migration of schema "PUBLIC" to version 1.3 - add table failed!
...
Executing SQL callback: afterMigrateError - repair

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

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

7. Резюме

В этой статье мы рассмотрели различные способы восстановления после неудачной миграции Flyway.

Мы видели, как такая база данных, как PostgreSQL, то есть та, которая поддерживает транзакции DDL, не требует дополнительных усилий для восстановления состояния базы данных Flyway.

С другой стороны, для таких баз данных, как H2, без этой поддержки, мы увидели, как восстановление Flyway можно использовать для очистки истории Flyway и, в конечном итоге, для применения исправленной миграции.

Как всегда, полный код доступен на GitHub .