Контроль версий + БД

Рейтинг: 1Ответов: 2Опубликовано: 22.10.2014

Всем привет!

Имеется БД со множеством таблиц, функций и всего остального. Как реализовать работу с её схемой через систему контроля версий?

Сейчас мне видится два варианта:

  1. Апдейт скрипты - вроде как самый правильный (или нет?)
  2. Когда мы правим сам скрипт CREATE, а в базе делаем ALTER до состояния соответствия с обновленным скриптом CREATE

Ответы

▲ 5Принят

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

class Version100318_25102014 extens AbstractMigration
{
    protected $tableName = 'dummmy_name';
    // применяется при накатывании миграций
    public function up(Schema $schema)
    {
        $schema->createTable(
            $this->tableName,
            [
                'column_a' => Schema::TYPE_PK,
                'ref_column' => Schema::TYPE_INTEGER,
            ]
        )
    }
    // при откатывании (да, такое тоже нужно)
    public function down()
    {
        $schema->dropTable($this->tableName);
    }
}

Это очень прекрасная штука, которая, впрочем, имеет свою вафельную сторону:

  • Миграции абсолютно не терпят распределенности. Миграции должны накладываться в определенном порядке, что заставляет прописывать время/версию в названии, и любая попытка работы с веткой проекта приведет к перемешиванию миграций, и, скорее всего, к падению процесса накатывания/слития миграций.
  • Как следует из пункта 1, любая попытка разделить проект на два, но сохранить общую БД потерпит жесткую кару. В теории можно вести проект, абсолютно не затрагивая совместные области, но на практике это невозможно.
  • Классический случай: Вася и Петя работают на своих компах, и почти одновременно создают миграции, повторяющие друг друга по смыслу, или пользующиеся одни и тем же ресурсом, при этом заглянуть в репо им не поможет - там еще нет изменений другого человека. VCS это без проблем сглатывает, а вот dev-ветка (или даже продакшен, если dev-ветки нет) ВНЕЗАПНО падает с ошибкой.
  • Вести визуальную схему становится проблематично, реверс-инжиниринг остается, но каждый раз перемещать эти квадратики заново - это маразм.
  • По ряду причин миграции постоянно опережают написанный код, и к моменту написания кода часто приходится писать миграцию(и), вносящую(ие) изменения, которые не удалось продумать до этого.

Полезные ссылки

▲ 3

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

class AddDirectionToTickets < ActiveRecord::Migration
  def change
    add_column :tickets, :direction, :string, limit: 10, null: false
  end
end

Сами миграции следует хранить в системе контроля версий -- git или Mercurial.

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

rake db:migrate

Вот и всё.

UPD: Ещё один нюанс. Обычно все миграции сортируются по имени, где указывается дата создания. Иногда, если работа идёт в двух разных ветках VCS и код одной из них залит на продакшен (и миграции применены), а вторая ветка добавляет миграции, которые по порядку идут раньше уже применённых, то эти миграции следует вручную передвинуть, переименовать. Нужно для того, чтобы миграции применялись на продакшене ровно в том порядке, в каком они были созданы.

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