Делаем oracle deploy-скрипт независимым от окружения

Уже не одна правильная статья написана про необходимость и преимущества хранения исходных кодов схем базы данных в системах контроля версий (типа CVS, SVN, TFS и др.), а также ведения deploy – скриптов.
Не стану повторяться, но разберем один специфических аспектов этого процесса.

Не секрет, что нормально поставленный процесс разработки состоит из собственно разработки(Dev), внутреннего тестирования(QA), приёмочного тестирования конечными пользователями (UAT) и, непосредственно, «Production». Детали жизненного цикла могут отличаться в индивидуальных случаях, но это не существенно для темы статьи.

Порой (а в опыте автора – часто) так случается, что окружения, на которых происходят разные этапы этого цикла могут отличаться по тем или иным причинам. Различия могут быть какие угодно. От разных tablespace-ов, до отличий в названиях схем, DBLink-ов и других индивидуальных особенностей. Как эффективно решить эту неприятность мы и рассмотрим в этой статье.



image

Рядом с основным deploy.sql-скриптом (который вызывает другие скрипты с непосредственно исходниками объектов базы), кладём папочку, в которой хранился бы набор файлов под каждую среду с набором индивидуальных параметров — define-ов.

	DEFINE DATA_TS = DEV_DATA_TS
	DEFINE INDEX_TS = DEV_DATA_TS
	DEFINE Some_Source_data_dbl = Dev_dbl_source.world
	-- ... и т.д.
	


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

	create table my_table (sample_col VarChar(2 char) ) tablespace &DATA_TS;
	.... 
	select * from dual@&Some_Source_data_dbl;
	


Таким образом ваши скрипты будут индифферентны к окружению, и вы с большей уверенностью сможете сказать, что отработав на тестовом окружении, они так же надёжно отработают и на UAT(например). В итоге, вы не меняете сами исходные коды под каждое конкретное окружение, тем самым убирая риск человеческого фактора: опечатки, забывчивости, вероятность отвлектись.
Риски того, ошибка проберется в сам файла параметров-подстановок уходит уже после 1-2-3 итераций сбора окружения (да и вычитать файл подстановок, как правило, не так уж сложно).

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

dev.sql
qa.sql
uat.sql
prod.sql

Но сейчас отказались от такой практики, и называем по TNS-имени среды.
Что это даёт? У нас есть .sh скрипт подобного вида (оставляю только суть):

	# ... здесь заголовки отвечающие за ключи, help, реакцию на отсутствие параметров итд
	par_file = ${3//.WORLD/}
	sqlplus $1/$2@$3 @deploy.sql $par_file
	# ... здесь отработка выхода с ошибкой
	


На вход скрипта (в данном упрощённом варианте, когда у вас только одна схема) приходит три параметра: логин, пароль и TNS-алиас. Учитывая что многие прописывают

names.default_domain = world

то приводим всё к единому формату (в нашем случае: отрезаем потенциально появившийся ".WORLD"). В итоге остается наше имя файла параметров (по названию TNSalias) которое надо будет вызвать.

Остается для полноты картины написать пример самого deploy.sql

	-- Создаём лог файл 
	spool _deploy.log
	-- запускаем набор подстановок на основании переданного 
	-- нам par_file в первом парамере вызова:
	@./defines/&1

	@../ddl/some_table_create.sql
	@../data/some_other_deploy_activity.sql

	@./validate_invalid_objects.sql
	@./run_post_deployment_checks.sql

	spool off
	exit
	


Вводя подобный стандарт в ваш процесс разработки вы со временем раз и навсегда забываете о разнице в ваших окружениях, что поверьте мне, сильно уменьшает «головную боль» в последний момент — в момент deployment-а.

PS: Хотелось бы поделиться с хабросообществом некоторыми навыками и best-practices, приобретенными мною за годы работы с базами данных. Хотелось бы открыть цикл коротких статей основанный на примерах c разборами тех или иных интересных, на мой взгляд, случаев, задач и подводных камней с которыми мне приходилось сталкиваться.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 11

    0
    Если у вас в разных средах разные данные, то с большой долей вероятности скрипт конвертации, отлаженный на тестовой среде, что-то сделает не так и/или упадёт в UAT/продакшене. Плавали, знаем.
      0
      Согласен, такое возможно. В данном контексте разговор больше об отличиях в структурах данных, а точнее инфраструктуре (tablespaces, database links, directory, schema name etc etc).
      В случае данный применять условную компиляцию тоже можно… но это уже совсем другая история, как говорится.
      И да, хотя бы UAT крайне стоит организовывать максимально подобным к Prod-окружению. Тем более что в ряде организаций у вас на Prod не будет доступа, даже r\o (к сожалению).
      +1
      А я вот люблю невозможное — когда SQL скрипты максимально независимы даже не от окружения родной СУБД, а от СУБД в принципе. Но тут макро-подстановка переменных уже не спасет, приходится в деплой-скрипты писать на активном языке, парсая исходные SQL и заменой и регулярными выражениями. Воистину, в аду для перфекционистов просто немного неровно стоят щербатые котлы…
        +1
        Извините, не удержался.
        [режим простых истин on] Делая приложение независимым от СУБД вы либо теряете бенефиты, которые предлагает та или иная СУБД (перфоманс как следствие) либо, погружаетесь в условную компиляцию или уровень абстракций от СУБД, пишите много кода, чтобы ваше приложение одинаково хорошо работало с _нексколькими_ выбранными СУБД. [режим простых истин off]
        Хотя, признаю, бывают (редкие) случаи когда БД испольуется как черный ящик «тупо для хранения» и к СУБД возлагается минимум специфических требований.
          0
          Есть продукты, например финансовые и банковские, которые должны иметь альтернативные СУБД и работать не хуже на них. Это вопрос покупки дорогостоящих лицензий и интеграции с уже написанной сложной инфраструктурой (то есть — от нас не зависит в общем то). А вести два-три скрипта загрузки по 100 таблиц и синхронно их менять — нужно иметь очень крепкие нервы. Поэтому, проще придумать свое расширение синтаксиса SQL, которое перед загрузкой в определенную СУБД нужно «откомпилировать» своей специальной программой. По сути сделать такой компилятор — это дешевле, чем вести версии для разных систем и избавляет от человеческого фактора. После компиляции SQL уже жестко привязан к СУБД с учетом оптимизации и платформенных особенностей, без которых, как Вы правильно говорите, невозможно сделать хорошего приложения. Чем-то конечно нужно будет жертвовать, например, мы еще не дошли до того, чтобы в разных СУБД было разное кол-во таблиц или связей в зависимости от особенностей.
            0
            No offence, я лишь вторю Кайту, в той части, где он говорит про должное понимание особенностей (и разницы этих особенностей) субд: это не только синтаксис, это куда более важная разница в реализации MVCC которая [потенциально] ведет к необходимости по-разному реализовывать логику самого приложения.
            Видимо вы пошли по второму пути, где учитываете эти особенности, оборотной стороной этой медали миритесь с возросшей сложностью.

            Что за продукт, если не секрет, к которому такие требования?
              +1
              Мне доводилось три проекта делать подобным методом, систему сбора и консолидации данных с телеметрии, поддержку принятия решений для debt-collection, и сейчас консультирую самый простой из них — «Кампус» для ВУЗа (КПИ). Использовались соответственно: Informix+Interbase+DB/2 (давно было), MsSQL+Oracle, MsSQL+MySQL. Компилятор не так сложен, 5кб регулярных выражений. В предыдущих проектах компиляторы были огромными — это правда, но только по недостатку опыта.
        +1
        Хороший паттерн, используем, плюсую.

        На текущем проекте пошли еще дальше и активно юзаем переменные окружения — по аналогии с приведенным выше, это как если бы все дефайны заполнялись из них. Это удобно, например для того, когда один application server фактически шарит между собой несколько окружений (dev, qa, uat) и нужно передавать приложениям разных окружений начальные коннективити данные (сервер, инстанс, схема) + если у вас фактически в одном инстансе крутится несколько разных окружений (dev, qa, uat). Переменные окружения в этом случае, добавляя гибкости, выполняют роль профиля, который легко подсосать и приложениям, и легко передать в деплоймент скрипт (через параметры) который развернет базу. Таких профилей создается по количеству энвайронментов, выбор происходит соурсингом соответствующего .sh файла (например удобно через менюшку в ~$APPLICATION_USER/.profile на этапе логина + shell альясы типа setdev2).

        Из других deployment-related полезностей — «обкатка» в CI среде патч- и ролбек- скриптов (которыми во время релиза докатывается/откатывается prod база). Для этого настраивается отдельный энвайромент, на котором автоматом (по шедулеру, по коммиту) сначала разворачивается последний прод клон, потом применяется набор патчей для разрабатываемой новой версии, потом применяются ролбеки и снова патчи (back-and-forth, back-and-forth) — т.о. гарантируется некая повторяемость, консистентность и атомарность релиза в части изменения БД.
          +1
          Да, CI среда для базы — давно моя «мечта», даже скрипты для этого подготовил, но нам постоянно не хватает по мнению менеджмента «свободного» окружения для этого и куча бюрократической волокиты с DBA- командой, которая поддерживает наши сервера и от них зависит настройка выгрузки прода. Но идея очень правильная, поддерживаю всеми руками и плюсую!
            +1
            Окружений никогда не хватает, факт, и бюрократия доставляет, согласен. Поэтому я и упомянул идею когда один физический oracle инстанс шарится между разными окружениям. Например CI может быть на том же инстансе где у вас уже есть DEV, для разделения схем можно использовать суффиксы / префиксы — например HR_DEV1, HR_DEV2, HR_CI — три схемы в одном инстансе которые относятся к разным энвайроментам. ДБАшники могут и не заметить чита, их лишь надо попросить аккаунтов создать, а тейблспейсы для простоты поддержки можно заюзать существующие. Придется мириться правда (возможно) с некоторым даунгрейдом перформанса, но зато «в теле такая приятная гибкость образовалась» (с) :-)

            По поводу выгрузки прода, совсем не обязательно каждый раз обновлять CI из свежеиспеченного физического дампа прода. Если предположить что структура прода между релизами не меняется (исключение — если quick fix выкатываете) то дамп можно сделать сразу после релиза, и потом его использовать вплоть до следующего релиза. Здесь исхожу из предположения, что данные, их свежесть, для обкатки не так важны.
            А если подумать в сторону логического дампа, который требует только SCHEMA OWNER чтобы подропать все что было и пересоздать заново, то ДБАшники точно ничего не узнают :-)

            Буду с интересом следить за следующими вашими статьями, пишите!
          0
          Я для таких целей чаще использую ant/maven. На вход параметры либо через коммандную строку либо через properties-файл, внутри вызывается sqlplus / sqlcmd / mysql / psql / whatever предварительно заменив все плейсхолдеры параметрами из конфига. Иногда используется комбинированный подход — maven'ом/ant'ом формируется, то что у автора называется par_file на базе параметров командной строки / ввёдных в интерактивном режиме значений / whatever, затем просто вызываются всё те же sqlplus / sqlcmd… В этом случае можно запускать как через ant/maven, так и без них, что удобно как для CI, так и для production версии.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое