Pull to refresh

Comments 15

По самой i подскажите. Там DB2 всё ещё есть? А Java? Оно у вас не используется? На какой технологии у вас работают клиентские места? Telnet? Если веб, то что запросы обрабатывает? В каком виде получаются запросы (байтовый буфер?)?

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

Там DB2 всё ещё есть?

Естественно - это же часть системы самой. Там много что на ней завязано.

А Java?

Есть но не используем практически - для наших задач слишком медленно и слишком много ресурсов жрет. Очень ограничено - плагин для CI/CD на гредле + вебсервисы для общения с внешними системами (но это фактически прослойка для преобразований интерфейсов и типов данных между внешним REST API и внутренними сервисами АБС).

На какой технологии у вас работают клиентские места?

Стандартно - эмулятор терминала IBM 5250. Но это чисто служебное применение. Все общение с внешними системами или через веб-сервисы или через очереди (IBM MQ, Kafka).

Фактически на сервере крутится ядро АБС. Очень много вынесено на внешние системы, а там другие платформы, другие технологии.

Как вообще там с отсталостью программных технологий?

Что понимать под "отсталостью"?

Это не мертвая система. Она постоянно развивается. Как по железу (процессоры PowerS появляются новые - не так давно Power10 вот появился - достаточно мощная штука), так и по софту - каждые 2-3 года новая версия ОС, пару раз в год - минорные обновления (TR - Technology Refresh). А там, поскольку все интегрировано в систему, не только системное, но и БД, SQL движку языкам и т.п. что-то новое появляется.

Ну и это только у нас она малоизвестна. В мире все немного иначе - Top Companies Using IBM i

Так что насчет "отсталости" можно поспорить :-)

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

На С/С++ пишется то, что удобнее писать на С/С++. Это не основные языки тут. Только для всякого низкоуровневого.

Для бизнес-логики и работы с БД (90% наших основных задач) есть RPG (про него отдельную статью сделаю). А там никаких фреймворков не надо - все необходимое уже есть в самом языке - он специально для работы с БД и решения бизнес-задач предназначен. Там и поддержка всех типов данных, что есть в БД (дата-время, форматы с фиксированной точкой...) и вся арифметика с ними и средства работы со строками и доступ к БД хоть напрямую (позиционирование, чтение, запись...), хоть вставкой SQL выражений непосредственно в RPG код. Быстрый, эффективный и удобный для своих задач язык.

Ну и плюс ILE, позволяющая легко стыковать код на RPG с кодом на С/С++ (там, где возникает необходимость)

Спасибо, понятно.

А вы имели опыт разработки (хотя бы более года) на чём-то другом что бы корректно делать выводы про "все необходимое уже есть в самом языке"?

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

Вы пишете, что отказались от Java, то есть отказались от кучи продуктов, выполняющих за вас кучу задач. И даже какой-нибудь Message Broker не используете, хотя есть MQ. Поэтому интересно - насколько далеко вы находитесь от рекомендаций IBM по выбору используемых технологий? Сами всё выбирали?

А вы имели опыт разработки (хотя бы более года) на чём-то другом что бы корректно делать выводы про "все необходимое уже есть в самом языке"?

Да, немножко было. С 1991 по 2017гг писал (коммерческая разработка, до 91-го не учитываем - это так, для себя, там был и фортран и паскаль и бейсик, немножко prolog'а, но ничего серьезного) на С (сначала), потом С++ (когда он появился). Плюс немного с БД - dsVista (ныне это RDM - Raima Data Manager), Paradox Engine, BDE, InterBase, FireBird. Были эпизоды работы с Clarion, Clipper.

Как считаете - достаточно?

"Все есть в языке" - вот вам простой пример. Как вы на (любой язык по вкусу) быстро проверите наличие записи в БД для заданного значений ключа. Просто наличие, содержимое записи не интересно.

Или есть "историческая" таблица - некий ID + дата. На один ID несколько записей с разными датами. Есть индекс ID + DTA. Нужно найти записи с минимальным и максимальным значением DTA для заданного ID.

Как это решать? Например, на чистом С++, без дополнительных библиотек?

Или. Есть таблица. Формат записи у нее содержит 30 полей. Сколько строк кода вам придется написать чтобы у вас была структура (в которую вы будете читать запись из этой таблицы), соответствующая структуре записи?

Про работу с полями БД типа date, time, numeric, decimal - какой язык позволит работать с ними, не создавая в рантайме дополнительных объектов?

Мне интересно сравнить концепт от IBM с остальным миром

Зачем? Это очень дорогая, но при этом очень надежная и производительная система. Но она предназначена для работы с БД и бизнес-логикой. Т.е. ЦА - прежде всего банки, страховые и т.п. Именно в таких приложениях раскрываются все ее плюсы.

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

90% кода пишется на RPG - вся банковская бизнес-логика. Описанный в статье кейс - это просто разработка вспомогательного инструмента для решения насущных задач, основанного на том, что предлагает система. Можно не упираться и использовать *DTAQ для которого уже есть и системные API (функции послать, получить и т.п.) и SQL (тут вообще почти все можно скулем делать вплоть до чтения джоблогов). Просто там ниже производительность и выше потребление ресурсов. Что для нас критично.

Вы пишете, что отказались от Java, то есть отказались от кучи продуктов, выполняющих за вас кучу задач.

Например?

От Java отказались потому что

  • потребляет слишком много ресурсов

  • не обеспечивает достаточной производительности

  • не поддерживает напрямую работу с БД (да, можно подтянуть зависимости, но опять с потерей производительности - максимум, что получите - динамический SQL который является наихудшим из возможных вариантов)

  • не поддерживает те типы данных, с которыми в основном работаем - дата, время, форматы с фиксированной точкой - опять только через "зависимости" и создание рантайм объектов просто для того, чтобы сложить с округлением пару чисел с фиксированной точкой

И даже какой-нибудь Message Broker не используете, хотя есть MQ

Откуда такое утверждение?

Поэтому интересно - насколько далеко вы находитесь от рекомендаций IBM по выбору используемых технологий?

Тоже не понял... Какие рекомендации? О каких "технологиях" идет речь?

Как считаете - достаточно?

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

Как вы на (любой язык по вкусу) быстро проверите наличие записи в БД для заданного значений ключа

Псевдокод:

If (select.one.from(table).where("id=",xxx)==null) ...

Но вы, разумеется, ожидаете очень быстрое решение (по скорости выполнения). Такова привычка, выработанная десятилетиями низкоуровневого программирования. В данном же случае представлен вариант работы библиотеки, которая в общем случае классифицируется как ORM (object to relational mapping). Смысл существования такой библиотеки - сокращение затрат на написание кода, а не на сокращение потребления ресурсов (включая время). Если код краток и понятен, то его не только можно написать быстро, но в нём так же будет минимум ошибок. Суммарно это даёт повышение скорости разработки, хотя и с ущербом для использования ресурсов.

Но должен заметить, что ресурсы перерасходуются вполне умеренно. В примере выше имеем перерасход памяти сотню-две байт непосредственно в момент исполнения, но после исполнения эти байты освобождаются. По скорости, в сравнении со временем обработки запроса БД (а это обращение к диску), перерасход составляет тысячные доли общих затрат, а то и даже тысячные процента.

Нужно найти записи с минимальным и максимальным значением DTA для заданного ID

Object dates[] = select.one.fields("min(dta),max(dta)").from(table).where("id=",xxx).groupBy("id");

Например, на чистом С++, без дополнительных библиотек?

Неправильная постановка задачи. Или вы собрались сами реализовать протокол сетевого взаимодействия вашей СУБД?

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

Хотя ibm i, возможно, предоставляет API для си-разработчиков, выполняющий именно то, что вы просите. Но вы забыли один момент - API кто-то за вас разработал. И включил его в состав вашего ПО. Но включить что-то состав вы можете самостоятельно, без оглядки на кого-то, кто это сделает за вас. Собственно, так поступает сегодня весь мир.

Теперь выгоды встроенного ПО - оно быстрее. Но всё остальное - минусы. У вас нет выбора. Функционал беден в сравнении с массой доступных для других платформ библиотек. Вы полностью зависите от поставщика техники, включая программную составляющую. У вас из-за санкций ещё не отключили что-нибудь важное в сердце вашей системы - в IBM i?

Сколько строк кода вам придется написать чтобы у вас была структура (в
которую вы будете читать запись из этой таблицы), соответствующая
структуре записи?

Спектр решений здесь примерно такой:

Одна строка - данные подключения к БД - и вы получаете сгенерированные классы со всем необходимым. Вторая строка - select.one.from(table).where("id=",xxx);

Нижняя часть спектра - вы сами пишете низкоуровневые команды и отправляете их через драйвер БД.

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

с полями БД типа date, time, numeric, decimal - какой язык позволит
работать с ними, не создавая в рантайме дополнительных объектов?

Любой язык, в котором есть так называемые "примитивные" типы данных. Правда придётся объявлять даты в БД как long, например. Но это если уж мы настолько зажаты в тиски, что даже на объект, оборачивающий long, у нас нет памяти. Мне жаль программистов, которым для решения бизнес-задач выдают что-то вроде IBM PC AT 286.

Зачем?

IBM, на самом-то деле, создала много интересных решений. И в случае с AS 400 я вижу хорошо масштабируемый подход, объединяющий вертикальное и горизонтальное масштабирование. Но не было возможности почувствовать его мощь.

Например?

ORM, Web, ESB. Это минимум, но есть много частных случаев с различными удобными библиотеками.

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

Откуда такое утверждение?

Из вашего списка.

Какие рекомендации? О каких "технологиях" идет речь?

Я же говорю - IBM много чего полезного придумали. Но в вашем случае низкоуровневый подход, похоже, закрывает от вам общую картину.

If (select.one.from(table).where("id=",xxx)==null) ...

Отлично. А теперь вопрос - что такое select.one.from(table).where("id=",xxx)? Сколько строк кода занимает вот это все вот?

Вот вам не псевдокод. На RPG.

setll keyvalue myidx;
if %equal(myidx);
  // запись со значеним ключа keyvalue существует
else;
  // записи с таким значением ключа нет
endif;

Это скомпилируется и будет работать. SetLL - нативная операция RPG. Позиционирование курсора перед записью со значением ключа равным или большим заданному. %Equal - BIF (buil-in-function) того же RPG. Возвращает true если спозиционировались по равному значению ключа и false в противном случае.

Причем, работать это будет быстрее - во-первых, не потребуется строить план запроса в рантайме. Во-вторых, оно даже не читает запись и вообще не обращается к таблице, только поиск по индексу (дереву) - есть там нужно или нет.

Когда речь идет о работе с десятками и сотнями миллионов записей, разница во времени выполнения может быть в разы. Одна из недавних задач у меня была - поиск совпадений между адресами клиентов (порядка сотни миллионов адресов) и адресами субъектов списков росфинмониторинга (террористы, экстремисты, несколько десятков тысяч адресов). И сравнивать надо каждый с каждым. Причем, совпадение - это не равенство строк, а условие что "все уникальные элементы адреса субъекта входят (порядок не важен) в адрес клиента.
Так вот, задача решается на "чистом" SQL - можно (в наших конкретных условиях) написать SQL запрос, который сразу выдаст список совпадений. Правда, это был запрос на 3 экрана с кучей агрегаций и group by. Но... работает это несколько часов (с учетом того, что совпадения еще надо в таблице отдельной фиксировать). Реализованный на RPG алгоритм, где основное количество обращений к БД было именно в виде таких вот проверок наличия записи без чтения, отработал за 15 минут.

Object dates[] = select.one.fields("min(dta),max(dta)").from(table).where("id=",xxx).groupBy("id");

И опять. Вы используете какие-то внешние библиотеки. Дергаете SQL, который будет строить план запроса. Используете агрегации, которые сами по себе не быстрые. А можно все проще -

// Определим структуру куда читать запись
// она соответсвует стурктуре формата записи MYREC 
dcl-ds dsMyRec likerec(MYIDX.MYREC: *all);

// Посколькоу MYIDX построен по ID + DTA, то первая запись
// с заданным ID будет содержать минимальное значение DTA
setll ID MYIDX; // спозиционировались на первую запись для ID
read MYIDX.MYREC dsMyRec; // прочитали запись - тут будет минимальная дата

// обратная операция - SetGT устанавливает курсов после последней
// записи с заданным значением ключа
setgt ID MYIDX; // посделняя запись для ID - DTA тут будет максимальной
readp MYIDX.MYREC dsMyRec; // прочитали запись назад - получили запись с максимальной DTA

Как видите, простые задачи и решаются просто. "В один ход". Без библиотек. Штатными средствами языка.

Одна строка - данные подключения к БД - и вы получаете сгенерированные классы со всем необходимым.

Один момент. Здесь нет "подключения к БД". БД - это часть системы. Мы уже "внутри БД". Достаточно просто открыть файл и обращаться к нему средствами языка (чтение-запись) ровно как вы в С работаете с обычными файлами (ну только у вас тут файл структурирован и работает с записями и есть возможность искать нужное по индексам). Или в SQL просто пишем select.. и сразу получаем результат.

Далее - что такое в вашем понимании "БД"? Вот сколько таблиц там у вас? У нас - десятки тысяч объектов (таблиц и индексов). И на все прописывать классы? Извините, но зачем, когда можно работать без этого? Если можно просто прочитать запись как статически описанную структуру и дальше просто работать с ее полями как с обычными переменными?

Далее. Если речь идет о каких-то сложных запросах, я могу непосредственно в RPG коде написать:

        exec sql declare curRDMS14Clients cursor for
                   with Holders as
                        (
                          select F0UCUS1,
                                 F0UCLC1,
                                 F0UCUS2,
                                 F0UCLC2
                            from F0UPF F0U
                            join FPKPF FPK  on (FPKSCON, FPKCTP) = (F0U.F0USCON, 'F')
                          where (F0UCUS2 <> F0UCUS1 or F0UCLC2 <> F0UCLC1)
                            and F0UCD >= :$PDate
                            and F0USCID in ('00', '01', '02', '03', '14', '16')
                            and F0USCON like 'P%'
                        ),
                         
                        CheckOwner as
                        (
                          select RDKCUS    OWNCUS,
                                 RDKCLC    OWNCLC,
                                 RDKSER    OWNSER,
                                 RDKNUM    OWNNUM,
                                 RDKSERNUM OWNSERNUM,
                                 RDKOPN    OWNOPN,
                                 RDKEDT    OWNEDT
                            from RDKPF RDK1
                       left join HDA1PF on (HDA1CUS, HDA1CLC, HDA1TYP) =
                                           (RDK1.RDKCUS, RDK1.RDKCLC, :ActDteTp)
                           where (RDKSDL, RDKOSN, RDKUCD) = ('Y', 'Y', '001')
                             and (HDA1DAT is null or HDA1DAT < :YearAgo)
                        ),
                         
                        CheckHolder as
                        (
                          select RDKCUS    HLDCUS,
                                 RDKCLC    HLDCLC,
                                 RDKSER    HLDSER,
                                 RDKNUM    HLDNUM,
                                 RDKSERNUM HLDSERNUM,
                                 RDKOPN    HLDOPN,
                                 RDKEDT    HLDEDT
                            from RDKPF RDK2
                       left join HDA1PF on (HDA1CUS, HDA1CLC, HDA1TYP) =
                                           (RDK2.RDKCUS, RDK2.RDKCLC, :ActDteTp)
                           where (RDKSDL, RDKOSN, RDKUCD) = ('Y', 'Y', '001')
                             and (HDA1DAT is null or HDA1DAT < :YearAgo)
                             and not exists
                                     (
                                       select CAFCUS
                                         from CAFPF
                                        where (CAFCUS, CAFCLC, CAFATR1) = (RDK2.RDKCUS, RDK2.RDKCLC, 'Y')
                                     )
                        )
                    
                   select F0UCUS1,
                          F0UCLC1,
                          F0UCUS2,
                          F0UCLC2,
                          coalesce(HLDSER,    ''),
                          coalesce(HLDNUM,    ''),
                          coalesce(HLDSERNUM, ''),
                          coalesce(HLDOPN,    0),
                          coalesce(HLDEDT,    0),
                          coalesce(OWNSER,    ''),
                          coalesce(OWNNUM,    ''),
                          coalesce(OWNSERNUM, ''),
                          coalesce(OWNOPN,    0),
                          coalesce(OWNEDT,    0),
                          cast (case
                                  when (HLDEDT is not null)
                                    then 'Y'
                                  else 'N'
                                end as char(1)) FLAGM,
                          cast (case
                                  when OWNEDT is not null
                                    then 'Y'
                                  else 'N'
                                end as char(1)) FLAGN
                     from Holders
                     left join CheckHolder on (HLDCUS, HLDCLC) = (Holders.F0UCUS2, Holders.F0UCLC2)
                     left join CheckOwner  on (OWNCUS, OWNCLC) = (Holders.F0UCUS1, Holders.F0UCLC1)
                     where HLDSER is not null
                        or OWNSER is not null;

А затем

exec sql open curRDMS14Clients;

и дальше уже выгребать нужные записи:

exec sql fetch curRDMS14Clients for :sqlRows rows into :dsSQLData;

dsSQLData - массив структур размерностью sqlRows куда будут заносить данные выборки. В данном случае

      dcl-ds dsSQLData qualified dim(sqlRows);
        CUS1    char(6)     inz;
        CLC1    char(3)     inz;
        CUS2    char(6)     inz;
        CLC2    char(3)     inz;
        HSER    char(10)    inz;
        HNUM    char(35)    inz;
        HSERNUM char(45)    inz;
        HOPN    zoned(7: 0) inz;
        HEDT    zoned(7: 0) inz;
        OSER    char(10)    inz;
        ONUM    char(35)    inz;
        OSERNUM char(45)    inz;
        OOPN    zoned(7: 0) inz;
        OEDT    zoned(7: 0) inz;
        FLGM    char(1)     inz;
        FLGN    char(1)     inz;
      end-ds;

zoned - тип данных в RPG с фиксированной точкой, соответствующий типу numeric в БД.

Тут читаем сразу блоками по 1000 записей

dcl-c sqlRows     const(1000);

Прочитали блок - обработали в цикле, читаем следующий блок... Так бьыстрее.

И опять, заметьте, никаких библиотек - все это заложено в языке.

Любой язык, в котором есть так называемые "примитивные" типы данных. Правда придётся объявлять даты в БД как long, например. Но это если уж мы настолько зажаты в тиски, что даже на объект, оборачивающий long, у нас нет памяти. Мне жаль программистов, которым для решения бизнес-задач выдают что-то вроде IBM PC AT 286.

А теперь представьте, что у вас в распоряжении язык, где все эти типы уже есть. Т.е. вы можете определить переменную типа date (которая на 100% соответствует типу DATE в БД). И не надо ничего оборачивать - язык уже предоставляет вам все для работы с этим типом. Представить в виде строки или числа в нужном формате - пожалуйста. Добавить/вычесть нужное количество дней, месяцев, лет - запросто: dte += %days(10); Аналогично - время. Аналогично - числа с фиксированной точкой (все денежные расчеты только в таких форматах). Для DECIMAL в RPG есть тип packed, для NUMERIC - zoned. Вся арифметика - как с обычными числовыми типами (плюс для каждой операции можно указать что она должна проводиться с округлением).

Что касается на чем работаем - IBM Power E980, 120 процессорных ядер Power9, RAM 12Тб, 400Тб SSD массивы. Но на всем это одновременно крутится десятки тысяч процессов с самой разной логикой, за сутки проводится сотни миллионов бизнес-операций. И это в штатном режиме, когда загрузка сервера на уровне 50-60% А бывают режимы пиковой нагрузки, когда загрузка доходит до 90%. И нельзя допустить чтобы в таком режиме что-то начало тормозить - это сразу скажется на клиентах.

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

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

Ох... Мы о разных вещах говорим. То что мы тут делаем - это уровень ядра АБС (автоматизированной банковской системы). Это то, что "работает само". "Пользователи" (а кто это? клиенты? или кто?) вообще сюда доступа не имеют. Тот же мобильный банк или инетбанк или еще что-то - они все на "внешних системах". У них там нормальные интерфейсы. Они работают через какой-нибудь REST API который через вебсервисы посылает нам конкретный запрос и получает на него конкретный ответ. Но интерфейсы - не наше дело.

У нас есть некоторые интерфейсы для внутреннего пользования. Это текстовые экранные формы с полями для ввода (т.н. "дисплейные файлы", описываемые на специальном языке DDS - отдельная тема). Там есть валидация всего ввода, если что-то не то ввел, и поле подсветится и ошибка детальная выведется. Примерно так:

Попробуете ввести хтонь какую-нибудь, получите

Тут на самом деле вообще никаких проблем нет - все эти интерфейсы можно хоть руками на DDS писать,

хоть рисовать в специальном редакторе.

Работа с такой формой как с файлом - установил значение полей - write - вывелось на экран. Дальше read и пользователь вводит значения полей, потом возврат в программу и смотрите чего он навводил там.

Но это все внутреннее. Такого не сильно много, это типовые, "джуновские" задачи, там все по шаблонам, думать не надо.

Но основная часть того, что пишем работает в фоновых заданиях и никаких интерфейсов не имеет.

Я же говорю - IBM много чего полезного придумали. Но в вашем случае низкоуровневый подход, похоже, закрывает от вам общую картину.

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

А что описано в статье - это совершенно другой уровень. Это непроектная задача, создание инструмента, который потом будет использоваться (точнее, уже используется) в проектных задачах.

Хотя ibm i, возможно, предоставляет API для си-разработчиков, выполняющий именно то, что вы просите. Но вы забыли один момент - API кто-то за вас разработал. И включил его в состав вашего ПО.

Еще раз - это все штатные средства системы. Здесь все "из коробки".

У вас нет выбора. Функционал беден в сравнении с массой доступных для других платформ библиотек.

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

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

Нет. "отключить" что-то тут весьма затруднительно. Сервера изолированы от внешнего мира. К ним не добраться. Можно только посылать запросы (вебсервисы или очереди) и получать ответы.

И тут такая область, что любой зависит от единожды выбранной платформы - что-то поменять (перейти на что-то другое) займет годы и стоит будет космически (есть пример Банка Содружества Австралии и Океании - они попробовали поменять платформу - 5 лет, с 2012 по 2017 и $750млн денег).

Смысл существования такой библиотеки - сокращение затрат на написание кода, а не на сокращение потребления ресурсов (включая время).

А для нас потребление ресурсов (и скорость работы) является приоритетным фактором. В 2020-м году, когда уходили на удаленку, было 36млн клиентов. Сейчас - более 50млн. А за каждый клиентом тянется огромное количество данных. Т.е. объемы обрабатываемых данных постоянно растут. И очень много задач - "дефекты производительности" старого кода - просто переписываем старое (используя накопленный опыт "как делать быстрее и эффективнее").

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

Сколько строк кода занимает вот это все вот?

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

Здесь смысл такой - человечество придумало множество удобств, сокращающих затраты труда программиста. Но пока вы их не прочувствуете на личном опыте, вы не поверите. Надеюсь, что ООП вам знакомо, так вот на его примере мы видим объединение логики со структурами данных, которое даёт нам то самое упрощение. Нам нет необходимости заглядывать внутрь объекта, ведь мы знаем, что всё нужное там уже есть (алгоритмы и данные), поэтому мы просто используем выставленный наружу функционал. Сравните с привычным вам подходом на си - отдельно данные и отдельно функции (алгоритмы). И вам приходится структурировать программу так, что бы ненужные данные не захламляли вашу текущую задачу. А в ООП это структурирование уже выполнено.

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

Аналогично, встроенные в RPL средства содержат в себе какую-то логику, которую в них заложили их создатели. Но в тех дополнительных библиотеках, которые вы объявили ненужными, вся эта логика тоже есть, а плюсом к ней идут всяческие дополнения, позволяющие написать запрос, в том числе существенно сложнее показанных ранее, в одну строчку. Вам же приходится бегать по курсору, по сути переходя на уровень обработчика запросов в СУБД. И это всё вы делаете ради экономии на разборе SQL. Но эта экономия ничтожна. В показанном мною первом примере результирующий запрос мог бы выглядеть так: select * from table_name where id=?, или даже так: select id from table_name where id=?, если бы я явно указал список полей. Всё это самые тривиальные запросы, которые планировщик СУБД прекрасно умеет переводить в примерно тот код, который вы написали на RPL. То есть эффективность выполнения запроса будет ровно такая же, как у вас, кроме одного - разбора SQL-выражения. Но такое тривиальное выражение разбирается за микросекунды. Если вы сравните микросекунды со временем загрузки нужной части индекса с диска при поиске, вы поймёте, что экономить эту мелочь просто бессмысленно.

поиск совпадений между адресами клиентов (порядка сотни миллионов адресов) и адресами субъектов списков росфинмониторинга

И это всё тоже эффективно решается на SQL. Если у вас кто-то сумел сделать такое решение неэффективным (выполняется часы), это не значит, что эффективно сделать нельзя. Здесь просто нужно понимать суть алгоритма и то, как работает СУБД. Всё, далее я на SQL дам вам ваши же 15 минут, или даже быстрее. И собственно разбор SQL в СУБД займёт ну пусть даже несколько сот миллисекунд (хотя скорее десятков), но что это в сравнении с 15-ю минутами? И что важно - я это сделаю хоть на IBM i, хоть на любой другой платформе, где есть SQL.

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

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

Один момент. Здесь нет "подключения к БД". БД - это часть системы. Мы уже "внутри БД"

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

Вот сколько таблиц там у вас? У нас - десятки тысяч объектов (таблиц и индексов). И на все прописывать классы? Извините, но зачем, когда можно работать без этого? Если можно просто прочитать запись как статически описанную структуру и дальше просто работать с ее полями как с обычными переменными?

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

А теперь представьте, что у вас в распоряжении язык, где все эти типы уже есть

Мне не надо это представлять, сегодня практически в любом языке все типы есть. Другое дело, что они объектные, то есть оборачивают ту же дату в форме long внутрь класса, который уже умеет выдавать вам месяц, год, число и т.д. Да, за счёт расхода памяти на оборачивание мы получаем некоторые издержки, но это же копейки в сравнении с наличными ресурсами. Плюс встроенные средства RPL точно так же обязаны содержать какие-то обёртки над структурой данных, которая в DB2 содержит данные о дате. Так что опять получается никакой существенной выгоды вы от использования RPL не получаете.

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

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

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

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

А внутри библиотеки? Это же команда языка.

Надеюсь, что ООП вам знакомо, так вот на его примере мы видим объединение логики со структурами данных, которое даёт нам то самое упрощение.

И которое не везде применимо. У нас вот не очень. Представьте - на сервере порядка 30 тысяч программных объектов. И каждый из них реализует логику какого-то процесса. И эта логика в общем случае укладывается в схему "сформировать или получить извне поток данных и обработать его в соответствии с заданной бизнес-логикой".

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

Просто создаю объект, жму точку, IDE показывает список методов, выбираю нужный, и всё - задача решена.

Нет. Это может работать для примитивных задач. На самом деле в каждой новой задаче у вас будет свои условия выборки по своим таблицам которая дает свой набор данных. А потом еще 10 страниц текста в ТЗ что и как с этим набором данных нужно сделать. И чтобы выбрать нужный метод вам сначала придется его написать. Каждый раз. Для каждой новой задачи.

А примитивы типа "заменить в строке все вхождения 'abc' на 'cba' тут есть на уровне языка - тот же scan-replace - %scanrpl.

Или "разбить строку на массив слов по пробелам" - %split. И никаких объектов для этого не надо. Все уже в языке.

Но вот логика - это основное. Это везде свое. Общих мест не найдете - в каком-то случае вам надо реализовать рассылку уведомлений клиентам, которые попадают под какие-то условия, в каком-то - найти и зафиксировать с "стоп-листах" совпадения клиентов с субъектами списка росфина. Где-то вообще десяток разного рода проверок в рамках комплаенс-контроля платежей - там от платежного документа плясать будете...

Поэтому пишете на RPL там, где вполне можно писать на SQL и быстрее и с меньшим количеством ошибок.

Я вам привел пример задачи по поиску совпадений где решение на SQL работает несколько часов, а решение на RPG - 15 минут. По коду примерно одинаково. По сложности тоже.

SQL вообще не так быстр как вы думаете. Просто у вас не было возможности сравнить его с иными способами. А у нас есть вполне объективные метрики - PEX-статистики.

Мы используем SQL там, где это оправданно (опять же, опираясь на результаты PEX). Иногда, да, это быстрее. Иногда - медленнее.

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

Если надо из листа фанеры вырезать что-то фигурное - возьмем лобзик. Если раскроить на ровные прямоугольники - циркулярку. Дрова пилим цепной пилой. И т.д. и т.п.

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

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

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

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

Это не только память, но и ресурсы процессора. Когда все это происходит 1 000 раз - не заметите разницы. Когда 1 000 000 раз - уже заметите. И к вам придет сопровождение и скажет - ваша задача работает 40 минут и тормозит другие процессы. Нужно переделать.

Вот такое бывает получаем

Коллеги, сервис *** за последние 5 недель увеличил потребление процессорных ресурсов в 3 раза!!!
Он уже является 2-м по величине сервисом после *****.
В качестве альтернативы мы рассматриваем перенос запуска сервиса на резервный сервер, но там есть лаг по отставанию до 10 мин.
Заказчикам сервиса это может не понравиться :(

Идешь и смотришь код - мама дорогая... Какой-то программист-абстракционист писал. Абстракция на абстракции и абстракцией погоняет. Все очень концептуально, но... Работает медленно.

Делаешь как надо и в результате

Коллеги, доброе утро! Спасибо!
Уже утром видим очень хороший результат!!!
Доработка сокращает потребление процессорных ресурсов почти в 10 раз!

Или вот такое:

Вам сегодня должна прийти премия за особый вклад в развитие и оптимизацию нашей АБС, который сделан в рамках проекта ******, в части оптимизации процедур процедуры массовых проверок клиентов в части комплаенс процедур и проверок - рассылки уведомлений ***** (сокращение c 2 часов до 40 минут), ускорении процедуры *****, оптимизацию проверки *****

Ценим ваши усилия в этом и других направлениях вашей работы, неравнодушное отношение и подход!
Спасибо!

Быстродействие и эффективное использование ресурсов - это то, что тут всех интересует и ценится в первую очередь.

При этом можно писать "ровно", красиво и понятно. И классы с объектами тут совершенно не нужны. ООП хорошо в других местах - где у вас набор сущностей ограничен, где сущности могут наследоваться друг от друга и где поведение каждой сущности можно описать конечным образом.

Когда занимался разработкой системы мониторинга инженерного оборудования зданий - там это все очень хорошо ложилось в ООП - есть абстрактное устройство у него есть базовый набор свойств и методов. От него можно наследовать устройства конкретных типов (в т.ч. контроллеры нижнего и верхнего уровня) с конкретизацией свойств и методов, присущих данному типу. Есть "объекты" (где эти устройства установлены физически). Тоже прекрасно ложится в ООП. Но там действительно структура задачи описывается в терминах объектно-функциональной модели с обменом сигналами между объектами.

Тут нет. Слишком много сущностей и слишком много свойств у каждой из них. И каждая задача будет использовать свой уникальный набор этих свойств. Прописать все заранее нереально - возникнет новая задача и начинай все с начала. Тут описание идет не в терминах объектов и функций, но в терминах потока данных и его последовательных трансформаций. Попытка применить тут ООП - натягивание совы на глобус.

Хотя у новичков (и у меня тоже на первых порах было) - вот прям зудит. "Все сделать концептуально, на ООП". Потом проходит по мере погружения. Хотя С++ на этой платформе есть. И даже с ограниченной поддержкой типов данных (decimal есть). И работы с БД - есть RECIO библиотека для прямого доступа к БД, можно вставлять SQL код непосредственно в С++ код (никаких библиотек - у компилятора специальный препроцессор который все что нужно сделает).

Вот только профита перед RPG никакого... RPG как язык проще. И мощнее в плане описания структур данных. И писать на нем легче и быстрее. И код прозрачнее - никаких 10500 слоев абстракций которые непойми что и как там внутри делают. И в нет UB, как ни старайся. Или работает как сказали, или... получи системное исключение.

Плюс встроенные средства RPL точно так же обязаны содержать какие-то обёртки над структурой данных, которая в DB2 содержит данные о дате

И опять нет. Дата - это системный формат. Он поддерживается системой. Т.е. компилятор там где-то просто заменит функцию работы с датой на нужной MI в процессе компиляции. Как в БД, так и в языке. Просто в языке не нужно поверх него что-то еще наворачивать - для языка формат переменная типа date - точно такая же как int (например).

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

Во-первых, не язык. Этот самый DDS код компилируется в специальный файл (объект типа *FILE с атрибутом DSPF)

Формат его фактически совпадает с форматом таблицы БД. Он содержит несколько форматов записей - на каждый экран свой формат. И работа с ним точно также как с таблицей - объявляешь файл

  Dcl-F DSPFile workstn(*ext) InfSR(*PSSR)
               EXTDESC(#FMDname) EXTFILE(*EXTDESC) usropn
               ignore(CPJFMDZ);

Открываешь, устанавливаешь нужные значения полей и делаешь write - все выводится на экран. Делаешь read - пользователь вводит данные и вы получаете заполненные поля.

И все это работает очень быстро. Отрисовкой занимается терминал IBM 5250 - никаких браузеров тут не надо. И очень экономично - оно и на медленных каналах связи будет быстро работать и на любом самом слабом компе - эмулятору терминала много ресурсов не надо. В отличии от современных браузеров.

А чтобы ввести или изменить запись с таблице никакой графики не нужно. Не тот случай.

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

Еще раз. Я в разработке с 91-го года. И пробовал много чего - DOS, Win, Linux (и даже немного QNX, так, краем зацепило). Занимался математической обработкой данных, немного машинным моделирование физических процессов, достаточно много - промавтоматизацией. Работал с БД (одно время работал в группе обеспечения торгов на бирже). И не только с реляционными БД, но и БД, построенной на сетевой модели (где связь между разными типами записей можно строить не по ключам, на основе наборов - заданных на уровне структуры БД отношений "один-ко-многим" где одна запись является "владельцем набора", другие - "членами набора". И где у каждой записи есть "физический адрес в БД" который не меняется в течении ее жизни.

С теми же InterBase и FireBird работал через компоненты VCL - такой фреймворк на все случаи жизни (в т.ч. и для работы в БД через SQL) от Borland.

И процедурное программирование пробовал и ООП. Даже немного логическое на Prolog. Вот функционального не довелось - не попалось задач.

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

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

Поэтому сейчас использую как минимум два языка - RPG и C/С++ Бывает что код и на том и на том присутствует в одном бинарнике - с помощью интегрированной языковой среды (ILE) программы, написанные на любых языках ILE - C, C++, COBOL, RPG и CL, можно связать в один исполняемый файл. И бывает ситуации, когда вижу что "вот эту функцию мне проще не на RPG написать (хотя могу), а на C (потому что это будет изящнее по коду и работать будет быстрее). И нет проблем.

А внутри библиотеки? Это же команда языка.

Язык позволяет формулировать новые команды. Таким способом я получаю максимально удобное представление операций. А что там внутри новой команды, в момент использования библиотеки я понимаю, но чаще всего могу просто не думать об этом, потому что знаю, что преобразование в SQL будет достаточно эффективным. Хотя конкретно вам могу объяснить - вы видите, как происходит сбор данных для подготовки SQL, а внутри, уже в момент выполнения запроса, из собранных данных формируется корректный SQL, достаточно эффективный, то есть если нужно получить запись по id, то будет такой результат: select * from table_name where id=?, ну а потом, после получения результата, запись будет скопирована в соответствующую структуру, которая задаётся классом. Итогом будет объект, содержащий данные из запрошенной записи.

И которое не везде применимо. У нас вот не очень

Здесь вы уходите в область вашего представления о разработке. Я вам только замечу - ваше представление не является единственно верным. То есть другие представления могут быть эффективнее. Но что бы доказать это я буду вынужден растягивать эту беседу на очень долгий срок, чего мне не хочется делать. Скажу только коротко, что большая часть задач в теме OLTP (а именно эта часть актуальна и для банка и для кучи других организаций) автоматизируема до уровня "программист вообще не нужен". И без всякого модного ИИ. Другое дело, к счастью для многих программистов, со стороны бизнеса нет достаточного понимания самого себя, что бы эффективно пользоваться такими инструментами. А достигается полезный результат (при условии соответствующего по уровню развития бизнеса) именно переходом на высокие уровни абстракции, которые в RPL вы застрелитесь формулировать.

На самом деле в каждой новой задаче у вас будет свои условия выборки по своим таблицам которая дает свой набор данных

Вот, вот. Условия-то свои, а всё остальное - полностью идентично. Подставляй значения в шаблон и получай радость.

Но вот логика - это основное. Это везде свое. Общих мест не найдете

Найдём. На то и созданы абстракции. Их суть в том, что бы эти общие места находить.

Все структуры формируются в compile time. Дальше туда уже читаются данные

Здесь вы просто не поняли, о чём речь. Классы - это структуры, которые да, формируются при компиляции. И потом наполняются данными во время выполнения программы. Об этом я и говорил.

Идешь и смотришь код - мама дорогая... Какой-то программист-абстракционист писал. Абстракция на абстракции и абстракцией погоняет. Все очень концептуально, но... Работает медленно

Да, можно переусложнить, можно замедлить, можно вообще всё испортить. Но это не значит, что нельзя сделать эффективно. Хотя соглашусь, что эффективно сделать не просто, от того и появляются все перечисленные косяки.

Быстродействие и эффективное использование ресурсов - это то, что тут всех интересует и ценится в первую очередь

Не так. Вы указываете на стандартную ошибку - оптимизацию по простейшему критерию. Вы выбрали критерий - стоимость железа. Допустим ваш сервер стоит миллион долларов. И на него пишут программы суммарно 100 человек (для разных частей АБС). Допустим у них скромная з/п, со всеми накладными (налоги, офис, страховки и т.д.) путь на человека будет 250 т.р. Итого имеем 25 миллионов рублей в месяц, или примерно 3 миллиона долларов в год. Если железо полностью заменяется раз в 5 лет, то расходы на людей в 15 раза больше его стоимости. То есть если сократить затраты труда программистов на 1/15, получим выигрыш в размере стоимости железа. За 5 лет при выделении миллиона долларов на сокращение затрат труда программистов абсолютно реально не то что на 1/15, а весьма вероятно на десятки процентов сократить потребность. Вот вам и пример - вы выбрали ошибочный критерий оптимизации, то есть если бы выбрали критерий "стоимость работы программистов", то получили бы в разы большую экономию.

потому что знаю, что преобразование в SQL будет достаточно эффективным

Откуда вы это знаете? Это что-то, подтвержденное нагрузочными тестами и сбором performance статистик (с указанием - вот тут SQL занял столько-то процессорного времени, тут столько-то), или просто слепая вера?

Еще раз - огромное количество собранных статистик у нас показывают что SQL не во всех случаях является оптимальным с точки зрения производительности.

Я даже больше скажу - SQL это всегда накладные расходы на построение плана запроса. Который может меняться в зависимости от многих фактором (вплоть до объема данных - на разных распределениях количества данных по таблицам SQL для одного и того же запроса может давать разные планы). Плюс расходы на маппинг хост-переменных в/из запрос.

А дальше встает вопрос регулярного выбора - какова доля этих расходов в итоговом результате. Где-то ей можно пренебречь (иные подходы хуже), где-то она слишком велика.

ну а потом, после получения результата, запись будет скопирована в соответствующую структуру, которая задаётся классом

Это статическая структура, объявленная во время компиляции и куда вы просто читаете с диска блок данных (запись), или класс, у которого в рантайме вызывается конструктор, выполняющий каки-то преобразования?

Вопрос в том - сколько дополнительных операций от физического чтения блока данных с диска до возможности написать record.date += %days(7)

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

Мое представление о разработке таково - для каждой задачи есть наиболее эффективный инструмент. И надо уметь его использовать. И не пренебрегать возможностью его использовать.

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

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

Вот, вот. Условия-то свои, а всё остальное - полностью идентично. Подставляй значения в шаблон и получай радость.

О каком шаблоне идет речь? Вот три ситуации:

  1. Нужно отобрать клиентов у которых

    1. Срок действия основного документа или уже истек или истекает через 30 дней

    2. Еще не посылалось уведомление

    3. Для каждого из этих клиентов сформировать и отправить сообщение соответствующего типа

  2. Нужно для всех клиентов найти совпадения с субъектами списков росфина по признакам

    1. Совпадает наминования (если это ЮЛ) или ФИО + ДР (для ФЛ)

    2. Или совпадает ИНН

    3. Или (для ФЛ) совпадают реквизиты документов

    4. Есть совпадения по адресам

    5. Каждое совпадение должно быть зафиксировано в стоп-листе - если оно там уже есть, проверить набор признаков совпадения, если изменился обновить, если совпадения нет - добавить, если в стоп-листе есть совпадения, которые отсутствуют в актуальной версии списка - их нужно удалить

  3. Есть платежный документ Нужно

    1. Классифицировать платеж (исходящий - от клиента к неклиенту, входящий - от неклиента к клиенту, внутренний - между клиентами), классифицировать плательщика и получателя (ФЛ-ЮЛ).

    2. В зависимости от типа платежа, класса плательщика и класса получателя необходимо провести набор проверок (вхождения плательщик/ получателя в черный/белый списки, суммы на кредитных/депозитных счета определенных типов в рублевом эквиваленте у плательщика и получателя, наличие рисков и запретов у плательщика и получателя и т.п. - всего порядка 10-ти проверок - последовательность, параметры - зависит от типа платежа и классов контрагентов)

    3. По результата проверок выносится решение - пропустить платеж или отправить на ручной контроль в финмон. Если на контроль - что именно и почему вызвало подозрения.

Вот вам три задачи. Реальных (правда упрощено - там не один десяток страниц постановки по каждой). Покажите мне общий шаблон, куда можно "просто подставить данные чтобы стало щасте". Там вообще ничего общего.

Найдём. На то и созданы абстракции. Их суть в том, что бы эти общие места находить.

Вот и попробуйте найти для приведенных выше задач.

Если для вам шаблон это "написать SQL и запихнуть его в свойства компонента" - так для нас это вообще не вопрос - никаких компонентов - просто пишем exec sql select ... into ... from ... в RPG программе и вперед - SQL препроцессор RPG компилятора этот запрос еще и проверит на этапе компиляции и выдаст ошибки и/или предупреждения если что не так.

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

И, заметьте, я говорю только по соей тематике - автоматизация процессов комплаенс-контроля. А есть еще куча других - система расчетов, тарифы, лимиты, депозиты, универсальный кассовый модуль, зарплатные проекты, модуль пластиковых карт... Там свои задачи и все ничуть не проще.

Здесь вы просто не поняли, о чём речь. Классы - это структуры, которые да, формируются при компиляции. И потом наполняются данными во время выполнения программы. Об этом я и говорил.

Класс - это немного больше структуры. Структура времени компиляции - это байтовый буфер с разметкой на поля. Все.

Класс - намного более сложная сущность. У него как минимум есть конструктор и деструктор, который (даже есть он ничего не делает) вызывается в рантайме при объявлении переменной соотв. типа. У класса есть методы и, соответственно, таблица методов (всякие VMT и т.п.). У класса определяется уровень доступа к данным. Все это намного сложнее простой структуры. Странно всего этого не понимать тому, кто с ООП серьезно работает (не на уровне "ну... классы это типа как структуры, только с методами внутри" - это примитивное объяснение для школьников, в жизни все сложнее)

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

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

Ошибки в том же комплаенс-контроле могут привести к обвинениям в сомнительных финансовых операциях. А это тоже штрафы и в пределе лишение лицензии.

Цена некачественного кода очень высока тут. Это не интернет-лабаз, где ну не смог клиент оформить заказ на зарядку для телефона, да и бог с ним. Никаких особых последствий не будет.

Попробуйте с этой точки зрения посмотреть.

Откуда вы это знаете?

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

огромное количество собранных статистик у нас показывают что SQL не во всех случаях является оптимальным с точки зрения производительности

Я не говорил, что стандартный инструмент во всех случаях лучше. Я говорил, что он во многих случаях (в большинстве случаев) экономит усилия программиста при достаточно эффективном результате. Достаточно эффективный, это такой, когда нет смысла экономить миллисекунды при времени исполнения минуты.

Это статическая структура, объявленная во время компиляции и куда вы просто читаете с диска блок данных (запись), или класс, у которого в рантайме вызывается конструктор, выполняющий каки-то преобразования?

Эта структура - класс. С накладными расходами в виде конструктора и чего-то ещё. Теперь математика: время чтения с диска, пусть будет SSD, положим 1 миллисекунда, затем преобразование в нужную нам структуру. За миллисекунду пусть прочитали 100 килобайт по сто байт структур (это пиковая, редко в реальности достижимая скорость выборки строго нужных данных). То есть всего 1000 структур. Время выполнения конструктора и прочего для каждой структуры - пусть 100 наносекунд, хотя всё это в кэшах и в цикле, так что процессор много чего может оптимизировать. Итого имеем 100 микросекунд на всё. Всего имеем примерно 10% затрат на конструкторы и 90% на чтение. Далее напишите всю обработку вручную, вместо одной строчки, как я показывал. Сравните свои затраты с моими. Получите в 100 раз больше, чем у меня. Вспомните про стоимость программистов.

для каждой задачи есть наиболее эффективный инструмент

Есть всего лишь эффективные и неэффективные решения. Разница определяется расчётом. Это просто математика. Её вполне способен считать компьютер.

Вот три ситуации:

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

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

Сэкономили на разработчиках, сэкономили на тестировщиках. Получили плохой код. В результате в период "пиковых нагрузок" все встало колом

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

Далее напишите всю обработку вручную, вместо одной строчки, как я показывал

Обработку чего? В так и не поняли? Допустим, у меня в БД есть поля типов Date и Decimal(15,0). Я пишу структуру:

dcl-ds dsMyRec;
  dteFld date;
  decFld packed(15:0);
end-ds;

Все. Никаких вычислений. Я просто прочитал (как в байтовый буфер) в нее данные и пользуюсь. Типы date и packed (аналог decimal в БД) для RPG "родные" - он их понимает и умеет с ними работать. Зачем нужно что-то еще? Чтобы что?

Дальше уже что угодно

dsMyRec.dteFld += %days(10);       // Увеличили дату на 10 дней
str = %char(dsMyRec.dteFld: *iso); // Преобразуем в строку в формате ISO
str = %char(dsMyRec.dtaFld: *eur); // и в формате EUR
dsMyRec.decFld += 50;              // Увеличили число на 50

Работаем с полями структуры средствами языка.

И где тут затраты на "написание обработки"?

И я так и не понял что в вашем понимании "шаблон"? Запрос на отбор данных? Так я его просто в код вставлю - exec sql select ... и все. Дальше в цикле уже обработка результатов, прочитанных в структуру данных (см. выше).

Запросы везде разные. Т.е. их все равно прописываем руками. логика обработки везде разная. Это все равно руками писать надо. Так в чем профит ваших "шаблонов"? Чтобы получить данные мне нужно описать структуру и вставить в код текст запроса. Никаких преобразований типов из БД в типы языка тут не нужно - все типы совпадают.

Всего имеем примерно 10% затрат на конструкторы и 90% на чтение.

10% затрат, которые можно избежать в наших реалиях - очень много. Непозволительная роскошь. Потому десятки и сотни миллионов записей в выборке у нас нормальное дело.

Вот типичный ответ с нагрузочного тестирования

Из PEX статистики работы ******  видно, что 33% времени и 36% ресурсов  CPU  тратится на выполнение QSQRPARS в программе *****, т.е. парсинг статических выражений при подготовке SQL запроса,  

Сократить данные русурсозатраты практически до нуля можно путем описания параметров sql запросов  через SQL Descriptor Area (SQLDA).

Поскольку ***** один из наиболее активно используемых сервис модулей, необоснованное повышенное ресурсопотребление является малодопустимым. Просьба инициировать доработку STL#CHKN.

Тут могут предъявить за излишне интенсивное использование динамической памяти - new/delete. Так что если можно сделать на 10% быстрее - это просто роскошно... Ну такая вот специфика.

И есть что - как работать с БД через SQL компоненты - я знаю и умею. И это ну ничуть не быстрее чем делать тоже самое через embedded sql в RPG. С той разницей что ошибку в sql в компоненте вы увидите только когда оно на тесте упадет, а я когда sql препроцессор ругнется при компиляции.

Неужели вы думаете, что если бы на С++ с каким-нибудь фреймворком было бы все это делать эффективнее, им бы тут не пользовались? Но на этой платформе более 80% кода пишется на RPG. Потому что основное назначение платформы - работа с БД и бизнес-логика. А RPG специально для этих целей разрабатывался. Чтобы было удобно писать и в результате получался эффективный код.

Тут могут предъявить за излишне интенсивное использование динамической памяти - new/delete

Вот это довольно показательный момент. Он суммирует наш разговор. Вместо быстрого решения задач вам приходится думать о быстрой работе компьютера. Про стоимость работы программиста я вам писал, но раз вам "предъявляют" за минимальные попытки ускорить написание программ, то здесь я уже не могу помочь. Выбор в сторону оптимизации скорости исполнения сделан не вами, но вы его полностью приняли и отказываетесь воспринимать альтернативы. 10% по вашему очень много. Ну ладно, я вам писал про в 15 раз дороже в случае сравнения стоимости компьютера и труда. Это тоже вас не заинтересовало. Но чем тогда вас убеждать?

Вот совсем коротко:

  1. Труд дороже железа. Доказать пытался, но не вышло.

  2. Бой за 10% ускорения стоит в разы дороже, чем решение той же задачи без ускорения на 10%. Опять пытался доказать, но не получилось.

    Что ещё сказать? Я не знаю.

Прежде всего, вы не смогли меня убелить в том, что

Object dates[] = select.one.fields("min(dta),max(dta)").from(table).where("id=",xxx).groupBy("id");

Будет чем-то проще чем

dcl-ds minnmaxDte qualified;
  id     int(10);
  minDTE date;
  maxDte date;
end-ds;

exec sql select id,
                min(dte),
                max(dte)
           into :minnmaxDte
           from table
          where id = :id
       group by id;

Это - работающий, компилирующийся код.

Дальше я уже могу сразу работать с полями структуры minnmaxDte.

Причем, в таком подходе sql препроцессор еще и проверит правильность sql запроса на этапе компиляции, подготовит все нужные SQLDA (SQL Descriptor Area), SQLCA (SQL Commwnication Area) и преобразует exec sql ... в вызовы системных API SQL движка.

И это static embedded sql где часть подготовки запроса (формированеи SQLDA/SQLCA) будет сделана на этапе компиляции. А у вас dynamic sql, который приводит к

Из PEX статистики работы ******  видно, что 33% времени и 36% ресурсов  CPU  тратится на выполнение QSQRPARS в программе *****, т.е. парсинг статических выражений при подготовке SQL запроса

Т.к. все формирование SQLDA/SQLCA будет идти в рантайме (у нас такое доже возможно - dynamic embedder SQL где строка запроса формируется в рантайме и требует отдельной операции exec sql prepare ...), но это считается крайне неэффективным подходом и допускается в самых исключительных случаях, когда других вариантов просто нет (иногда там приходится руками SQLDA прописывать и потом вызывать запрос с using descriptor).

Т.е. при равных трудозатратах мой код и эффективнее и ошибки в запросе выявляются сразу - ошибочный запрос просто не скомпилируется (вы же можете любую хтонь написать и это выявится только в рантайме).

Мне не удалось вас убедить в том, что вот эти Object, select у вас - это то, чего в вашем языке не хватает для решения задачи. А у меня все это уже есть "из коробки".

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

Есть много языков программирования. Как универсальных, так и DSL (Domain Specific Language), разработанных для решения узкого класса задач. Мы работаем именно с таким. И, будучи универсальным, он максимально эффективен как по удобству разработки (применительно к классу решаемых задач), так и по эффективности получаемого результата.

По поводу скорости разработки. Во-первых, она не ниже чем у вас за счет использования DSL. Во-вторых, больше времени уходит на тестирование. Мы работаем с деньгами. С большими деньгами. С чужими деньгами. Кроме того, мы находимся под жестким контролем регулятора (ЦБ). Так что цена ошибки тут очень высока в самом прямом смысле слова. Тут недопустимо "херак-херак и в продакшн, упадет - на ходу починим". Каждая более-менее серьезная ошибка на проме - это очередной героизм от сопровождения (дежурной смены), а дальше комиссия по инцидентам и разборки с разного рода "выводами".

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

По поводу стоимости. От разработчиков никуда не деться - постоянно появляются новые процессы, требуются изменения в логике старых (изменения в законодательстве, требованиях регулятора...). Кроме новых бизнес-процессов постоянно растут объемы данных - в 20-м году, когда уходили на удаленку, было 36млн клиентов. Сейчас - более 50-ти. А каждый клиент - это огромный "паровоз" данных и связанных сущностей - клиентские данные (одних типов адресов у клиента более 5-ти штук), счета (даже у обычного "физика" их несколько, а у крупных "юриков" может быть и десятки, а то ситни), карты, доверенные лица (со своими данными), держатели карт, риски (репутационные, страновые, санкционные и т.п.). Так что миллион новых клиентов тащит за собой насколько десятков миллионов разных связанных с ним сущностей.

Все это (и новые клиенты и новые процессы) постоянно увеличивают нагрузку на сервер. И если писать неэффективный код, то наращивать сервера (которые очень и очень недешевые) придется каждый год (в наших условиях - раз в 3-4 года - за те почти 7 лет что тут работаю - один раз перешли с Power8 на Power9, а до этого несколько лет на Power7 жили, сейчас оттягиваем момент перехода на Power10).

Так что ваша арифметика "сменил один раз сервер и в шоколаде" тут не работает. Тут вопрос в том - как часто вы его будете менять.

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

Более мощные сервера тащат за собой большее потребление энергии (в том числе и на кондиционирование), больших емкостей резервного энергоснабжения (тут не бытовые UPS'ы стоят, а мощные ДГУ, способные держать не только сервер, но всю инфраструктуру вокруг него).

Так что подход "фигня - клиент купит новый сервак чтобы не тормозило" крайне примитивен и не реалистичен. В жизни все намного сложнее.

Да, забыл. Очень хотелось бы посмотреть на конструкцию select... вашу для запроса по 12-13 таблицам, содержащую 6 "обобщенных табличных выражений" (with ... as (...)). Ну и джойны, группировка, агрегация, само собой... Короче, там на три экрана запрос.

Сдается мне, что выглядеть это будет весьма затейливо...

В то время как у себя ч такие запросы сначала "откатаю" в интерактивном SQL - посмотрю что он выдает, как работает, план запроса проанализирую, а потом просто копипастом перенесу готовый запрос в RPG код, добавив впереди exec sql. И кто быстрее в результате справится? :-)

Sign up to leave a comment.

Articles