Привет! Как и обещала в предыдущем посте про Edition-Based Redefinition — вот вторая часть.
Итак, с чем мы работаем? Наш основной production сервер — это Oracle 12С, Enterprise Edition. И, что важно отметить, на нем работает несколько десятков приложений одновременно. Почему мы заостряем на этом внимание? Технология относительно новая, она не совсем хорошо обкатана. И было бы нелогичным переводить на нее сразу какие-то критичные системы. Поэтому для себя решили, что будем потихоньку идти от менее критичных систем к более критичным. Соответственно, следующая проблема, которую нам необходимо было понять: как работать c технологией EBR и как организовать интеграцию в той ситуации, когда у нас одна схема версионирована, а другая — нет. В 12 версии Oracle, как оказалось, можно создавать неверсионные объекты, неверсионированные пакеты, неверсионированные представления в версионированной схеме для организации той самой интеграции.
Понятное дело, мы можем создать неверсионированное API и отдать его неверсионной схеме для использования. Но что если это неверсионное API (или его часть) используется внутри нашего приложения и нам необходимо его внутри себя версионировать, а на сторону отдавать неверсионирующие объекты? Вот это как раз следующая проблема, с которой мы боролись. У нас многократно используется API, но, как вы помните, есть ограничение того, что версионируемые объекты не могут быть используемыми неверсионируемыми. Естественно, при условии того, что на сервере крутится несколько приложений, нужно было понять, как мы будем переходить одним приложением на новый Edition и сохранять возможность использования API этой схемы другими схемами БД.
Есть несколько вариантов установить Edition:
Эти варианты отмели сразу по причине того, что, как минимум, некоторые приложения не будут переходить на использование EBR.
Также есть возможность создать сервис для определения версии, либо же определить версию, при подсоединении приложения к схеме. Это удобно, если мы коннектимся к базе через какое-то стороннее приложение.
А если нам необходимо, чтобы job-ы запускались по расписанию и работали в какой-то определенной редакции? Соответственно, есть еще один вариант переключения на новую версию — это ориентировать текущую сессию на конкретную версию.
Собственно, это для себя мы и выбрали в качестве решения. На версионируемую схему можно повесить after logon триггер, который будет говорить, что текущая сессия в данной схеме будет работать вот в этом еdition.
Возвращаясь к интеграции и дублировании логики. Есть задачи: хочется версионировать какую-то логику приложения внутри своей схемы, а также необходимо выставить ее неверсионному пользователю. Сначала казалось, что это не получится реализовать, но, углубившись в вопрос, посмотрели, что обычный пакет dbms_sql, который в принципе нацелен на выполнение каких-то динамических запросов, может помочь в решении данной проблемы. Как? Очень просто — мы можем при обработке запроса или вызова любого анонимного блока подкинуть имя еdition’а в качестве параметра, указав тем самым версию, в которой будет парситься и исполняться данный код. Если нам необходимо какую-то процедуру использовать внутри себя и эту же процедуру отдать сторонней схеме, мы просто оборачиваем ее вызовом в процедуре dbms_sql.parse, создавая таким образом обертку, которую можно положить в неверсионный объект, и — пожалуйста, нашим версионным объектом может пользоваться неверсионнный пользователь.
С чем здесь столкнулись? Почему-то при указании еdition не выдаются out-параметры в процедурах. Почему — непонятно, но в тех схемах, в которых мы работали, это не используется часто. Может, как-то переделаем логику, либо будем искать какие-то дальнейшие решение.
Следующие проблемы — баги или особенности EBR, с которыми мы столкнулись. Первое, это проблема с конструируемыми типами и конвейерными функциями. При чем же здесь Pipeline? Помимо того, что у нас есть действительно некие алгоритмы, работающие на основе конвейерных функций, Pipeline — это некое решение для выставления версионируемой вьюхи на сторону. В наследие нам достались вьюхи, которые содержат в себе довольно сложную логику по предварительной обработке данных, и этими вьюхами пользуются также сторонние схемы. Соответственно, нужно было понять, как выставить наши версионирующие вьюхи неверсионной схеме- потребителе данных при условии того, что выходной набор колонок не будет меняться. В качестве некого такого решения было понятно, что можно обернуть все эти вью при помощи dbms_sql в Pipeline функцию и положить в конечном итоге во вьюху для потребителя, да, это ресурсозатратно для сервера, но это бы не потребовало доработок со стороны принимающей системы.
Так вот, возвращаясь к использованию конвейерных функций, оказалось, что если в имеющемся пакете версии не 0 создается конструируемый тип, и он конструируется впервые, то пакет просто не компилируется и разваливается. Сразу решение не пришло, отправились искать, что же было предложено собратьями программистами? Кто-то сказал, что требуется в нулевой версии пакета создать этот тип, либо в нулевой версии базы данных создать пакет с соответствующим типом. Было не совсем понятно, зачем это делать. Отыскали решение, что вот эти типы, которые неявно создаются при компиляции пакета, можно было просто вынести в отдельный тип как объект базы данных. Тем самым проблема с конвейерными функциями решается.
Следующая особенность, на которую мы наткнулись — это нестандартное поведение версионирующих представлений.
Помните, я говорила о том, что происходит наследование объектов и их актуализация? Так вот, оказалось, что если мы с вами создали версионируещее представление на неком условно нулевом edition, к пятому edition мы заметили, что у нас неточности в комментариях. Например, я за собой замечаю, что в комментарии на колонку могу две буквы местами поменять. Не оставлять же такое несовершенство!
Так вот, обычная команда comments приводит к тому, что у нас версионирующее представление актуализируется в текущей версии. Из-за этого разваливаются все зависимые пакеты, и как с этим бороться? Для этого написали некий скрипт, который при создании нового еdition будет актуализировать версионирующее представление всякий раз при выпуске очередного релиза. Большой нагрузки на словарь БД это не несет, зато при необходимости сделать мелкие исправления или, к примеру, выдать новые гранты, нам не потребуется создавать новый edition
Ну и последний такой баг или особенность … Непонятно почему, при изменении not null ограничения на колонке версионирующие представления разваливались. То есть, как только мы говорим, что наша колонка базовой таблицы теперь должна иметь ограничения not null, разваливается вьюха даже при использовании dbms redifinition. Данную вещь мы не смогли никак победить, возможно, читатели с этим сталкивались, будет интересно узнать, найдено ли решение.
Что хотелось бы сказать в заключение? Edition-Based Redefinition — это действенная технология для реальной возможности накатить релиз либо хотфикс в режиме онлайн, в режиме работы пользователей. Мы потрогали, пощупали, поняли, что организовать интеграцию на одном сервере, когда не все схемы будут переезжать на использование данной технологии — реально, не говоря уже о том, что версионируемая схема может работать на выделенном сервере или на отдельной контейнерной БД.
А в качестве применения, и ответа на ключевой вопрос, «можно ли в продакшн?»… Мы надеемся, что Edition-Based Redefinition переедет рано или поздно во все наши проекты, и, возможно, это будет последней остановкой работы наших приложений и гарантией спокойного сна разработчика, отвечающего за установку нового релиза.
Собственно, все. Спасибо за внимание.
Итак, с чем мы работаем? Наш основной production сервер — это Oracle 12С, Enterprise Edition. И, что важно отметить, на нем работает несколько десятков приложений одновременно. Почему мы заостряем на этом внимание? Технология относительно новая, она не совсем хорошо обкатана. И было бы нелогичным переводить на нее сразу какие-то критичные системы. Поэтому для себя решили, что будем потихоньку идти от менее критичных систем к более критичным. Соответственно, следующая проблема, которую нам необходимо было понять: как работать c технологией EBR и как организовать интеграцию в той ситуации, когда у нас одна схема версионирована, а другая — нет. В 12 версии Oracle, как оказалось, можно создавать неверсионные объекты, неверсионированные пакеты, неверсионированные представления в версионированной схеме для организации той самой интеграции.
Понятное дело, мы можем создать неверсионированное API и отдать его неверсионной схеме для использования. Но что если это неверсионное API (или его часть) используется внутри нашего приложения и нам необходимо его внутри себя версионировать, а на сторону отдавать неверсионирующие объекты? Вот это как раз следующая проблема, с которой мы боролись. У нас многократно используется API, но, как вы помните, есть ограничение того, что версионируемые объекты не могут быть используемыми неверсионируемыми. Естественно, при условии того, что на сервере крутится несколько приложений, нужно было понять, как мы будем переходить одним приложением на новый Edition и сохранять возможность использования API этой схемы другими схемами БД.
Есть несколько вариантов установить Edition:
- установить значение Edition по умолчанию на всю базу данных
- установить среду окружения, опять же на всю базу данных.
Эти варианты отмели сразу по причине того, что, как минимум, некоторые приложения не будут переходить на использование EBR.
Также есть возможность создать сервис для определения версии, либо же определить версию, при подсоединении приложения к схеме. Это удобно, если мы коннектимся к базе через какое-то стороннее приложение.
А если нам необходимо, чтобы job-ы запускались по расписанию и работали в какой-то определенной редакции? Соответственно, есть еще один вариант переключения на новую версию — это ориентировать текущую сессию на конкретную версию.
Собственно, это для себя мы и выбрали в качестве решения. На версионируемую схему можно повесить after logon триггер, который будет говорить, что текущая сессия в данной схеме будет работать вот в этом еdition.
Возвращаясь к интеграции и дублировании логики. Есть задачи: хочется версионировать какую-то логику приложения внутри своей схемы, а также необходимо выставить ее неверсионному пользователю. Сначала казалось, что это не получится реализовать, но, углубившись в вопрос, посмотрели, что обычный пакет dbms_sql, который в принципе нацелен на выполнение каких-то динамических запросов, может помочь в решении данной проблемы. Как? Очень просто — мы можем при обработке запроса или вызова любого анонимного блока подкинуть имя еdition’а в качестве параметра, указав тем самым версию, в которой будет парситься и исполняться данный код. Если нам необходимо какую-то процедуру использовать внутри себя и эту же процедуру отдать сторонней схеме, мы просто оборачиваем ее вызовом в процедуре dbms_sql.parse, создавая таким образом обертку, которую можно положить в неверсионный объект, и — пожалуйста, нашим версионным объектом может пользоваться неверсионнный пользователь.
С чем здесь столкнулись? Почему-то при указании еdition не выдаются out-параметры в процедурах. Почему — непонятно, но в тех схемах, в которых мы работали, это не используется часто. Может, как-то переделаем логику, либо будем искать какие-то дальнейшие решение.
Следующие проблемы — баги или особенности EBR, с которыми мы столкнулись. Первое, это проблема с конструируемыми типами и конвейерными функциями. При чем же здесь Pipeline? Помимо того, что у нас есть действительно некие алгоритмы, работающие на основе конвейерных функций, Pipeline — это некое решение для выставления версионируемой вьюхи на сторону. В наследие нам достались вьюхи, которые содержат в себе довольно сложную логику по предварительной обработке данных, и этими вьюхами пользуются также сторонние схемы. Соответственно, нужно было понять, как выставить наши версионирующие вьюхи неверсионной схеме- потребителе данных при условии того, что выходной набор колонок не будет меняться. В качестве некого такого решения было понятно, что можно обернуть все эти вью при помощи dbms_sql в Pipeline функцию и положить в конечном итоге во вьюху для потребителя, да, это ресурсозатратно для сервера, но это бы не потребовало доработок со стороны принимающей системы.
Так вот, возвращаясь к использованию конвейерных функций, оказалось, что если в имеющемся пакете версии не 0 создается конструируемый тип, и он конструируется впервые, то пакет просто не компилируется и разваливается. Сразу решение не пришло, отправились искать, что же было предложено собратьями программистами? Кто-то сказал, что требуется в нулевой версии пакета создать этот тип, либо в нулевой версии базы данных создать пакет с соответствующим типом. Было не совсем понятно, зачем это делать. Отыскали решение, что вот эти типы, которые неявно создаются при компиляции пакета, можно было просто вынести в отдельный тип как объект базы данных. Тем самым проблема с конвейерными функциями решается.
Следующая особенность, на которую мы наткнулись — это нестандартное поведение версионирующих представлений.
Помните, я говорила о том, что происходит наследование объектов и их актуализация? Так вот, оказалось, что если мы с вами создали версионируещее представление на неком условно нулевом edition, к пятому edition мы заметили, что у нас неточности в комментариях. Например, я за собой замечаю, что в комментарии на колонку могу две буквы местами поменять. Не оставлять же такое несовершенство!
Так вот, обычная команда comments приводит к тому, что у нас версионирующее представление актуализируется в текущей версии. Из-за этого разваливаются все зависимые пакеты, и как с этим бороться? Для этого написали некий скрипт, который при создании нового еdition будет актуализировать версионирующее представление всякий раз при выпуске очередного релиза. Большой нагрузки на словарь БД это не несет, зато при необходимости сделать мелкие исправления или, к примеру, выдать новые гранты, нам не потребуется создавать новый edition
Ну и последний такой баг или особенность … Непонятно почему, при изменении not null ограничения на колонке версионирующие представления разваливались. То есть, как только мы говорим, что наша колонка базовой таблицы теперь должна иметь ограничения not null, разваливается вьюха даже при использовании dbms redifinition. Данную вещь мы не смогли никак победить, возможно, читатели с этим сталкивались, будет интересно узнать, найдено ли решение.
Что хотелось бы сказать в заключение? Edition-Based Redefinition — это действенная технология для реальной возможности накатить релиз либо хотфикс в режиме онлайн, в режиме работы пользователей. Мы потрогали, пощупали, поняли, что организовать интеграцию на одном сервере, когда не все схемы будут переезжать на использование данной технологии — реально, не говоря уже о том, что версионируемая схема может работать на выделенном сервере или на отдельной контейнерной БД.
А в качестве применения, и ответа на ключевой вопрос, «можно ли в продакшн?»… Мы надеемся, что Edition-Based Redefinition переедет рано или поздно во все наши проекты, и, возможно, это будет последней остановкой работы наших приложений и гарантией спокойного сна разработчика, отвечающего за установку нового релиза.
Собственно, все. Спасибо за внимание.