У нас есть приложение, которое сочетает в себе как быструю (<1 секунда), так и медленную миграцию базы данных (> 30 секунд). Прямо сейчас мы выполняем миграцию базы данных как часть CI, но затем наш инструмент CI должен знать все строки подключения к базе данных для нашего приложения (в разных средах), что не идеально. Мы хотим изменить этот процесс, чтобы приложение запускало собственные миграции базы данных при запуске.
Вот ситуация:
У нас есть несколько экземпляров этого приложения - около 5 в производстве. Давайте позвоним им node1, ..., node5
. Каждое приложение подключается к одному экземпляру SQL Server, и мы не используем скользящее развертывание (насколько я знаю, все приложения развертываются одновременно)
Проблема: скажем, у нас длительная миграция. В этом случае node1
запускается, а затем начинается выполнение миграции. Теперь node4
запускается, и длительная миграция еще не завершена, поэтому node4
также запускается миграция -> возможно ли повреждение данных? Как бы вы предотвратили эту проблему или эта проблема настолько важна, чтобы о ней беспокоиться?
Я думал о решении этой проблемы с распределенной блокировкой (используя etcd
или что-то в этом роде). По сути, все приложения пытаются получить блокировку, только одно из них получает ее и выполняет миграции, а затем разблокирует. Когда остальные приложения запускаются и входят в критическую секцию, все миграции уже выполнены, поэтому скрипт миграции просто завершается.
Тем не менее, моя интуиция говорит: «Это излишне, должно быть более простое решение», поэтому я подумал, что попрошу здесь, чтобы узнать, есть ли у кого-нибудь еще идеи получше.
Ответы:
Поскольку вы упомянули SQL-сервер: согласно этому предыдущему сообщению DBA.SE , изменения схемы могут (и должны) вноситься в транзакции. Это дает вам возможность спроектировать ваши миграции точно так же, как и любые другие формы одновременной записи в вашу БД - вы запускаете транзакцию, а когда она терпит неудачу, вы откатываете ее назад. Это предотвращает, по крайней мере, некоторые из худших сценариев повреждения базы данных (хотя одни только транзакции не предотвратят потерю данных, когда существуют разрушительные шаги миграции, такие как удаление столбца или таблицы).
До сих пор я уверен, что вам также понадобится
migrations
таблица, в которой зарегистрированы уже примененные миграции, поэтому процесс приложения может проверить, была ли применена конкретная миграция или нет. Затем используйте «SELECT FOR UPDATE» для реализации ваших миграций следующим образом (псевдокод):SELECT FROM Migrations FOR UPDATE WHERE MigrationLabel='MyMigration42'
INSERT 'MyMigration42' INTO Migrations(MigrationLabel)
Это встраивает механизм блокировки непосредственно в тест «была ли миграция уже применена» .
Обратите внимание, что этот дизайн - в теории - позволит вашим шагам миграции не знать, какое приложение на самом деле его применяет - может быть возможно, что шаг 1 будет применен приложением 1, шаг 2 приложением 2, шаг 3 приложением 3, шаг 4 приложением 1 снова и так далее. Тем не менее, это также хорошая идея не применять миграцию, пока используются другие экземпляры приложения. Параллельное развертывание, как уже упоминалось в вашем вопросе, может уже устранить это ограничение.
источник
Может быть, вы можете найти библиотеку, которая поддерживает миграцию базы данных с несколькими узлами.
Я знаю о двух библиотеках в мире Java, обе они поддерживают то, что вам нужно:
Вероятно, есть и другие инструменты для Java и других языков.
Если вы не можете (или не хотите) использовать такой инструмент, таблицу можно использовать как блокировку или даже как журнал миграции, см. Пример Док Браунс .
источник