Как-то вот здесь я рассказывал о методе синхронизации БД, который мы решили использовать в нашем проекте.
Так вот, сегодня хочу сказать, что решение хоть и было неплохим, но от него пришлось отказаться по причине некоторых проблем, которые не решаются с его помощью.
Проблемы эти таковы:
- При изменении объекта базы данных (процедуры, функции, триггера и т.д.) разработчик создаёт скрипт и помещает его в репозиторий. Всё бы хорошо, но несколько разработчиков могут написать свои скрипты (пофиксить что-то в процедуре, скажем), положить в репозиторий независимо.. И “победит” последний. Причём узнаётся об этом обычно в самый ответственный момент: на этапе тестирования (если не позже). Проявляется баг, разработчик недоумевает “я же пофиксил его, вот и скрипт есть!”. А то, что одни-двумя изменениями дальше идёт совершенно иная копия – как же это проверишь.
- Вышеописанная проблема многократно усугубляется в случае использования веток (бранчей).
Так, как в разных бранчах делаются разные куски функциональности, то, во-первых, достаточно сложно поддерживать “сквозную” систему нумерации. Либо при каждом слиянии бранчей нужно как-то “перенумеровывать” скрипты, но тоже никогда не скажешь, какой именно должен быть порядок в итоге.
Кроме того – та же проблема: в одном из бранчей поменяли процедуру, в другом – тоже. Скрипты перенумеровали, залили в репозиторий – а не работает.
При этом очень сложно сказать, какая же версия была “в оригинале” (базовая), какая являет собой изменение в одном бранче, какая – в другом. Это же всё разные файлы, да и их ещё поискать.
В общем, решение послужило какое-то время, оказалось, что проблем достаточно много с ним и его было решено поменять.
На замену пришёл новый Database Project для Visual Studio 2008: http://www.microsoft.com/downloads/details.aspx?FamilyID=bb3ad767-5f69-4db9-b1c9-8f55759846ed&displaylang=en
Его нужно скачать, установить – и вроде как все проблемы решены :)
Краткое описание того, что мы имеем, используя эту штуку.
Все объекты БД представлены в виде скриптов.
На этапе инициализации проекта ему нужно указать БД, из которой он “заскриптует” все элементы схемы: таблицы, представления, процедуры, очереди, индексы, ключи, роли.. всё-всё-всё, даже CLR-сборки, если таковые имеются.
В результате разработчик получает дерево, по своей структуре очень похожее на то, что мы привыкли видеть в Management Studio, но все элементы – в виде create-скриптов.
Проект “умеет” отслеживать ошибки на этапе “компиляции”.
Это очень удобно! Например, если мы попытаемся создать скрипт для индекса по несуществующей колонке, создать view для несуществущей таблицы или что-то ещё “этакое” (да просто допустим синтаксическую ошибку или опечатку), то мы увидим ошибку “компилятора” (так же, как мы видим в случае ошибок в коде). Понятное дело, что пока в проекте есть ошибки, изменения в БД применяться не будут.
Что характерно, проект разбирает скрипты и зависимости между ними самостоятельно, а не просто пытается выполнить SQL-код и перехватывает ошибки.
Так с предупрежденями.
Возможности рефакторинга.
Если честно – много не пробовал. Но оно там есть. Например, переименование таблицы или столбца. Просто кликаем правой кнопкой мыши на элементе в дереве, выбираем Refactor –> Rename – и готово. Будет переименовано везде.
Использование макро-переменных.
Скрипты – они, конечно, обычные SQL-скрипты, но в них можно (если захочется) использовать макро-переменные в виде $(DatabaseName). Значения этим переменным можно задать в конфигурации проекта, далее эти макросы будут автоматически подменены там, где они встречаются.
Зачем? Ну, я не знаю :) То же имя базы данных (или linked-сервера). Тот же логин пользователя. Словом, то, что может отличаться от среды к среде (у разработчиков – одно, в продакшн – другое).
Ещё много всякого…
…в виде определения изменений между проектом и базой данных, возможностями юнит-тестирования и т.д.
Как это работает.
Процесс примерно такой:
- Мы говорим проекту “Deploy”.
- Происходит Rebuild проекта. На этом этапе делаются все проверки на ошибки, предупреждения и т.д. В том случае, если ошибок нет, идём дальше.
- Происходит сравнение схемы базы данных, в которую мы делаем deploy cо схемой, которая присутствует в нашем проекте в виде кучи create-скриптов. По результатам этого сравнения генерируется дельта-скрипт, который содержит уже всевозможные alter, delete, create и т.д.
Надо сказать, что дельта эта генерируется очень качественно. Мы тестировали на достаточно сложных комбинациях – всё проходило “на ура”. Проблем не было ни разу. - Дельта-скрипт применяется в базу данных.
Кстати, применять дельта-скрипт вовсе не обязательно, можно “сказать” проекту, что этот скрипт нужно просто сгенерировать.
Вообще настроек у проекта достаточно много: ANSI-ключи, нужно ли удалять объект, если он пропал из схемы проекта, нужно ли делать бекап перед применением дельта-скрипта и т.д, проще посмотреть.
Да, это всё DDL.
Что касается DML: тут всё совсем примитивно просто: есть два раздела: PreDeployment Scripts и PostDeployment Scripts. Туда (обычно в Post-, разумеется) и предполагается складывать скрипты для DML. Единственное, за чем надо следить – так это за тем, чтобы скрипты проверяли, нужно ли делать то, что они хотят делать. Ибо все они будут исполняться каждый раз при обновлении БД из проекта. Но это, как и понятно, сделать очень и очень нетрудно и вообще best practices :)
Автоматическое развёртывание.
Поскольку всё делается через msbuild, все targets уже на месте, то нет проблем ни с каким билд-сервером, ни даже без него (msbuild можно и из командной строки запустить). Словом, всё точно так же, как и с любым “кодовым” проектом - “на ура” автоматизируется ночной билд-деплой, например.
Плюсы.
Для нас – очень существенные. Судите сами: каждый объект - это просто текстовый sql-файл. Как и любой другой файл с кодом, он отлично “уживается” в системе контроля версий, если два или более разработчика правят один файл – они будут иметь нормальный “человеческий” конфликт при чекине (или при слиянии веток) на этом файле и будут иметь все возможности грамотно этот конфликт разрешить.
Кроме того, правка таблиц, например, становится гораздо более лёгким делом, так как не приходится задумываться об ALTER TABLE, проверке на наличие (дабы не свалиться с исключением при попытке добавить то, что почему-то уже существует) и т.д. Нужен новый столбец: просто дописал его в CREATE-скрипт для таблицы – и всё. Остальное проект сделает за тебя.
Минусы.
У нас база данных – большая. Поэтому сравнение идёт достаточно долго. Долго – это где-то от 5 до 10 минут.
Хотя тут тоже не всё понятно. Сравение идёт долго на UAT-сервере, а вот локально, на той же БД (backup/restore) – одна минута пятнадцать секунд на весь деплоймент. Так что тут нужно ещё смотреть, нет ли у нас каких-то инфраструктурных затыков.
Пока это единственный минус, который я нашёл. Хотя для нас он не очень-то и существенен, даже если с инфраструктурой не разбираться. Ну, будет ночью билд-деплой длиться не 15 минут, а 25. Ну и кому какая разница, все спят :)
Словом, будем теперь так жить.