Хранимый код в базе? Не смешите, на дворе 2017 год!
В этом году бренду QIWI исполнилось 10 лет. За это время в нашей основной транзакционной базе накопилось более чем 130 тысяч строк хранимого PL/SQL кода. На Хабре регулярно встречаются статьи о том, как различные команды разработчиков категорически не используют хранимый код в БД, стараясь убрать излишнюю нагрузку с БД и таким образом удешевить систему. По этой теме можно долго дискутировать, и такая точка зрения опровергается, например, вот в этом видео.
Что бесспорно — хранимый PL/SQL код традиционно имел один существенный минус: релиз PL/SQL программы требовал остановки сервиса, поскольку процесс компиляции этого кода должен был получить эксклюзивную блокировку в словаре БД (так называемый library cache pin). Не вовремя запущенная случайная рекомпиляция могла подвесить всю систему. Приходилось регулярно выделять технические окна для релиза PL/SQL кода. Заверенные скриншоты жалоб наших возмущённых клиентов, попавших в такие окна, бережно хранятся в наших архивах. Однако не прошло и 20 лет от создания PL/SQL, как Oracle этот недостаток если не устранил полностью, то существенно смягчил.
Welcome to Oracle Edition-Based Redefinition
Мы не будем приводить детальные примеры кода с использованием Edition-Based Redefinition, а опишем несколько ключевых пунктов проекта по его внедрению. С некоторой натяжкой этот механизм, который принято сокращать до EBR, можно считать системой контроля версий объектов БД внутри самой БД. Теперь приложения способны работать с разными версиями одних и тех же процедур, пакетов и представлений. Однако в БД, кроме кода, есть еще и структуры данных в виде таблиц, и Oracle пришлось придумать способ межверсионной трансформации как самих таблиц, так и данных в них.
Сразу оговоримся, что наши разработчики используют EBR только для представлений (view) и PL/SQL кода, и не используют для таблиц. Предметная область хорошо изучена и структуры данных вполне стабильны. В течение года столбцы в горячих таблицах менялись или добавлялись от силы раз пять, при этом изменений кода было в десятки раз больше.
Приложение
Наше Java-приложение умеет само переключаться на использование новой версии PL/SQL кода. Текущий edition можно извлечь из базы таким нехитрым запросом:
select property_value
from database_properties
where property_name = 'DEFAULT_EDITION'
Приложение хранит это значение и регулярно опрашивает базу, не изменилось ли оно.
Успешный релиз новой версии PL/SQL кода выполняет команду вида
alter database default edition = ED_1180_23185307
а приложение, узнав, что edition изменился, в подходящий момент выполняет команду вида
alter session set edition = ED_1180_23185307
и тем самым переключается на использование новой версии хранимого кода.
Теоретически возможен и откат PL/SQL кода на предыдущую версию – для этого надо выполнить команду alter database с установкой предыдущего edition, а приложение должно на него переключиться.
Баги
СУБД Oracle внутри крайне сложна, в её оптимизацию и развитие вложено столько человеко-лет, что любые новые возможности в её ядре не проходят безболезненно для остального функционала. Речь конечно же идёт о багах и устраняющих их патчах. EBR был вовсе не исключением, а, наоборот, существенным возмутителем спокойствия. Скажем так: без техподдержки обойтись невозможно.
К сожалению, отдельного списка патчей, устраняющих связанные с EBR баги, Oracle не ведёт. Однако Oracle активно использует EBR в одной из своих популярных ERP-систем – Oracle E-Business Suite (OEBS). Поэтому можно взять набор патчей, который Oracle рекомендует к установке на базу OEBS, и установить на вашу базу те из них, которые потенциально наиболее вероятны для вашего приложения. Найти его можно на сайте поддержки Oracle в Section 3 документа Oracle E-Business Suite Release 12.2: Consolidated List of Patches and Technology Bug Fixes (Doc ID 1594274.1)
Подводные камни
При работе с Oracle Edition-Based Redefinition мы нашли четыре недостатка:
- Ограничение на число editions, равное 2000. Со скоростью 2 релиза в неделю мы исчерпаем их за 20 лет. Надеемся, к тому времени Oracle сподобится-таки убрать это ограничение.
- Плоская, а не древовидная структура editions, 1 parent <–> 1 child. Нам это пока что никак не мешает.
- Не-версионируемые (non-editioned) объекты не могут ссылаться на версионируемые (например, в версии 11g такие объекты, как materialized view, являются non-editioned и не могут ссылаться на editioned view).
- Специфика в раздаче прав на версионируемый код.
На последнем пункте хочется остановиться поподробнее, так как этот эффект описан крайне скудно.
Дело в том, что выдача прав на версионируемый объект, последний раз изменявшийся в каком-либо предыдущем edition, копирует этот объект в текущий edition, со всеми уже знакомыми нам симптомами перекомпиляции и, если не повезет, зависаниями на словарной блокировке library cache pin. По всей видимости, это связано с внутренней реализацией editioned схем в БД.
Поэтому процедуру раздачи прав пришлось слегка изменить: сначала мы находим edition, в котором искомый объект был изменен последний раз, устанавливаем этот edition в нашей сессии с помощью вышеописанной команды alter session, и лишь после этого выдаем нужные права.
Как говорится, не баг 26654363, а expected behavior. Что ж, обходной путь не слишком трудозатратен и с ним в подавляющем большинстве случаев можно ужиться.
Итог проекта: минус 16 часов планового простоя в год
99.8% -> 99.98%
P.S. Мы ищем DBA и разработчиков БД!
Автор: PeterBobrov