Вот, кстати, да. Что очень нравится в IBM i - это то, что в каждом задании есть JOBLOG. Который, по окончании задания падает в спул (вывод на печать, хотя физическим принтером никто не пользуется - "спульники" можно и так посмотреть). И в нем есть все. Все ошибки, все сообщения. Очень подробно. Что случилось и где.
В RPG есть дополнительные средства. Например, команда dump - в обработчике необработанных исключений делаем дамп и в спул падает полный дамп в данной точке - полная диагностика (в какой строке, какая ошибка, состояние всех переменных и т.п.)
Если исключение перехвачено и обрабатывается самостоятельно - есть PSDS - Program Status Data Structure. Там тоже немало информации что и где случилось.
Т.е. диагностика отказов тут намного лучше реализована чем в других средах.
Тут суть в том, что эта функция будет вызываться многократно (может быть тысячи, может десятки тысяч раз) в рамках одной выборки, и, значит, в рамках одного задания (job). Т.е. в рамках одной группы активации. Это типичный сценарий использования. Как пример - отобрать в таблице доверенных лиц тех, у кого типы доверенностей входят в заданный в настройках список.
При первом вызове с новой строкой будут проведена некоторая подготовка данных (как вариант - превращение строки в массив элементов и его сортировка) для быстрого поиска элемента (например, быстрым поиском). И все последующий вызовы уже не будут манипулировать самой строкой (кроме проверки что он не изменилась с предыдущего вызова), а будет сразу выполняться быстрый поиск элемента в подготовленом при первом вызове массиве.
К RPG это ровным счетом не относится. Все это можно и на С/С++ написать и оно будет работать точно также - группы активации, сохранения статических данных при многократном вызове в рамках одной группы активации - это особенности работы IBM i, а не конкретного языка.
Зная эти особенности и понимание типичных сценариев использования можно таким вот образом использовать их для повышения эффективности.
Думаю, что это будет эффективнее чем SYSTOOLS.SPLIT:
BEGIN
DECLARE FOUND_POSITION INTEGER DEFAULT 1 ;
DECLARE VSPLIT_LENGTH INTEGER DEFAULT 1 ;
DECLARE VWORK CLOB ( 1 M ) ;
DECLARE LIST_ITEM BIGINT DEFAULT 1 ;
DECLARE ITEM_START INTEGER DEFAULT 1 ;
DECLARE SCAN_START INTEGER DEFAULT 1 ;
SET VWORK = INPUT_LIST ;
IF VWORK IS NULL THEN RETURN ;
ELSEIF DELIMITER IS NULL OR LENGTH ( DELIMITER ) = 0 THEN
BEGIN
PIPE ( LIST_ITEM , VWORK ) ;
RETURN ;
END ;
END IF ;
SPLITLOOP : LOOP
SET FOUND_POSITION = LOCATE_IN_STRING ( VWORK , DELIMITER , SCAN_START , 1 ) ;
IF FOUND_POSITION = 0 THEN SET VSPLIT_LENGTH = LENGTH ( VWORK ) - ITEM_START + 1 ;
ELSE
IF ESCAPE IS NOT NULL AND FOUND_POSITION > 1 AND
SUBSTRING ( VWORK , FOUND_POSITION - 1 , 1 ) = ESCAPE THEN
SET VWORK = OVERLAY ( VWORK , '' , FOUND_POSITION - 1 , 1 ) ;
SET SCAN_START = FOUND_POSITION + LENGTH ( DELIMITER ) - 1 ;
ITERATE SPLITLOOP ;
ELSE SET VSPLIT_LENGTH = FOUND_POSITION - ITEM_START ;
END IF ;
END IF ;
PIPE ( LIST_ITEM , SUBSTRING ( VWORK , ITEM_START , VSPLIT_LENGTH ) ) ;
IF FOUND_POSITION = 0 THEN LEAVE SPLITLOOP ;
END IF ;
SET ITEM_START = FOUND_POSITION + LENGTH ( DELIMITER ) ;
SET SCAN_START = ITEM_START ;
SET LIST_ITEM = LIST_ITEM + 1 ;
END LOOP ;
RETURN ;
END
Манагер спрашивает: "а какое приложение жрет ресурсы больше", серверисты наши разводят руками и говорят: "Мы не знаем". Для меня это звучит как детский лепет.
Вот именно. У нас сопровождение постоянно мониторит загрузку. И всегда видит кто сколько жрет.
Запрос типа
Сервис ... за последние 5 недель увеличил потребление ресурсов в 3 раза. На данный момент является вторым по величине сервисом.
В качестве альтернативы рассматривается перенос сервиса на резервный сервер, что приведет к лагу до 10ти минут что недопустимо
Означает необходимость доработки сервиса для оптимизации потребления ресурсов.
Там основная проблема в скорости доступа. Елси та же БД сосотоит из нескольких десятков тысяч таблиц и индексов, то разносить ее по физически разным серверам становится сложно в плане резкой деградации скорости выполнения запросов (когда разные таблицы одного запроса на разных физических машинах оказываются).
Примерно аналогично и для IBM i. Лицензии там бессрочные. Обновления (TR - Technology refresh) тоже можно каким-то образом доставать. Даже новое железо можно в принципе. Т.е. тут как всегда - вроде как и ушли, но... есть нюансы, как говорится.
Ну у нас именно так. Есть центральные сервера (не МФ, middleware - IBM i) и есть "куча мелюзги" - "внешние системы". Там и винда и RHEL и SLES и много чего.
Центральные сервера - это АБС. Ядро всего банка. Все mission critical. Все остальное (что не так критично по времени недоступности, скорости отклика и т.п.) вынесено на внешние системы.
Обычно where in - это какая-то строка из настроек. Например,
'U1U2U3N2N3N7N8N9C2'
В данном случае, это набор двухсимвольных значений некоторого параметра. Т.е. в итоговом запросе оно должно выглядеть так:
where ... in ('U1', 'U2', 'U3', 'N2', 'N3', 'N7', 'N8', 'N9', 'C2')
В принципе, там есть w\a в виде
where ... in (select ELEMENT from table(SYSTOOLS.SPLIT(:str, ',')))
где
str = 'U1,U2,U3,N2,N3,N7,N8,N9,C2'
Беда в том, что там с производительностью не очень... SPLIT не очень быстрая функция...
Есть мысль написать свое, что-то типа
CONTAIN(elm, str, cnt, len)
которое будет на вход брать строку
'U1U2U3N2N3N7N8N9C2'
состоящую из
cnt = 9
элементов, каждый из которых имеет длину
len = 2
будет возвращать 'Y'/'N' в зависимости от того, содержится элемент elm в строке или нет.
Т.е.
CONTAIN('N3', 'U1U2U3N2N3N7N8N9C2', 9, 2)
вернет 'Y', а
CONTAIN('S1', 'U1U2U3N2N3N7N8N9C2', 9, 2)
Вернет 'N'
Естественно, пишется это на RPG, засовывается в SRVPGM (сервисная программа, аналог динамической библиотеки) со своей именованной группой активации.
На первом вызове строка раскладывается в массив (статическая переменная), при последующих просто сравниваем - изменились ли параметры с прошлого вызова - если нет, то используем уже готовый массив.
Ну а проверка наличия в массиве - это уже есть в RPG
if elm in array;
res = 'Y';
else;
res = 'N';
endif;
Ну а дальше погонять PEX и посмотреть получится ли быстрее на больших выборках.
В принципе, у меня есть уже готовая реализация "набора" в которой, в частности, есть такая вот функцийя
dcl-pr SET_FromString int(10) extproc(*CWIDEN : 'SET_FromString');
ptr pointer value;
pString pointer value options(*string);
end-pr;
Инициализация набора строкой без разделителя вида '214599S2K9536793'
Параметры:
ptr - указатель на набор
pString - строка для инициализации (переменная или строковая константа)
при инициализации строка будет разбита на элементы равной длины, той, что указана при
создании набора. Если размер элемента при создании был указан равным 2, то разбивка будет
проведена следующим образом: '21’, ‘45’, ‘99’, ‘S2’, ‘K9’, ‘53’, ‘67’, ‘93'
В набор будут занесены только «полные» элементы. Т.е. если к строке добавить еще один символ,
то при разбивке последним окажется неполный, односимвольный элемент который будет
проигнорирован.
В случае инициализации строковой переменной концевые пробелы предварительно удаляются.
И
dcl-pr SET_Exists ind extproc(*CWIDEN : 'SET_Exists');
ptr pointer value;
pItem pointer value options(*string);
end-pr;
Параметры:
ptr - указатель на набор
pItem - строковая переменная или константа, содержащая проверяемый элемент
Размер должен быть не меньше размера элемента набора [указанного при его создании]
Функция возвращает логическое значение *on/*off в зависимости от того найден указанный
элемент в наборе или нет.
Можно сделать на нем.
// Инициализируем набор строкой
n = SET_FromString(ptrSet : '234589S1K944675593': 3);
if (n > 0);
// Проверяем наличие элемента в наборе
if (SET_Exists(ptrSet : 'S1'));
// делаем что надо если элемент есть в наборе
else;
// а тут делаем что-то если его нет
endif;
endif;
Ну я знаю как это работает. Я с разными БД работал под ДОС, Вин и Линукс.
Там это для вас прозрачно, а когда начинаешь профайлить, то видишь сколько времени и ресурсов процессора уходит "под капотом".
У меня реально был случай, когда на доработку пришел модуль с динамическим скулем и заключением от сопровождения о том, что "33% времени и 36% ресурсов CPU тратится на подготовку SQL запроса". Треть времени и ресурсов программы!!! В наших реалиях это непозволительная роскошь.
Фактически это означает что бизнесу нужны серверные мощности на треть больше чем можно было бы. А это и деньги на оборудование и деньги на электропитание, охлаждении лишних железок. Вполне реальные такие деньги.
Специализированные решения (и по железу и по софту) тем и хороши что они оптимизированы для использования вычислительных мощностей под конкретный задачи.
Поэтому уход от того же COBOL в сторону языков общего назначения не выглядит разумным решением.
Другое дело, что там, наверняка, можно много чего вынести наружу. Того, что не завязано жестко на специфику. У нас таких "внешних систем", решающих business (но не mission) critical задачи несколько десятков. И там самые разнообразные решения используются - и RHEL и SLES и еще черт знает что... И та же MQ прекрасно работает - на нашей стороне MQ API для AS/400, на той стороне - JMS (Java Messaging System). Да и на самой ASке у нас есть Java интерфейсы (JT400) - на них система сборки, например, гредловая построена (в статье достаточно старая версия описана, сейчас там много чего накручено уже).
Так что если ядро системы написано на COBOL и работает, то совершенно не обязательно всю инфраструктуру строить на нем. Все это отлично интегрируется в современные решения. И это нормально (с точки зрения эффективности) - для каждой части большой системы выбирать наиболее подходящее для нее решение.
Я даже на АСке на двух языках пишу - RPG и C/C++. В зависимости от того, на чем мне удобнее реализовать данный конкретный кусок.
Как пример - https://habr.com/ru/articles/812605/ USRQ API написана на С (я могу и на RPG все это же написать, но неудобно). Скулевые UDF/UDTF - на RPG. Используется все это на 90% из RPG кода.
Я, собственно, к тому, что любой инструмент хорош в том, для чего он в первую очередь предназначен. А попытки все делать одним инструментом не очень хорошая практика. И если вас есть исправно работающий код на COBOL - не трогайте его. Пусть работает. Будто заняться больше нечем...
И чтобы его переписать вам потребуется человек, который свободно владеет COBOL, хорошо знает тонкости платформы на которой все это работает и еще в предметной области ориентируется. Но если у вас есть такой человек, то и переписывать ничего не надо...
Но я не про COBOL, а про RPG на IBM i. В целом (если не считать синтаксиса современного RPG) они с COBOL ровесники и функционально очень близки.
COBOL на i есть (номинально входит в группу ILE языков), но тут более 80% кода на RPG пишется.
Как мне кажется, на вскидку, в программе на основании исходных данных создается ветвление для разных структур запросов и каждый из них прописывается для статики.
По возможности - да. Но бывают [крайне редкие] исключения когда пришлось бы писать десяток вариантов статики (слишком большая вариативность по набору входных параметров). И тогда приходится морочиться с SQLDA. Хотя, справедливости ради, за 8 лет работы на АСке с таким столкнулся один раз всего... Что-то очень извратное было.
Со статикой единственный затык - конструкция where ... in (...) - вот там не получается подставить параметром строку в скобках. Набор параметров можно, а чтобы строкой - нет. Не сказать чтобы это было часто нужно, но иногда бывает потребность когда не знаешь заранее сколько там будет параметров.
Хороший пример этого можно наблюдать во время пандемии. В первые дни Covid-19 бизнесы массово закрывались. Уволенные сотрудники рванулись онлайн, чтобы подать заявление на получение пособий по безработице, и веб-сайты многих правительств штатов не выдержали нагрузки. Губернатор Нью-Джерси сообщил прессе, что их системы COBOL отчаянно нуждаются в помощи, чтобы справиться с новыми потребностями. «У нас в буквальном смысле есть системы, которым от сорока и более лет», — заявил он.
Но технологи, работавшие за кулисами над устранением неполадок, знали, проблема заключалась не в перемалывающем числа COBOL. Эти старые системы работали хорошо. Нет, всегда ломались более новые элементы — программы, управлявшие самим веб-сайтом.
«С ума сходило веб-приложение между мейнфреймом и внешним миром. Именно оно падало», — рассказывает программистка и писательница Марианна Беллотти, годами работавшая с государственными системами и следившая за этой системой Нью-Джерси. Но, по словам историка Хикса, властям было слишком неудобно признать «ой, да, это сломались наши веб-системы».
И что? На IBM i тоже есть компилятор COBOL в рамках ILE. А еще есть компиляторы С и С++. Но это не значит что IBM их все развивает.
Активно развивает она свой RPG (который пытались портировать на другие платформы, в т.ч. VisualAge RPF for #NET, но без большого успеха). Вот там да. Постоянно что-то новое появляется.
Да, это так. Потому что в случае с динамикой (как показывают наши PEX статистики) до 30% времени и ресурсов процессора могут уходить только на подготовку запроса. Это неэффективно. Там есть некоторые финты типа ручного формирования SQLDA (SQL Descriptor Area) и использования open cursor using descriptor, но это тот еще гимор. Пару раз использовал в ситуациях когда надо чтобы было быстро, но использовать статику никак не получалось ввиду значительной вариативности запроса в зависимости от набора входных параметров.
Да, вот только все это вы не вставите напрямую в код на шарпе или джаве... Вам придется все это запихивать в какой-то объект.
И получив из БД какой-нибудь decimal (или дату, или время) вы не сможете сразу с ними работать, не потратив время на создание какого-нибудь объекта на их основе...
Здесь же вы напрямую объявляете SQL запрос, который будет еще на этапе компиляции подготовлен (и компилятор может выдать ошибку если в синтаксисе запроса что-то не так). А получив данные вы сразу сможете работать с ними - decimal, numeric, date, time, timestamp - все эти типы поддерживаются языком (включая всю арифметику, в т.ч. и операции с округлением для форматов с фиксированной точкой).
А для несложных операций (типа прочитать десяток записей по значению ключа из одной таблицы или прочитать запись по ключу, изменить значение полей и записать обратно) можно вообще напрямую, без SQL с таблицами работать. И это будет быстрее, особенно когда это сильно нагруженный модуль с большой плотностью вызовов одновременно из многих заданий.
В этом и разница между специальным языком и языком общего назначения - в первом у вас в самом языке есть все для решения специфических задач предметной области наиболее эффективным образом, а во втором вам приходится привлекать дополнительные зависимости и тратить время на согласование форматов и типов данных языка и предметной области.
Если что - есть очень большой опыт писания на С/С++ В т.ч. и приложений для работы с БД (на других платформах). И С/С++ тут тоже есть. Но для работы с БД и банковской логики однозначный выбор в пользу RPG потому что на нем это делается в разы проще и быстрее. И вот что-то системное - тут да. Уже С/С++ удобнее.
FSTFRR - модуль внешнего ввода. Он определит в каком режиме работает (их несколько - внешний ввод переданных данных, накат из журнала в указанной библиотеке, восстановление по журналу...) потом вызовет FSTFVR - собственно валидатор, а потом, если не будет ошибки в валидаторе, уже FSTFUR - собственно изменение данных в таблице.
И ИНН будет проверяться по многим параметрам - соответствие формата типу клиента (у ЮЛ и ФЛ разные форматы), совпадение значения со значением в карточке клиента, валидность ИНН (там есть алгоритм проверки с контрольными цифрами и т.п.)...
Таки да. Но они ровесники - RPG тоже 1959-го "года рождения". Правда, изначально это был "эмулятор табуляторов" (чтобы не выбрасывать все нажитое тяжким трудом) для IBM 1401.
Потом уже стал более-менее языком, но с позиционным синтаксисом ("FIXED"), специфическим cycle mode выполнением программы, primary file (основная таблица) и т.п. Т.е. там хватало заморочек.
Нормальным процедурным языком оно уже стало в конце 90-х - начале 00-х.
Но как и COBOL, это все равно специализированный язык для работы с БД и финансовых вычислений. Пытаться использовать его как ЯВУ общего назначения неудобно (даже несмотря на то, что концепция ILE позволяет использовать любую функцию из C RTL в RPG программах фактически напрямую).
Зато с БД работать легко и просто. И со всем что в БД лежит - даты, время, строки, форматы с фиксированной точкой. Все в языке для этого есть. И при этом очень простой синтаксис (где-то на уровне классического паскаля).
COBOL в этом плане достаточно похож. Ну только синтаксис непривычный. В этом плане по самому языку порог входа несравнимо ниже прочих. Там больше сложностей будет с особенностями самой платформы на которой все это работает.
Там проблема в том, что многие процессы используют большое количество разнородных данных. И если начать все это разносить по серверам, начнутся лаги при сборе нужной информации.
А есть достаточное количество мест, где такие лаги просто недопустимы. Условно говоря - длительность некоего процесса в пять минут недопустима т.к. он начинает тормозить следующие за ним процессы и так по цепочке. И все вместо это начинает вылазить за рамки отведенного временного окна (работа банка в течении суток имеет несколько фаз и каждая из них привязана по времени).
Тот же контроль платежей (ориентировочно 100млн платежей в сутки, может и больше) - там все очень критично по времени и при этом проводится много проверок с использованием большого количество разнородных данных. Если там начнутся лаги связанные с запросами данных с других серверов - будет большая просадка, а это недопустимо.
Тут и так все что можно вынести во внешние системы - вынесено и обрабатывается там. На АБС остается только то, что критично по времени и быстродействию.
И в целом, система справляется. Даже в пиковых нагрузках. И да, приходится оптимизировать многие старые модули - за этим следит сопровождение. Много из того, что написано 5 и более лет назад и на тот момент устраивало по скорости, сейчас не устраивает и переписывается с учетом большого накопленного опыта (в плане оптимальности кода).
И тут, опять, помогает то, что с БД можно работать не только скулем (который не является безусловно оптимальным в плене производительнности). Используется комбинированный подход. Скажем, получить десяток записей из одной таблицы по заданному значению ключа прямым доступом всегда быстрее, чем скулем. Еще быстрее - проврека наличия записи по значению ключа (без ее чтения, просто есть/нет - оно не требует обращаения к таблице).
Также помогает кеширование между вызовами программы (опять особенность АСки - пока жива группа активации, все глобальные и статические данные сохраняются между вызовами одной программы, а таких ситуаций у нас достаточно). Это позволяет избежать лишних операций. Те же настройки - при первом вызове прочитали, организовали в памяти под быстрое и удобное использование, а при следующих 100500 вызовах уже просто пользуемся.
Тут сама платформа много тонкостей таких имеет, позволяющих оптимизировать программы при правильном использовании.
В этом и проблема миграции - там не просто с одного языка на другой перевести, там очень много логики менять надо. И придумывать как обходить те моменты, которых нет на других платформах.
Ну так-то у нас еще около 60-ти "внешних систем" вокруг АБСки.
Кто идет по пути децентрализации обычно сталкиваются с другими проблемами - огромное количество тесно связанных между собой данных. А дальше - или реплицировать их все по всем узлам и иметь лаги репликации, или распределять, но тогда лаги начинаются на уровне связывания.
Поэтому да. То, что не критично по времени - выносится на внешние системы с репликацией туда нужных данных. А что критично - то работает на АБС.
Ну и смотрите. 50млн клиентов. У каждого клиента 5-6 адресов (разных типов). Несколько ДУЛ (документ удостоверяющий личность). Счетов - 5-6 это мизер. Есть клиенты с несколькими десятками и даже сотнями счетов.
Дальше идут "субъекты клиентов" - всякие доверенные лица и т.п. Там же - доверенности. и уполномоченные лица.
Дальше - держатели карт (скажем, корпоративных или семейных)...
Итого на одного клиента может быть полторы-две сотни всяких связанных с ним записей только по клиентским данным.
Плюс всякие риски клиентов (страновые, репутационные, санкционные и т.п.)
Плюс всякие данные по актуализации.
Дальше - платежи. Проводки, полупроводки, платежные документы... Одних платежей за сутки проходит около сотни миллионов.
И бог знает что еще. Так что смело можно 50млн клиентов умножать на 100 а то и более. Так что там объемы приличные. И очень разнородные.
Ну и железяка там таки не одна. Их несколько, включая горячий резерв.
Тут еще такое дело - если у вас условно три железяки с равномерно распределенной нагрузкой, и в период пиковой нагрузки из них встанет одна, то две остальные не вывезут. И все встанет колом. Так что тут три слабых вместо одной мощной не спасет. Спасает горячий резерв с переключением в пару минут (время недоступности системы).
Про масштабируемость писал уже - PowerAXON на машинах Power10. Вот там реально можно наращивать мощность масштабированием кластера из мелких машин. И при этом все это работает как она большая с точки зрения конечного пользователя.
Вот, кстати, да. Что очень нравится в IBM i - это то, что в каждом задании есть JOBLOG. Который, по окончании задания падает в спул (вывод на печать, хотя физическим принтером никто не пользуется - "спульники" можно и так посмотреть). И в нем есть все. Все ошибки, все сообщения. Очень подробно. Что случилось и где.
В RPG есть дополнительные средства. Например, команда dump - в обработчике необработанных исключений делаем дамп и в спул падает полный дамп в данной точке - полная диагностика (в какой строке, какая ошибка, состояние всех переменных и т.п.)
Если исключение перехвачено и обрабатывается самостоятельно - есть PSDS - Program Status Data Structure. Там тоже немало информации что и где случилось.
Т.е. диагностика отказов тут намного лучше реализована чем в других средах.
В РЖД и IBM i были...
Тут суть в том, что эта функция будет вызываться многократно (может быть тысячи, может десятки тысяч раз) в рамках одной выборки, и, значит, в рамках одного задания (job). Т.е. в рамках одной группы активации. Это типичный сценарий использования. Как пример - отобрать в таблице доверенных лиц тех, у кого типы доверенностей входят в заданный в настройках список.
При первом вызове с новой строкой будут проведена некоторая подготовка данных (как вариант - превращение строки в массив элементов и его сортировка) для быстрого поиска элемента (например, быстрым поиском). И все последующий вызовы уже не будут манипулировать самой строкой (кроме проверки что он не изменилась с предыдущего вызова), а будет сразу выполняться быстрый поиск элемента в подготовленом при первом вызове массиве.
К RPG это ровным счетом не относится. Все это можно и на С/С++ написать и оно будет работать точно также - группы активации, сохранения статических данных при многократном вызове в рамках одной группы активации - это особенности работы IBM i, а не конкретного языка.
Зная эти особенности и понимание типичных сценариев использования можно таким вот образом использовать их для повышения эффективности.
Думаю, что это будет эффективнее чем SYSTOOLS.SPLIT:
Вот именно. У нас сопровождение постоянно мониторит загрузку. И всегда видит кто сколько жрет.
Запрос типа
Означает необходимость доработки сервиса для оптимизации потребления ресурсов.
Там основная проблема в скорости доступа. Елси та же БД сосотоит из нескольких десятков тысяч таблиц и индексов, то разносить ее по физически разным серверам становится сложно в плане резкой деградации скорости выполнения запросов (когда разные таблицы одного запроса на разных физических машинах оказываются).
В небольших конторах и задач под МФ нет.
Примерно аналогично и для IBM i. Лицензии там бессрочные. Обновления (TR - Technology refresh) тоже можно каким-то образом доставать. Даже новое железо можно в принципе. Т.е. тут как всегда - вроде как и ушли, но... есть нюансы, как говорится.
Ну у нас именно так. Есть центральные сервера (не МФ, middleware - IBM i) и есть "куча мелюзги" - "внешние системы". Там и винда и RHEL и SLES и много чего.
Центральные сервера - это АБС. Ядро всего банка. Все mission critical. Все остальное (что не так критично по времени недоступности, скорости отклика и т.п.) вынесено на внешние системы.
Обычно where in - это какая-то строка из настроек. Например,
В данном случае, это набор двухсимвольных значений некоторого параметра. Т.е. в итоговом запросе оно должно выглядеть так:
В принципе, там есть w\a в виде
где
Беда в том, что там с производительностью не очень... SPLIT не очень быстрая функция...
Есть мысль написать свое, что-то типа
которое будет на вход брать строку
состоящую из
элементов, каждый из которых имеет длину
будет возвращать 'Y'/'N' в зависимости от того, содержится элемент elm в строке или нет.
Т.е.
вернет 'Y', а
Вернет 'N'
Естественно, пишется это на RPG, засовывается в SRVPGM (сервисная программа, аналог динамической библиотеки) со своей именованной группой активации.
На первом вызове строка раскладывается в массив (статическая переменная), при последующих просто сравниваем - изменились ли параметры с прошлого вызова - если нет, то используем уже готовый массив.
Ну а проверка наличия в массиве - это уже есть в RPG
Ну а дальше погонять PEX и посмотреть получится ли быстрее на больших выборках.
В принципе, у меня есть уже готовая реализация "набора" в которой, в частности, есть такая вот функцийя
И
Можно сделать на нем.
Ну я знаю как это работает. Я с разными БД работал под ДОС, Вин и Линукс.
Там это для вас прозрачно, а когда начинаешь профайлить, то видишь сколько времени и ресурсов процессора уходит "под капотом".
У меня реально был случай, когда на доработку пришел модуль с динамическим скулем и заключением от сопровождения о том, что "33% времени и 36% ресурсов CPU тратится на подготовку SQL запроса". Треть времени и ресурсов программы!!! В наших реалиях это непозволительная роскошь.
Фактически это означает что бизнесу нужны серверные мощности на треть больше чем можно было бы. А это и деньги на оборудование и деньги на электропитание, охлаждении лишних железок. Вполне реальные такие деньги.
Специализированные решения (и по железу и по софту) тем и хороши что они оптимизированы для использования вычислительных мощностей под конкретный задачи.
Поэтому уход от того же COBOL в сторону языков общего назначения не выглядит разумным решением.
Другое дело, что там, наверняка, можно много чего вынести наружу. Того, что не завязано жестко на специфику. У нас таких "внешних систем", решающих business (но не mission) critical задачи несколько десятков. И там самые разнообразные решения используются - и RHEL и SLES и еще черт знает что... И та же MQ прекрасно работает - на нашей стороне MQ API для AS/400, на той стороне - JMS (Java Messaging System). Да и на самой ASке у нас есть Java интерфейсы (JT400) - на них система сборки, например, гредловая построена (в статье достаточно старая версия описана, сейчас там много чего накручено уже).
Так что если ядро системы написано на COBOL и работает, то совершенно не обязательно всю инфраструктуру строить на нем. Все это отлично интегрируется в современные решения. И это нормально (с точки зрения эффективности) - для каждой части большой системы выбирать наиболее подходящее для нее решение.
Я даже на АСке на двух языках пишу - RPG и C/C++. В зависимости от того, на чем мне удобнее реализовать данный конкретный кусок.
Как пример - https://habr.com/ru/articles/812605/ USRQ API написана на С (я могу и на RPG все это же написать, но неудобно). Скулевые UDF/UDTF - на RPG. Используется все это на 90% из RPG кода.
Я, собственно, к тому, что любой инструмент хорош в том, для чего он в первую очередь предназначен. А попытки все делать одним инструментом не очень хорошая практика. И если вас есть исправно работающий код на COBOL - не трогайте его. Пусть работает. Будто заняться больше нечем...
И чтобы его переписать вам потребуется человек, который свободно владеет COBOL, хорошо знает тонкости платформы на которой все это работает и еще в предметной области ориентируется. Но если у вас есть такой человек, то и переписывать ничего не надо...
Да, SQLDA это про динамику.
Но я не про COBOL, а про RPG на IBM i. В целом (если не считать синтаксиса современного RPG) они с COBOL ровесники и функционально очень близки.
COBOL на i есть (номинально входит в группу ILE языков), но тут более 80% кода на RPG пишется.
По возможности - да. Но бывают [крайне редкие] исключения когда пришлось бы писать десяток вариантов статики (слишком большая вариативность по набору входных параметров). И тогда приходится морочиться с SQLDA. Хотя, справедливости ради, за 8 лет работы на АСке с таким столкнулся один раз всего... Что-то очень извратное было.
Со статикой единственный затык - конструкция where ... in (...) - вот там не получается подставить параметром строку в скобках. Набор параметров можно, а чтобы строкой - нет. Не сказать чтобы это было часто нужно, но иногда бывает потребность когда не знаешь заранее сколько там будет параметров.
COBOL — древний код, который управляет вашими деньгами
И что? На IBM i тоже есть компилятор COBOL в рамках ILE. А еще есть компиляторы С и С++. Но это не значит что IBM их все развивает.
Активно развивает она свой RPG (который пытались портировать на другие платформы, в т.ч. VisualAge RPF for #NET, но без большого успеха). Вот там да. Постоянно что-то новое появляется.
Вот только доступ к этому файлику может быть у одного-двух человек...
Да, это так. Потому что в случае с динамикой (как показывают наши PEX статистики) до 30% времени и ресурсов процессора могут уходить только на подготовку запроса. Это неэффективно. Там есть некоторые финты типа ручного формирования SQLDA (SQL Descriptor Area) и использования open cursor using descriptor, но это тот еще гимор. Пару раз использовал в ситуациях когда надо чтобы было быстро, но использовать статику никак не получалось ввиду значительной вариативности запроса в зависимости от набора входных параметров.
Да, вот только все это вы не вставите напрямую в код на шарпе или джаве... Вам придется все это запихивать в какой-то объект.
И получив из БД какой-нибудь decimal (или дату, или время) вы не сможете сразу с ними работать, не потратив время на создание какого-нибудь объекта на их основе...
Здесь же вы напрямую объявляете SQL запрос, который будет еще на этапе компиляции подготовлен (и компилятор может выдать ошибку если в синтаксисе запроса что-то не так). А получив данные вы сразу сможете работать с ними - decimal, numeric, date, time, timestamp - все эти типы поддерживаются языком (включая всю арифметику, в т.ч. и операции с округлением для форматов с фиксированной точкой).
А для несложных операций (типа прочитать десяток записей по значению ключа из одной таблицы или прочитать запись по ключу, изменить значение полей и записать обратно) можно вообще напрямую, без SQL с таблицами работать. И это будет быстрее, особенно когда это сильно нагруженный модуль с большой плотностью вызовов одновременно из многих заданий.
В этом и разница между специальным языком и языком общего назначения - в первом у вас в самом языке есть все для решения специфических задач предметной области наиболее эффективным образом, а во втором вам приходится привлекать дополнительные зависимости и тратить время на согласование форматов и типов данных языка и предметной области.
Если что - есть очень большой опыт писания на С/С++ В т.ч. и приложений для работы с БД (на других платформах). И С/С++ тут тоже есть. Но для работы с БД и банковской логики однозначный выбор в пользу RPG потому что на нем это делается в разы проще и быстрее. И вот что-то системное - тут да. Уже С/С++ удобнее.
Ну я же не буду вам весь код раскрывать :-)
FSTFRR - модуль внешнего ввода. Он определит в каком режиме работает (их несколько - внешний ввод переданных данных, накат из журнала в указанной библиотеке, восстановление по журналу...) потом вызовет FSTFVR - собственно валидатор, а потом, если не будет ошибки в валидаторе, уже FSTFUR - собственно изменение данных в таблице.
И ИНН будет проверяться по многим параметрам - соответствие формата типу клиента (у ЮЛ и ФЛ разные форматы), совпадение значения со значением в карточке клиента, валидность ИНН (там есть алгоритм проверки с контрольными цифрами и т.п.)...
Таки да. Но они ровесники - RPG тоже 1959-го "года рождения". Правда, изначально это был "эмулятор табуляторов" (чтобы не выбрасывать все нажитое тяжким трудом) для IBM 1401.
Потом уже стал более-менее языком, но с позиционным синтаксисом ("FIXED"), специфическим cycle mode выполнением программы, primary file (основная таблица) и т.п. Т.е. там хватало заморочек.
Нормальным процедурным языком оно уже стало в конце 90-х - начале 00-х.
Но как и COBOL, это все равно специализированный язык для работы с БД и финансовых вычислений. Пытаться использовать его как ЯВУ общего назначения неудобно (даже несмотря на то, что концепция ILE позволяет использовать любую функцию из C RTL в RPG программах фактически напрямую).
Зато с БД работать легко и просто. И со всем что в БД лежит - даты, время, строки, форматы с фиксированной точкой. Все в языке для этого есть. И при этом очень простой синтаксис (где-то на уровне классического паскаля).
COBOL в этом плане достаточно похож. Ну только синтаксис непривычный. В этом плане по самому языку порог входа несравнимо ниже прочих. Там больше сложностей будет с особенностями самой платформы на которой все это работает.
Там проблема в том, что многие процессы используют большое количество разнородных данных. И если начать все это разносить по серверам, начнутся лаги при сборе нужной информации.
А есть достаточное количество мест, где такие лаги просто недопустимы. Условно говоря - длительность некоего процесса в пять минут недопустима т.к. он начинает тормозить следующие за ним процессы и так по цепочке. И все вместо это начинает вылазить за рамки отведенного временного окна (работа банка в течении суток имеет несколько фаз и каждая из них привязана по времени).
Тот же контроль платежей (ориентировочно 100млн платежей в сутки, может и больше) - там все очень критично по времени и при этом проводится много проверок с использованием большого количество разнородных данных. Если там начнутся лаги связанные с запросами данных с других серверов - будет большая просадка, а это недопустимо.
Тут и так все что можно вынести во внешние системы - вынесено и обрабатывается там. На АБС остается только то, что критично по времени и быстродействию.
И в целом, система справляется. Даже в пиковых нагрузках. И да, приходится оптимизировать многие старые модули - за этим следит сопровождение. Много из того, что написано 5 и более лет назад и на тот момент устраивало по скорости, сейчас не устраивает и переписывается с учетом большого накопленного опыта (в плане оптимальности кода).
И тут, опять, помогает то, что с БД можно работать не только скулем (который не является безусловно оптимальным в плене производительнности). Используется комбинированный подход. Скажем, получить десяток записей из одной таблицы по заданному значению ключа прямым доступом всегда быстрее, чем скулем. Еще быстрее - проврека наличия записи по значению ключа (без ее чтения, просто есть/нет - оно не требует обращаения к таблице).
Также помогает кеширование между вызовами программы (опять особенность АСки - пока жива группа активации, все глобальные и статические данные сохраняются между вызовами одной программы, а таких ситуаций у нас достаточно). Это позволяет избежать лишних операций. Те же настройки - при первом вызове прочитали, организовали в памяти под быстрое и удобное использование, а при следующих 100500 вызовах уже просто пользуемся.
Тут сама платформа много тонкостей таких имеет, позволяющих оптимизировать программы при правильном использовании.
В этом и проблема миграции - там не просто с одного языка на другой перевести, там очень много логики менять надо. И придумывать как обходить те моменты, которых нет на других платформах.
Ну так-то у нас еще около 60-ти "внешних систем" вокруг АБСки.
Кто идет по пути децентрализации обычно сталкиваются с другими проблемами - огромное количество тесно связанных между собой данных. А дальше - или реплицировать их все по всем узлам и иметь лаги репликации, или распределять, но тогда лаги начинаются на уровне связывания.
Поэтому да. То, что не критично по времени - выносится на внешние системы с репликацией туда нужных данных. А что критично - то работает на АБС.
Ну и смотрите. 50млн клиентов. У каждого клиента 5-6 адресов (разных типов). Несколько ДУЛ (документ удостоверяющий личность). Счетов - 5-6 это мизер. Есть клиенты с несколькими десятками и даже сотнями счетов.
Дальше идут "субъекты клиентов" - всякие доверенные лица и т.п. Там же - доверенности. и уполномоченные лица.
Дальше - держатели карт (скажем, корпоративных или семейных)...
Итого на одного клиента может быть полторы-две сотни всяких связанных с ним записей только по клиентским данным.
Плюс всякие риски клиентов (страновые, репутационные, санкционные и т.п.)
Плюс всякие данные по актуализации.
Дальше - платежи. Проводки, полупроводки, платежные документы... Одних платежей за сутки проходит около сотни миллионов.
И бог знает что еще. Так что смело можно 50млн клиентов умножать на 100 а то и более. Так что там объемы приличные. И очень разнородные.
Ну и железяка там таки не одна. Их несколько, включая горячий резерв.
Тут еще такое дело - если у вас условно три железяки с равномерно распределенной нагрузкой, и в период пиковой нагрузки из них встанет одна, то две остальные не вывезут. И все встанет колом. Так что тут три слабых вместо одной мощной не спасет. Спасает горячий резерв с переключением в пару минут (время недоступности системы).
Про масштабируемость писал уже - PowerAXON на машинах Power10. Вот там реально можно наращивать мощность масштабированием кластера из мелких машин. И при этом все это работает как она большая с точки зрения конечного пользователя.
https://infocity.tech/2022/11/obschij-paket-innovatsij-delaet-tehnologiju-power10-osobenno-interesnoj/