Обстановка в мире и разработка софта

Мы живем в удивительное время. Конечно, каждое время имеет свои удивительные особенности, но разгул всяческих несуразиц, происходящих сегодня повсеместно, не может не заставить задуматься об исключительности сегодняшнего дня. Возьмем, к примеру, такую далекую от политики область, как разработка софта. Казалось бы, как обстановка в мире, кроме, конечно, глобальной ядерной войны с последующим всеобщим каменным веком и вымиранием, может на нее повлиять? Оказывается, может.

Вот как было раньше? Раньше решения об используемых при разработке инструментах, например, принимались исходя из потребностей задачи, финансовых возможностей, конкурентных преимуществ, наличии компетентной команды разработчиков, уровне техподдержки, основательности компаний-производителей, из личных привязанностей, в конце концов[1], то есть, было какое-то логическое начало во всем этом. Если речь не шла о производстве софта, используемого в чувствительных к гос. тайне и безопасности областях, никто не смотрел, откуда (из какой страны) компания-производитель родом, где она зарегистрирована, где размещены ее центры разработки.

Сегодня же, не смотря на то, что санкционная политика в области информационных технологий практически не коснулась материковой России, в процессы принятия решений стали вмешиваться различные геополитические аспекты и, как следствие, исходящие от различных гос. учереждений и чиновников указаний об, скажем так, пригодности инструментов разработки тех или иных производителей. И, как водится в таких случаях, обычная логика принятия решений стала страдать. Еще бы, на нее стал воздействовать бюракратический аппарат государства, обладающий своей, особой, логикой, не всегда понятной простыми труженниками клавиатуры и мышки.

В результате, у простых разработчиков возникает головная боль, связанная с миграцией с одних продуктов на другие. И пусть этот процесс иногда даже является определенным развитием, но он всегда связан с проблемами и трудностями - тут и отсутствие опыта использования новых продуктов, и разные характеристики новых и старых инструментов, и необходимость обеспечения обратной совместимости, требования от конечных пользователей, чтобы "было как раньше". Не зря ведь некоторые программные, да и аппаратные, средства используются по многу лет, не смотря на прогресс "железа", платформ и идеологий разработки - они просто надежно работают, обеспечивая все потребности конечных пользователей.

Немного про миграцию

Так вот, о миграции. Наверное в связи со всей этой обстановкой в мире, а может и по каким-то другим причинам, было принято решение постепенно переходить на использование продуктов от компаний, которые, на взгляд принимающих этот курс, менее подвержены политической турбулентности. Миграция - процесс не быстрый, требующий аккуратности, внимательности и вдумчивости. В каждом конкретном случае приходится принимать во внимание массу факторов, например, разработывается продукт с нуля или это продолжение ранее начатой разработки, как он связан с другими системами, каков план перевода этих систем и так далее и тому подобное.

Немного про наш случай. Весь софт, разработка которого велась в нашей компании своими силами, или заказывался "на стороне", так или иначе связанный с использованием СУБД, был основан на продуктах Oracle. Позиции этой компании, как надежного поставщика, были подвергнуты сомнениям в связи с осложнениями взаимоотношений с нашими западными партнёрами, и началась миграция. В случае с новыми продуктами было решено, в основном, использовать open source СУБД Postgress. Системы же, которые начали разрабатываться до всех этих катаклизмов, так и продолжали строиться на БД Oracle, но компетентные сотрудники стали осматриваться, чем их можно заменить. И тут их взгляд упал на продукт корейской компании TMaxSoft под названием Tibero.

Если честно, я раньше не слышал ничего ни о самой компании, ни о её продукте. Но, в конце концов, в последнее время моя профессиональная деятельность больше была связана с использованием Java, практически без использования СУБД, так что, может, я что-то и пропустил.

Миграция - серверная часть

В любом случае, коллеги стали переходить на СУБД Tibero, и, первое время (а происходить это стало года полтора - два назад) я слышал достаточно много охов и вздохов, и, честно говоря, отнюдь не восхищения. Но кое-что меня удивило, а именно - речь шла о практически прямом переводе серверного кода с одной СУБД на другую. То есть, под Tibero портировался PL/SQL код, все вот эти пакеты, хранимые процедуры, тригеры и так далее и тому подобное. При этом вздохи разочарования раздавались тогда, когда код, написанный под (и для) Oracle не хотел компиллироваться или исполняться под Tibero. Но через некоторое время застопорившийся было процесс сдвигался с мёртвой точки, и двигался дальше до следующей несовместимости.

То есть, тех. поддержка TMaxSoft внимательно выслушивала клиента, вникала в проблему и либо указывала на не совсем верные действия, либо предлагала обходной путь, либо - передавала вопрос в разработку, откуда достаточно оперативно приходили патчи. Если честно, я не в курсе финансовой части сотрудничества, но оперативность и качество поддержки меня впечатляло. Постепенно количество вздохов уменьшалось и в какой-то момент PL/SQL код оказался портирован полностью. Ну, или, почти полностью. Я отметил про себя сей занятный факт, и на какое-то время забыл о существовании Tibero. Мог ли я тогда предположить, что спустя некоторое время сам начну использовать эту СУБД?!

Миграция - клиентская часть

Ну как - "начну", это достаточно громкое определение. Сейчас у меня такой период, когда я не очень-то и использую всевозможные СУБД - "пилю" проект на Java по организации взаимодействия различных систем между собой, передачи данных между ними. И тут, когда системы хранят информацию в различных БД, как раз и приходится делать запросы в эти самые базы (если не предусмотрен какой-нибудь другой способ получить эти данные).

Соединение с БД

Итак, система, которая ранее хранила данные в БД Oracle "переехала" на БД Tibero. А данные из этой системы я получаю, выполняя прямые запросы. Процедура, примерно, такая: для моего ПО создаётся пользователь, которому предоставляют определённые права к объектам БД, под этим пользователем производится соединение с БД, а затем выполняется переключение под позователя-владельца[2] объектов (он делается пользователем по умолчанию). Это делается, в частности, для упрощения запросов и их унификации - экземпляров БД много, не везде всё называется одинаково, а так, можно хотя бы имя пользователя-владельца объектов в запросах не писать.

В Oracle для установки текущего пользователя по умолчанию используется следующая конструкция:

ALTER SESSION SET CURRENT_SCHEMA=имя_схемы

где имя_схемы - имя пользователя-владельца объектов (хорошо, хорошо, имя схемы, которой принадлежат нужные объекты БД).

И первый вопрос, возникший у меня, был: сработает ли такой подход в Tibero, или придётся городить какой-нибудь огород с использованием синонимов, например. Честно говоря, я испытал облегчение и удовлетворение, когда эта конструкция отработала и в Tibero.

Выполнение анонимного блока

Простые запросы отработали тоже без каких-либо замечаний, а дальше... дальше пришёл черёд более серьёзных испытаний. Вот одно из них:

DECLARE  
   v_ret NUMBER;   
   v_ret_msg VARCHAR2(512);
   row_locked EXCEPTION;
   PRAGMA EXCEPTION_INIT(row_locked, -54);
   begin 
   BEGIN
   execute immediate 'select * ' ||
   'from table_name ' ||
   'where column_name_1 = :parameter_1 ' ||
     'and column_name_2 = :parameter_2 ' ||
   'for update of column_name_1 nowait'
   using :parameter_1, :parameter_2;
   
   update table_name set 
     column_name_1 = :parameter_3, 
     column_name_3 = sysdate
   where column_name_1 = :parameter_1 
     and column_name_2 = :parameter_2;
   if SQL%ROWCOUNT = 0 then
     raise NO_DATA_FOUND;
   end if;                                                                
   v_ret := 0;
   v_ret_msg := '';
   exception
     when OTHERS then
       v_ret := SQLCODE;
       v_ret_msg := SQLERRM; 
   end;
   :parameter_4 := v_ret;
   :parameter_5 := v_ret_msg;
END;

Смысл данного анонимного блока достаточно прост. Делается попытка заблокировать запись - на тот случай, если какой-то другой процесс работает с этими данными, мы просто хотим выйти с ошибкой. Если блокировка удалась, то мы изменяем заблокированную нами запись. Если запись измененилась, значит, всё хорошо, и мы запоминаем этот факт. Если же ни одна запись не изменилась, это может говорить о том, что кто-то где-то данные уже поменял - операции-то не атомарные, мало ли что. В этом случае мы сообщаем, что нужных данных не нашли. В случае любой ошибки мы фиксируем её, ну и выходим из блока, передав в выходных параметрах код и сообщение результата выполнения.

В этом небольшом куске кода есть много мест, где можно споткнуться при переезде на другую СУБД. Тут и анонимный блок сам по себе, и выполнение строки как запроса (для блокировки), и использование выходных параметров. Как вы думаете, сработал этот анонимный блок в Tibero, или нет? И правильный ответ... Нет!!! При попытке выполнить этот блок выдавалась следующая ошибка:

JDBC-15146:PSM compilation error.

Не буду утомлять перечислением того, как и что пришлось делать, чтобы найти ошибку[3]. Главное, в конечном итоге, то, что ошибка нашлась. И ошибка возникала в самом "ненужном" и необязательном месте.

Обратили внимание, что был описан Exception c именем row_locked? Это исключение ещё было ассоциировано с ошибкой Oracle с кодом -54 и... нигде не использовалось. Именно ассоциирование этого исключения с кодом и явилось причиной неработоспособности анонимного блока при миграции. Оказалось, что коды ошибок, используемые в Tibero, отличаются от кодов таких же по смыслу ошибок в Oracle. Я думаю, сделано это специально, видимо, по каким-то лицензионным причинам. В результате, при попытке выполнить этот блок, и выдавалась ошибка[4].

В общем, чтобы всё заработало, мне пришлось лишь изменить код ошибки, после чего строка стала выглядеть так:

...
   PRAGMA EXCEPTION_INIT(row_locked, -12033);
...

Немного про JDBC драйвер

Надо сказать, все остальные запросы выполнились, что называется, "на ура". Но запросы - не единственный возможный источник проблем. Ещё одна несовместимость всплыла при использовании jdbc драйвера от Tibero.

В программе есть код, который сохраняет данные из LOB объектов в файлы на диске. В этом коде открываются и закрываются потоки (например, BufferedInputStream), которые используются для получения данных из LOB объектoв. И после (по моменту исполнения кода) освобождаются ресурсы LOB при помощи вызова метода free.

Этот код нормально работал с jdbc драйвером Oracle, но при использовании драйвера Tibero стал приводить к возникновению исключения при вызове метода free - сообщение гласило, что используется неверная ссылка на LOB объект. По всей видимости, jdbc драйвер Tibero написан таким образом, что при закрытии потока, используемого для чтения из LOB объекта, он автоматически освобождает ресурсы, связанные с этим объектом. На мой взгляд, эта логика не совсем корректна. Такое поведение меня несколько расстроило, но не задержало (я стал фиксировать факт возникновения ошибки, но перестал прерывать выполнение программы), и, вскоре, программа была передана в тестирование.

Немного про XML

На этом очень важном этапе тоже всплыла некоторая особенность Tibero - выяснилось, что две СУБД по разному генерируют XML тексты. Так, Oracle правильно работает с символами, требующими генерации так называемых escape последовательностей, например, встретив символ " (двойные кавычки) он заменяет их на специальную последовательность ". Tibero же не заморачивается - в текст XML двойные кавычки попали, как есть (возможно, он просто был недонастроен, или настроен неправильно). И всплыло это только при попытке обработать XML в системе-получателе. Но эту особенность так же быстренько исправили[5], после чего никаких-либо других проблем, которые могли бы помешать переводу системы в эксплуатацию (production), найдено не было.

Заключение

В общем, могу сказать, что у меня осталось чувство лёгкого удивления от того, что всё прошло достаточно гладко, и я, если честно, зауважал разработчиков Tibero - не могу сказать, как эта СУБД в реальной эксплуатации себя проявит, но то, что сделано очень много для обеспечения совместимости с Oracle - это однозначно.


  1. Про всякие разные другие факторы, определяющие выбор, скромно промолчим ↩︎

  2. В Oracle вместо "пользователь" используется термин "схема" ↩︎

  3. На самом деле, я просто исключал из анонимного блока составляющие его части - до тех пор, пока ошибка не пропала 😎 ↩︎

  4. На самом деле, это была, скажем так, внешняя ошибка, под ней скрывалась более точная ошибка - 15069: Invalid DBMS error code for pragma exception_init. ↩︎

  5. Исправлял не я, генерация XML делалась в системе-источнике данных, так что, что именно было сделано, увы, сказать не могу. ↩︎