Миграции баз данных — это отличный способ безопасно обновить схему базы данных. Это именно то, что нам нужно в продакшене, ведь терять имеющиеся там данные крайне нежелательно. Если вы хотите узнать больше о миграциях — у нас есть скринкаст на эту тему, очень рекомендуем посмотреть его!

Сегодня же я хочу поделиться с вами советом, который поможет навести порядок в ваших миграциях... если вы обнаружили, что ваш каталог migrations/ разросся до каких-то немыслимых масштабов... как это случилось с нами! Конечно, стоит оговориться, что это представляет проблему в основном для локальной разработки: на продакшене нас не очень волнует, сколько файлов миграции было выполнено в прошлом.

Но в качестве сетапа для локальной разработки рекомендуется создать отдельную базу данных, а затем выполнить команду doctrine:migrations:migrate, чтобы получить именно то, что мы имеем в продакшене, включая таблицу migration_versions. Это лучше, чем выполнять команду doctrine:schema:update, которая затрудняет тестирование новых миграций.

И если у вас накопилось много старых файлов миграции, это все может выполняться очень долго! На SymfonyCasts у нас их было 49. Поэтому, хотя это совершенно необязательно, если гигантская папка migrations/ начинает доставлять вам неудобства, вы можете навести в ней порядок! Также вас следует поздравит с успешным сайтом, который прожил достаточно долго, чтобы в этом нуждаться.

Генерация полной единой миграции

Tip

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

Прежде всего, удалите все миграции:

rm -rf migrations/

Теперь сбросьте текущую схему в новую единую миграцию:

symfony console doctrine:migrations:dump-schema

Этот пакет предоставляет еще одну команду, которая помогает "свернуть" миграции, удалив все отслеживаемые версии и вставив одну актуальную:

symfony console doctrine:migrations:rollup

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

P.S. Спасибо Кристофу Ковету (Christophe Coevoet) за то, что он указал это в комментариях.

Конечно, сначала мы сделаем это локально, так что запустите:

rm -rf migrations/

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

symfony console doctrine:schema:drop --force

 ### Tip

Команда symfony console аналогична команде bin/console, но позволяет вводить переменные окружения, если вы используете интеграцию с Docker.

Теперь сгенерируем новую миграцию с помощью MakerBundle:

symfony console make:migration

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

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

Пропуск повторной миграции на продакшене

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

git status

Например, если раньше файл миграции назывался Version20220415102030.php, переименуйте только что созданный файл миграции именно в это имя и не забудьте соответствующим образом изменить имя класса внутри файла. Это позволит выполнить развертывание в продакшн, но эта миграция не будет выполнена (будет пропущена), поскольку она уже была выполнена в прошлом.

Удаление ранее выполненных миграций из продакшена

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

[WARNING] You have X previously executed migrations in the database that are not registered migrations.

Неприятно! Это происходит потому, что таблица migration_versions в продакшене теперь показывает, что в прошлом была выполнена куча миграций... но эти файлы миграций больше не существуют! Мы могли бы удалить их вручную из базы данных нашего продакшена, но... о ужас! Выполнять вручную запрос DELETE в производственной базе данных менее приятно, чем везти кошку к ветеринару.

Вместо этого сгенерируйте пустую миграцию:

symfony console doctrine:migration:generate

Затем откройте ее и добавьте в конец новый SQL-оператор:

final class Version20220415102031 extends AbstractMigration
{
    public function up(Schema $schema): void
    {
        // данная миграция генерируется автоматически, пожалуйста, измените ее в соответствии с вашими потребностями

        $this->addSql('DELETE FROM migration_versions WHERE version NOT LIKE "%20220415102030" AND version NOT LIKE "%20220415102031"');
    }

    // ...
}

Где 20220415102031 — номер пустой миграции, которую мы только что сгенерировали, а 20220415102030 — номер другой полной миграции, которую мы имеем. Убедитесь, что вы заменили эти номера на свои!

И все готово! Попробуйте сначала локально:

symfony console doctrine:migration:migrate

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

Теперь можно смело развертывать это в продакшене и радоваться чистому каталогу migrations/!

Всех желающих приглашаем на открытый урок «Kafka. Используем в Symfony», на котором рассмотрим использование альтернативы RabbitMQ в Symfony-приложениях. Обсудим преимущества и недостатки. Записаться можно на странице курса "Symfony Framework".

Комментарии (4)


  1. dimas846
    10.11.2023 07:27
    +1

    Миграции нужны для того, чтобы переключившись на какой то старый комит (7 месяцев назад) вы могли бы иметь и структуру базы из прошлого, откатившись до нужной миграции. Когда вы все объединили сам смысл миграций пропал.


    1. smple
      10.11.2023 07:27

      Немного не соглашусь

      Миграции нужны чтобы обновлять структуру данных к актуальному состоянию.

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


      1. dimas846
        10.11.2023 07:27

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

        Изначальной проблемы как бы нет, или она не достаточно раскрыта в. Все миграции за раз нужно накатить например новому разработчику, пришедшему в проект. Больше это делать не придётся. Так пусть и лежат это ~100 файлов в своей папке.


        1. smple
          10.11.2023 07:27
          +1

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

          мой опыт на большинстве проектов:

          1. Добавляя миграцию приходится открывать файл с сотнями миграций и скролить вниз чтобы найти файлик, такое делается ~10 раз в месяц, мне такое не нравится.

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

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

          4. Новый разработчик на проекте, это вы описали, такое бывает у всех по разному но пусть будет ~1 в квартал (3 месяца)

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

          При этом я вполне спокойно отношусь к тем кто не объединяет миграции, объединение миграций это инструмент, а не silver bullet, поэтому его надо применять к месту, кому то он может не подходить.