Открыть список
Как стать автором
Обновить

Комментарии 17

Всё четко и круто, спасибо за статью. Жирный плюс за jOOQ :)

И интересно сколько у вас миграций (десятки, сотни, тысячи) что они начали занимать в тестах ощутимое время.

Удивили ручными миграциями — у вас были факапы в проде? Я стараюсь отлавливать такие проблемы локально, а если никак уйти от неё нельзя — перехожу к миграциям батчами (в приложении или вне) без ломания обратной совместимости.
Это же решает и вопрос удаления колонки — сначала релизим код, который перестал её использовать, а затем уже удаляем. Пробовали? Чем не подошло?
Спасибо за комментарий!

И интересно сколько у вас миграций
на данный момент 230

Удивили ручными миграциями — у вас были факапы в проде? Я стараюсь отлавливать такие проблемы локально, а если никак уйти от неё нельзя — перехожу к миграциям батчами (в приложении или вне) без ломания обратной совместимости.
Я возможно не до конца понял вопрос, но постараюсь ответить. Под ручными миграциями (в комбинированном подходе) подразумевается вызов кода с миграцией из приложения (не подключение к БД и выполнение каких-либо скриптов руками). Т.е. это тот же код, что выполняет flyway, но не в момент прогона всех миграций, а в произвольное время, чтобы не замедлять релиз. В этом коде как раз часто бывают батчи. Поскольку схема данных в таких миграциях не изменяется, то, как правило, рисков нарушить обратную совместимость минимум.
Насчет факапов при релизе миграций – не было :-)

Это же решает и вопрос удаления колонки — сначала релизим код, который перестал её использовать, а затем уже удаляем. Пробовали? Чем не подошло?
Именно так и делаем. Я лишь имел ввиду, что несмотря на всю очевидность подобной миграции, состоящей из 2 последовательных шагов, и наличия соответствующих требований в инструкциях по написанию миграций, всегда есть риск человеческого фактора — «новичок/уставший разработчик/любой из нас» забыл и не подумал об этом, а на ревью не заметили. К счастью, пока такого не случалось — бывало, что «забывали», но на ревью отлавливали. Но все равно эту проверку хотелось бы автоматизировать.

А зачем столько миграций, первые несколько сотен разве нельзя смержить? Да и откат в случае проблем все равно будет вперёд идти.

К сожалению, flyway не имеет встроенного механизма для объединения миграций. Поскольку он проверяет набор выполненных миграций и миграций в коде, то чтобы заменить первые Х миграций на одну большую придется что-то сделать с таблицей schema_version (удалить старые Х миграций, добавить запись с правильной контрольной суммой для новой миграции). Для одного окружения, например только для production, это еще можно сделать, поскольку все БД на одной версии, но непонятно как быть с локальными средами разработчиков и тестовыми окружениями, где версии могут быть какими угодно.
В общем, мы уже думали над этой задачей, но пока приемлемого решения не нашли.
С локальными средами разработчиков всё должно быть просто — снести всё и прогнать миграции заново. На тестовых — скорее всего через коллбеки
У большинства разработчиков в локальной БД есть какие-то тестовые данные, которые они используют (не говоря уже про отдельные тестовые окружения, где данных может быть много), и не хочется заставлять их тратить время на повторное создание этих данных. Хочется сохранить удобство текущего подхода и избежать лишних манипуляций — ты просто запускаешь сервер и база актуализируется автоматически.
Есть определенные идеи как сделать встроенную поддержу схлапывания миграций в flyway – надеюсь получится выделить время на доработку.
Я когда-то еще с Liqubase размышлял как организовать тестовые данные. Представлял себе, что все тест кейсы должны иметь для себя тестовые данные, кооторые можно описать, а затем засунуть в Liquibase миграцию. Последний позволял легко с помощью csv файла решать такие задачи.
Но на практике оказалось, что всем этого не нужно, и скорее будет мешать.

Я же пришел к выводу, если разработчику нужны тестовые данные — пусть напишет скрипт, который эти тестовые данные ему создадут. У разработчика есть много опций — в тесте через АПИ, миграцией с insert или дампом БД, новой таблицей flyway_schema_version.
Но блокировать всю компанию из-за того, что кто-то себе локально настроил тестовые данные — звучит страшно. Возможно ваши данные создаются очень сложно, и как раз ваш подход дешевле)
Я согласен с вами — тестовые данные нужны только для тестов и располагать их в общих миграциях будет неправильно. С другой стороны, если для тестов также хочется иметь версионность (т.е. для разных версий схемы данных разные тестовые наборы, а не только тесты для последней версии), то можно вести отдельную систему миграций. Примерно так:
Основные миграции:
V1_create_table1.sql
V2_alter_table1.sql
V3_create_table2.sql
Тестовые миграции:
V1_create_test_data_table1.sql
V2_create_test_data_table1.sql
V3_create_test_data_table2.sql
А при запуске тестов в зависимости от версии схемы данных накатывать на базу соответствующую миграцию с данными.

Не очень понимаю, когда это может быть нужно, но теоретически, наверное, имеет право на жизнь :-)

По схлопыванию, я пришел к вот такому выводу — для создания таблицы нужен отдельный файл миграции. А дальше лишь миграции `V123__alter_table_A.sql`. Схлопывание далее будет делаться вручную. Я всё никак не допишу статью на Хабр про этот способ, вот тут есть прототип как оно может выглядеть.

Очень интересно будет прочитать как вы решите эту задачу)

Есть одна жирная проблема с автоматическими миграциями: они должны быть линейно упорядочены. Это сильно усложняет жизнь, если несколько девелоперов пилят фичи в разных ветках. Приходится вручную контролировать порядок версий при сливе в master main, а каждый девелопер при апдейте из main должен ломать голову как применить предыдущие миграции на своей локальной базе. Есть ли какие-то рекомендации по этому поводу?

Эта проблема решается хорошо в Liquibase и Python Django миграциях.
В случаи с флайвеем можно называть миграции по датам, например V20200801__my_suer_migration.sql а затем прогнать на тест\стейдж энве, где уже есть данные. Пусть лучше свалится CI. На практике это дешевле, чем городить огород на Liquibase.
Да, вы правы, такая проблема есть. Мы решаем ее весьма костыльным (но работающим путем) — с помощью отдельного канала в слаке, где разработчики, создающие миграцию, «бронируют» очередную версию для себя. Там же они иногда «передоговариваются» о порядке и обмениваются версиями, чтобы не тормозить последующую миграцию в случае, когда один из pull request-ов завис на ревью. В целом у нас есть рекомендация разбивать реализацию функционала на несколько реквестов, 1й из которых — только sql-миграция без бизнес-логики, чтобы минимизировать время между «бронированием» версии и мержем миграции.
Также для того, чтобы гарантировать линейный порядок версий в мастере, мы используем плагин для bitbucket, который проверяет грубо говоря, что новый реквест имеет версию миграций +1 относительно последней миграции в мастере.

Стоить добавить, что поскольку не все миграции зависимы друг от друга, то иногда их можно выполнять в любом порядке (например, без разницы в каком порядке вы создадите 2 индекса) – для этого flyway имеет опцию outOfOrder. Но мы решили, что подобная двойственность (часть миграций упорядочена, часть нет) – еще одно место для потенциальных проблем (для упорядоченных миграций) и поэтому не используем ее и порядок всегда одинаков.

Мы на текущем проекте используем MySQL. У меня Windows, и поднятие чистого контейнера с docker hub занимает секунд 20, без pull. Миграций тоже около 100+, и, если не использовать tmpfs для data dir, поднятие контекста становится вечным. Ваш вариант (подготовить образ со схемой данных) тоже рассматривал, но сколкнулся с тем, что тесты все равно занимают много времени, если пишут на диск, а не в ОЗУ.
Сколько у вас тестов и как долго они выполняются? Не удалось ли вам с подготовкой образа как-то примонтировать tmpfs (может быть, копированием data dir через init.sh в образе)?
Если что, оставил свой путь в заметке: https://rocketscien.se/testcontainers

Сколько у вас тестов и как долго они выполняются?
Интеграционных тестов примерно 2 тысячи, выполняются около 5-7 минут.
Не удалось ли вам с подготовкой образа как-то примонтировать tmpfs (может быть, копированием data dir через init.sh в образе)?
Сконцентрировавшись на проблеме прогона миграций на запуске каждого тестового класса, мы почему-то даже не посмотрели в эту сторону, но выглядит перспективно, попробую — спасибо за идею и за вашу заметку!
Меня тут немного осенило, и я ещё ускорил свои тесты :) делюсь с вами знанием.
rocketscien.se/testcontainers-2
Спасибо!
Кстати, для того, чтобы быстро откатывать вашу базу в начальное состояние вы можете посмотреть в сторону «тонкого клонирования» — быстрое создание копии файловой системы на основе ZFS. Есть даже более менее готовое решение — Database Lab. Сам я его на практике не пробовал, но вероятно, мы будем рассматривать его как один из вариантов для реализации т.н. «иммутабельных» тестовых сред (когда на каждый запуск набора e2e тестов создается новая база с подготовленными тестовыми данными).
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.