Как работать с несколькими сценариями изменения базы данных, приходящими из разных веток Git?

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

  • производство, которое является живой отраслью. Все работает в живой веб-среде.
  • интеграция, в которой интегрированы все новые функции. Этот филиал включается в производство каждую неделю.
  • одна или несколько ветвей функций, в которых разработчики или группы разработчиков разрабатывают новую функциональность. После того, как это сделано, разработчики объединяют свою ветвь функций с интеграцией.

Так что ничего сложного здесь нет. Но, поскольку наше приложение является веб-приложением, работающим с базой данных MySQL, новые функциональные возможности часто требуют изменений в схеме базы данных. Чтобы автоматизировать это, мы используем dbdeploy, который позволяет нам создавать сценарии изменения, учитывая число. Например, 00001.sql, 00002.sql и т. Д. При слиянии с веткой интеграции dbdeploy проверит, какие сценарии изменения имеют большее число, чем последний выполненный сценарий в этой конкретной базе данных, и выполнят их.

Теперь предположим следующее. - интеграция имеет изменения сценариев до 00200.sql. Все они выполняются в базе данных интеграции. - у разработчика Джона есть ветвь FeatureX FeatureX, которая была создана, когда интеграция все еще имела сценарий 00199.sql как самый высокий сценарий изменения.

Джон создает 00200.sql из-за некоторых необходимых изменений схемы БД.

Теперь, в какой-то момент Джон вернет свои модификации обратно в ветку интеграции. Джон получит конфликт слияния и увидит, что его 00200.sql уже существует в интеграции. Это означает, что ему нужно открыть конфликтующий файл, извлечь его содержимое, вернуть этот файл обратно в "мое" (исходное состояние, как при интеграции) и поместить свое собственное содержимое в новый файл.

Теперь, так как мы работаем с десятью разработчиками, мы ежедневно сталкиваемся с такой ситуацией. И хотя мы понимаем причины этого, иногда это очень громоздко. Джон переименовывает свой сценарий, выполняет объединение, фиксирует интеграцию, передает изменения в апстрим, только чтобы увидеть, что кто-то другой уже создал 00201.sql, требуя от Джона повторной обработки.

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

Итак, вкратце, мои вопросы:

  • Как автоматизировать изменения схемы базы данных при работе с разными ветвями объектов, которые работают на разных экземплярах одной и той же БД?
  • Как предотвратить конфликты слияния все время, сохраняя возможность иметь фиксированный порядок в исполняемых скриптах alter? Например, 00199.sql должен быть выполнен до 00200.sql, потому что 00200.sql может зависеть от того, что сделано в 00199.sql.

Любые другие советы приветствуются.

5 ответов

Рельсы использовали для этого именно с теми проблемами, которые вы описали. Они изменились на следующую схему: файлы (rails называет их миграциями) помечены меткой времени utc, когда файл был создан, например

20140723069701_add_foo_to_bar

(Вторая часть названия не влияет на порядок).

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

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

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

Несколько предложений:

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

2 - иметь центральное местоположение для получения следующего доступного номера, тогда люди используют последний номер.

Я использовал Liquibase в прошлом довольно успешно, и у нас не было проблемы, которую вы описали.

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

Кроме того, включите имя разработчика в название сценария изменения. Это предотвратит конфликты слияния на 100%.

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

Я использовал два разных подхода, чтобы преодолеть вашу проблему в прошлом.

Во-первых, использовать ORM, который может обрабатывать обновления схемы.

Другой подход заключается в создании сценария, который постепенно создает схему базы данных. Таким образом, если разработчику требуется дополнительная строка в таблице, он должен добавить соответствующий оператор sql после создания таблицы. Аналогично, если ему нужна новая таблица, он должен добавить для этого оператор SQL. Тогда слияние становится вопросом обеспечения того, чтобы все происходило в правильном порядке. Это в основном то, что делает процесс обновления базы данных в ORM. Такой сценарий необходимо кодировать очень оборонительно, и каждый оператор должен проверять, существуют ли его предпосылки.

Для инструмента командной строки dbvc я использую git log определить порядок обновления скриптов.

git log -c --no-merges --pretty="format:" --name-status -p dev/db/updates/ | \
  grep '^A' | awk '{print $2}' | tac

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

  • Если вы бежите git merge bсначала будут запускаться обновления от мастера, а затем от B.
  • Если вы бежите git rebase b, обновление от B будет запущено первым, а затем от master.
Другие вопросы по тегам