Пишу, по большей части, про историю, свою жизнь и немного про программирование.

✨ Двойные миграции (Оракл+Постгрес)

У нас идёт замена в наших продуктах сильно платного «Оракла» (это база данных) на бесплатный «Постргес». Несмотря на ударные темпы, продолжаться это ещё будет долго. Продуктов много, несмотря на то, что начали мы ещё в прошлом году, весь этот год будем жить сразу на двух базах.

В этой связи, когда есть различия, приходится миграции писать в двух комплектах — под каждый диалект. Я придумал как писать их так, чтобы каждая БД видела куски кода, предназначенные только ей.

Например:

CREATE UNIQUE INDEX dn_part_num_org_n_cat ON document_n(/*/**/
CASE WHEN d_deleted = 0 AND num IS NULL AND n=0 AND category=0 THEN id END,
CASE WHEN d_deleted = 0 AND num IS NULL AND n=0 AND category=0 THEN org_id END);
--*/ id, org_id) WHERE d_deleted=0 AND num IS NULL AND n=0 AND category = 0;

Часть с /*/**/ и до —*/ видит только «Оракл» — для него это выглядит так: комментарий открывается, сразу закрывается, идёт код, который он и воспринимает, а последняя строка закоментирована при помощи двух минусов — это стандартный коментарий в эскуэле.

«Постгрес» эту часть не видит — он поддерживает вложенные коментарии, поэтому его интерпретация другая: открываются два комментария, первый закрывается сразу, а второй — на последней строке, остаток которой «Постгрес» воспринимает как часть кода.

30 комментариев
Dmitry V.Abramov (imap.livejournal.com) 2016

Красиво.

Старого воробья, обстрелянного браузерными несовместимостями, на мякине не проведешь!

Евгений Степанищев (bolknote.ru) 2016

Комментарий для imap.livejournal.com:

Ага, чем-то очень похоже на CSS-хаки, условные комментарии IE, вот это всё :) Надо бы, кстати, записывать трудности перехода. Я уже понатыкался на такие подробности, которые почему-то в интернетах совершенно не освещены.

Pavelpat 2016

Ужас какой. Это ведь не для продакшна, да? Хотя узнаю document_n.

PastorGL 2016

Жесть! >_<

Просто на месте убью любого, кто посмеет закоммитить подобный код в любом из проектов, где я участвую... Впрочем, спасибо за выпуклый пример как не надо делать — на следующем разборе полётов какого-нибудь джуниора с замашками ниндзюка будет что свеженького показать.

Евгений Степанищев (bolknote.ru) 2016

Комментарий для Pavelpat:

Ужас какой. Это ведь не для продакшна, да? Хотя узнаю document_n.

Это для продакшна, да. Миграция. В чём именно заключается ужас?

Евгений Степанищев (bolknote.ru) 2016

Комментарий для PastorGL:

Просто на месте убью любого, кто посмеет закоммитить подобный код в любом из проектов, где я участвую...

В этом месте было бы неплохо показать как надо делать.

Евгений Степанищев (bolknote.ru) 2016

Комментарий для PastorGL:

Конструктивной критикой было бы «вот как я справился бы с переводом продукта на полмиллиона строк с Оракла на Постгрес». А «убью на месте» и «ужас» — это не критика, а кидание какашками.

Евгений Степанищев (bolknote.ru) 2016

Ссылка в тему: http://bolknote.ru/all/4408

Pavrlpat 2016
  • Это неявно.
  • Нужную миграцию сложно найти в каталоге.
  • Пара миграций нужны не всегда.
  • Что если нужна миграция для разных версий одной СУБД?
  • Что если вам потребуется добавить поддержку mysql в проект?
  • Что если безопасники какого-либо вашего клиента зарежут такую возможность по своим причинам?
  • Что с миграциями со вюхами и хранимками? Как отличать, что для psql, что для oracle?
  • Как разработчикам писать это не ошибаясь?

Я бы сделал скриптик, который в специальной дериктории создаёт новые миграции с таймстаспом в названии и расширениями что-то вроде .postgre.sql и .oracle.sql. Почему не такое простое и явное решение?

Евгений Степанищев (bolknote.ru) 2016

Комментарий для Pavrlpat:

За 11 лет существования СЭД мы делаем это в первый раз. Чтобы переехать (а не сделать поддержку, ты неверно задачу понял) нужны очень и очень веские причины, потому что такой переезд занимает годы. Переезжать куда-либо ещё никаких причин нет, тем более, что на MySQL это сделать невозможно, не знаю, что ты помнишь про СЭД, но предлагать MySQL, ну это примерно как предложить сменить PHP там на bash.

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

Мы не делаем поддержку нескольких версий СУБД, у нас не сайтик для шаред-хостинга. Есть определённые требования — программные и аппаратные. Каждый продукт имеет одинаковые требования по всем инсталляциям.

Отличать ничего не нужно, есть определённая обвязка, информационная и диагностическая. Так же как и зачем-то искать какие-то там скрипты, тем более по совершенно неинформативному таймстампу, их тяжело сравнивать человеку (гораздо проще у нас сделано, диагностика говорит, что, например, скрипт №33 не применён).

Про безопасников агрумент не понял вообще. Как это вообще связано с безопасностью?

Твой простой скрипт требует изменения кучи кода обвязки вокруг миграций, пока нам не до этого, а скоро уже и не понадобится — когда переход состоится.

Ну и я про явность и неявность я даже объяснять не хочу. Правила Пайтона замечательны, но и сам Пайтон не может им следовать, потому что абстракции не бывают явными, а Пайтон — это очень высокоуровневая абстракция от железа. Нет ничего явного в этом мире. Есть привычное.

На всё ответил?

Евгений Степанищев (bolknote.ru) 2016

Комментарий для Pavrlpat:

А вот эти пропустил.

— Пара миграций нужны не всегда.

Ок. И что? Тогда просто пишем миграцию на универсальном эскуэле.

— Что с миграциями со вюхами и хранимками?

А что с ними?

Сергей 2016

Красиво. Но тащить подобное в продакшен.... Нафиг, нафиг.

Pavelpat 2016

Чтобы переехать (а не сделать поддержку, ты неверно задачу понял)
Каждый продукт имеет одинаковые требования по всем инсталляциям

А вы делаете одномоментный переход на другую СУБД? СЭД — это же несколько проектов (или речь идёт только об одном проекте?). Их тоже переводить одномоментно? Это я к тому, что в какой-то момент у Вас всё равно будет поддержка сразу двух СУБД.

Но предлагать MySQL, ну это примерно как предложить сменить PHP там на bash.

Сейчас такой необходимости нет, но что если по каким-либо причинам Postgres будет использовать нельзя?

Переезжать куда-либо ещё никаких причин нет, тем более, что на MySQL это сделать невозможно

Не согласен. Сложно, но можно. Конечно, просто адаптировать запросы — да, скорее всего не получится, в силу того что MySQL просто беднее по функциональности.

Так же как и зачем-то искать какие-то там скрипты

А что тут искать? Скрипт make_migration.sh который создаёт в папке ./sql/migrations/ два файла TIMESTAMP_shortname.postgre.sql и TIMESTAMP_shortname.oracle.sql с одинаковыми TIMESTAMP? Чем такое решение хуже, чем предложенное Вами?

тем более по совершенно неинформативному таймстампу

Даже просмотр через «ls -la» покажет всё отсортированно по дате. Кроме того в SQL-файле миграции удобно держать заголовок с описанием этой самой миграции.

Про безопасников агрумент не понял вообще. Как это вообще связано с безопасностью?

Что если сторонняя организация будет проводить аудит безопасности системы? Они разрешат использовать такие миграции?

Твой простой скрипт требует изменения кучи кода обвязки вокруг миграций, пока нам не до этого, а скоро уже и не понадобится — когда переход состоится.

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

Ну и я про явность и неявность я даже объяснять не хочу. Правила Пайтона

А причём тут python? Явное лучше чем неявное, не только в python. Там это просто акцентируется.

На всё ответил?

Нет.

  • Как разработчикам писать это не ошибаясь?
  • Почему не такое простое и явное решение?
PastorGL 2016

Комментарий для Евгения Степанищева:

Конструктивной критикой было бы «вот как я справился бы с переводом продукта на полмиллиона строк с Оракла на Постгрес». А «убью на месте» и «ужас» — это не критика, а кидание какашками.

К счастью, мне не доводилось переводить продукта с полумиллионом строк (имеется в виду на SQL?) с Oracle на Postgre. Мне доводилось поддерживать продукты, которые изначально должны быть database-agnostic в широких пределах. В частности, использовал пары Postgre и H2 (для серверной и толстого клиента), или Postgre и MySQL (для разных хостингов), или MSSQL и Postgre (для облачного и локального сервера), или даже MySQL и SQLite. Причём, во всех случаях с точки зрения кода было вообще неважно, какая из БД используется в данный момент.

Это решается применением слоя ORM. Hibernate, JPA, либо вообще что-то самописное, сводящее все запросы до уровня SQL:1999, с которым уже давно всё полностью совместимо.

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

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

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

http://fotki.yandex.ru/next/users/pastorgl/album/37525/view/476650

Евгений Степанищев (bolknote.ru) 2016

Комментарий для Pavelpat:

— Как разработчикам писать это не ошибаясь?

Так же как они пишут что угодно другое.

— Почему не такое простое и явное решение?

На этот вопрос я уже ответил выше, довольно подробно.

Евгений Степанищев (bolknote.ru) 2016

Комментарий для PastorGL:

Описано решение какой-то другой задачи.

Теперь про миграции. Их не нужно писать руками. В большинстве своём ORM уже поддерживают рудиментарные миграции на добавление полей, таблиц, и индексов

У нас высоконагруженный продукт, ORM с нашими запросами на справится. Мы широко используем практически все возможности СУБД, плюс нередко хинтуем запросы.

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

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

Евгений Степанищев (bolknote.ru) 2016

Комментарий для PastorGL:

http://img-fotki.yandex.ru/get/25541/16986249.3a/0_745ea_8440a214_L#bolk.png%7Chttps%3A%2F%2Ffotki.yandex.ru%2Fnext%2Fusers%2Fpastorgl%2Falbum%2F37525%2Fview%2F476650#404x258

Ну, это всё забавно и всё такое, только почему вы вообще решили, что вы меня можете учить как делать какие-то вещи?

Евгений Степанищев (bolknote.ru) 2016

А теперь, когда все комментирующие вдоволь наужасались, давайте я тоже ужаснусь.

Когда я занимался восточными единоборствами, я узнал интересную штуку: в самом начале вас учат бить только кулаком, представляя всё так, будто ударов прочими частями руки не существует — так бить прямо запрещают и такие удары считаются грубой ошибкой.

Позже когда достигнута нужна физическая и умственная форма, учат бить ладонью, пальцами и т. д. Удар не кулаком больше не является ошибкой, мастер совершенно свободен в выборе инструмента — он знает когда его применить и умеет это делать.

Так вот, господа. Вы, ребята, либо перепутали меня с учеником, которому разрешено бить только кулаком, либо не продвинусь дальше уровня «кулака». Считаете, что ваши заученные приёмы и есть то, чем надлежит пользоваться.

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

Ужас же в том, что вы, по всей видимости, не видите, что надо двигаться дальше.

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

Рамиль 2016

Должно быть классно, когда любой человек в команде, глядя на такой код, может сходу понять что значат все эти хитрые комбинации комментариев.

Efendy 2016

Что вы прицепились — для разового решения нужен результат, а не красота. Представьте, что вам нужно транспортировать хрупкий товар (произвести миграцию) — вы упаковываете его в кучу пенопласта, обвязываете скотчем и сверху сбиваете каркас из кривых досок — главное чтоб содержимое доехала невредимым. А вы тут рассказываете как запихнуть его в золотой сундук с кодовым замком с жпс навигатором.

zg (zg.livejournal.com) 2016

+1 к Efendy.

vladon 2016

Я бы сделал какой-нить прокси, которому на вход подаётся оракловый SQL, а он в зависимости от того кому отправляет, либо не трогает, либо конвертит в постгресовый.

Евгений Степанищев (bolknote.ru) 2016

Комментарий для vladon:

Для запросов, которые являются частью продукта, сделана очень похожая вещь. У каждого коннектора (их сейчас два — для Оракла и Постгреса) впереди стоит тупая срезалка, которая вырезает чужеродные секции. Секции для Оракла помечаются парой /*@o*/…//, для Постгреса — /*@p*/…//, в жизни это выглядит вот так:

http://pastebin.com/KBFxmgLt

Кроме того, есть несколько билдеров, которые в универсальном виде принимают параметры, а на выходе отдают адаптированный запрос. Билдеры сейчас есть для:
— иерархических запросов (так же известных как «рекурсивные»)
— для массовой вставки (bulk insert)
— для вызова функций/процедур
— для запросов MERGE
— для запросов UPSERT

А так же небольшой хелпер, который умеет делать универсальный OFFSET/LIMIT (в случае нашего 11-го Оракла — через ROWNUM).

PastorGL 2016

Комментарий для Евгения Степанищева:

Ну, это всё забавно и всё такое, только почему вы вообще решили, что вы меня можете учить как делать какие-то вещи?

Кто-то сам попросил конструктивной критики.

А теперь ещё и обижается.

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

Только будьте готовы, что если вы решите это безобразие показать кому-то со стороны, в лучшем случае покрутит у виска пальцем, либо, как я, внесёт в свою подборку «Как нельзя делать».

PastorGL 2016

Комментарий для Евгения Степанищева:

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

Меряться опытом — это вообще последнее дело. Вот у меня его, например, 17 лет. Из которых больше десяти на сеньорских позициях, включая проекты, которые годами пилили командами, раскиданными по трём часовым поясам. Только кто сказал, что мой опыт лучше вашего? Я лично так не считаю, потом что он, очевидно, совсем разный. Мой лишь говорит мне, что хаки любого рода — это плохо для проекта.

Евгений Степанищев (bolknote.ru) 2016

Комментарий для PastorGL:

Вы опять ничего не поняли. Я не говорю плохо о вашем опыте. Мне непонятно с чего вы решили, что у меня не хватает опыта, чтобы принимать такие решения взвешенно. В хорошо документированных хаках или хаках, которые решают разовые задачи быстро, нет ничего плохого.

Вот у меня его, например, 17 лет. Из которых больше десяти на сеньорских позициях, включая проекты, которые годами пилили командами, раскиданными по трём часовым поясам. Только кто сказал, что мой опыт лучше вашего?

Ну всё-таки вы решили померятся. Ну ок. Опыт у меня — 27 лет программирования, включая три ассемблера. 19 лет на позициях руководителей разработчиков. Занимался всем — играми, бизнес-приложениями, взломами программ, заказными программами для взломов, вебом, работал со всем миром, включая Европу, США, арабский мир. В основном известен тем, что способен решить любую задачу, если она в пределах возможного. Это мой основной скилл.

alxt 2016

Комментарий для Евгения Степанищева:

Я вот что тут не понял.
Это скрипты миграции? Т. е. они запускаются только при обновлении версии?
Если так, что чтобы не сделать просто два скрипта и выполнятся будет только нужный. Будет два каталога «миграция оракла», «миграция постгреса».

Небольшой оверхед на копирование (разработчик явно будет делать на чём-то одном и после проверки основной ветки будет переписывать на другой), зато не будет лишних извращений. Объём кода в данном случае не играет роли.

Никто никогда не правит миграционные скрипты (после того, как он попал в релиз), так что проблемы «один поправил, второй забыл» никогда не будет.

Евгений Степанищев (bolknote.ru) 2016

Комментарий для alxt:

Если так, что чтобы не сделать просто два скрипта и выполнятся будет только нужный. Будет два каталога «миграция оракла», «миграция постгреса».

Выше уже разобрана эта ситуация.

mjr27 2016

О. Можно сказать, коллегу встретил.

Занимался лет 15 назад, точно такой же задачей, только софт был на C/Motif. На момент этой задачи в проекте было 1.5кк LOC и 15 лет говнокода.

Многого уже не вспомню (разве что цирк с ROWID), но до сих пор с грустной улыбкой вспоминаю, как из костылей, грязи и палок делалась statement-based репликация для бедных. Триггеры, собирающие в файлы запросы; rsync файлов на другую машину и демон, обрабатывающий там эти файлы. Тем не менее, со своими задачами (2-3к инсертов в секунду, автовыбор master’a, не более 0.1% потерь) оно справлялось.

До Slony оставалось три года.

P.S: а миграции мы с год отдельно поддерживали для обеих веток.

Евгений Степанищев (bolknote.ru) 2016

Комментарий для mjr27:

Понятно :) Тяжко, нам полегче приходится — всё-таки Постгрес сейчас довольно много умеет :)