Ну не знаю… Долгое время вообще не задумывался. Фронт, бэк… Нужно было делать интерфейсы — делал интерфейсы. С нуля. Т.е. сам придумывал, проектировал, писал. Потом понял что мне больше нравится колупаться в поторохах. Алгоритмы обработки данных, протоколы обмена, использование системных API для получения максимальной функциональности и эффективности…
Когда менял работу, тоже особо не задумывался. Но сложилось удачно — попал в самы глубокий бэк, команда ядровых функций АБС. Что идет от вебсервисов из мобильного клиента, интернет банка, из Пеги, попадает к нам. Ну и то, что работает в фоне — обработчики очереди сообщений, журнальные мониторы, репликаторы, регулярные отчеты…
Есть немного интерфейсов, но онииспецифические — текстовые. Т.н. «опции» для интерактивного ввода и редактирования различных таблиц.
Там, кстати, очень много софта идет в комплекте именно для кроссплатформенной разработки.
Ставится IBM Installation Manager, а потом уже через него много чего можно поставить. WAS для запуска вебсервисов, которые будут работать с аской, RAD — тоже на базе эклипса, но заточена под разработку вебсервисов.
Access i Client Solution — пакет где эмулятор терминала, интерактивный sql, средство для просмотра спулов (вывод на принтер), средство для работы и файлами бд…
Там очень много софта, позволяющего работать с сервером. А сам сервер… Такая дура размером с большой холодильник. Ниимонитора ни клавиатуры там нет.
Система самодостаточна в рамках своего предназначения — высокопроизводительные коммерческие серверы.
Саодостаточность в том, что в нее все необходимое уже интегрировано на уровне самой системы. Т.е. таблица БД это не просто файл, но системный объект.
Там сразу будет и БД и компиляторы и отладчик. И да, есть SEU — редактор для написания кода. Есть интерактивный SQL.
Но это сервер. У него нет монитора, нет клавиатуры. Общение с ним идет черех удаленный терминал IBM5250 или его программный эмулятор. Там все хардкорно — текстовые менюшки и команды языка CL.
Понятно, что через RDi работать удобнее. Хотя можно пользоваться любой средой, есть любители Visual Code, кто-то вообще в Notepad++ пишет. Без разницы, если настроишь гредл.
Есть проект, в нем исходники плюс скрипты под гредл плагин. Написал, потом gradle as400syncdeploy и оно само сгенерируе программу инсталлятор, забросит все на сервер, скомпилирует и запустит там инсталлятор.
Просто в RDi это делается нажатием кнопки, а к другой среде или прикручивать в инструменты или просто запустить из командной строки.
Это нормальная кроссплатформенная разработка. Это свой мир со своими порядками.
Абсолютно ничего общего с win и *nix Это принципиально иная школа осостроения. Одни TIMI (Technology Independant Machine Instructions) чего стоит. Там принципиальнотнет ассемблера. Есть эти самые MI. Каждая из которых работает с объектом (все есть объект). Вся система состоит из объектов. У каждого объекта имя и тип. Файлов как таковых тожеинет — объекты.
Программный объект кроме исполняемого кода содержит еще timi код. И при каждом запуске проверяется — собран ли этот программный побъект под данную архитектуру или нет. Если нет, то исполняемый код автоматически перегенерируется из timi.
Вот заменили у нас сервер с powers 824 на powers 924 (тестовый сервер для разработки). После этого все pgm при первом запуске на новом проце автоматически перегенерировались под него. С оптимизацией под данный конкретный проц.
Де модели памяти — одноуровневая singlelevel и terraspace (ее не разрешают нам). В одноуровневой сквозная адресация памяти и диска. Вот есть такой объект userspace — что-то типа двоичного файла в других ос. На него можно получить указатель и работать с ним как с областью памяти.
Размер указателей, кстати, 16 байт :-)
Концепция групп активации тоже не сразу заходит в голову. Это нечто типа изолированного контейнра памяти. В рамках одной задачи их может быть несколько. Когда запускается новая программа (а она может запускаться из другой программы в рамках одной задачи), она может создавать свою группу активации или запускаться в той, которой работает запускающая программа.
Глобальные и статические переменные будут сохранять свое значение пока группа активации не закроется. Т.е. можно в рамках одной задачи запускать одну программу несколько раз и глобалы и статики будут сохранять свои значения с прошлого запуска…
Да не за что. Система очень мощная не похожая ни на что другое (концептуально она объекно-ориентированная — «все есть объект»). И самодостаточная. Т.е. в ней сразу есть все что надо. И компиляторы и отладчик и БД и инструменты администрирования.
Если кому интересно — книга одного из отцов-основателей: Френк Солтис «Основы AS/400». От истории создания до внутреннего устройства весьма доходчивым языком.
От ILE (Integrated Language Environment) просто в восторге. Поскольку до этого без малого 30 на С/С++, то возможность часть задачи, там где это удобнее, писать на С, а часть на RPG очень радует. В целом работа с таблицами, строками, экранными и принтерными формами на RPG удобнее, а вот всякие системные вещи типа организации распараллеливания с межпроцессным взаимодействием через сокеты, динамические массивы и списки — это удобнее на С/С++.
Пишешь кусок кода на RPG, кусок на C/C++. Потом каждый кусок компилится в отдельный модуль (аналог объектного файла) и потом уже все модули собираются в единый программный объект.
И на фронте и на бэке свои плюшки. У фронтов постоянные проблемы типа «а на моем телефоне не работает».
На бэке — сопровождение «поставку не согласуем т.к. фукнция xxx жрет 30% процссорных ресурсов выделенных заданию что недопустимо».
Или как недавно было — все тесты прошли, включая нагрузку, а на проме упало. По джоблогам и дампам примерно понятна проблема, но воспроизвести не удается никак и нигде. Соответственно, подозрительный участок кода переписан, но внедряться страшно т.к. проверить что помогло не получается.
А модуль работает в таком участке, где лажать нельзя — прокол может обернуться штрафом со многими нулями от регулятора (в лучшем случае).
Это бывшая AS/400 со всеми вытекающими. ILE, включающая в себя CL, RPG, COBOL, C/C++ (компиляторы встроенные, любой программный объект может содержать несколько модулей на разных языках, главное правилтно интерфейсы прописать, функции сишной RTL можно высывать, например, из программ на RPG). Отладчик встроенный, БД встроенная, DB2.
Пишем в основном на RPG, но бывает и на С, если это удобнее. Достаточно много сервисных программ написано на С/С++.
Средства разработки — RDi (IDE на базе эклипса, заточенная на работу с ас-кой. Т.е. пишешь на обычном компе, потом оно все забрасывает на сервер и там собирает. Для пущего удобства приручен gradle — пишутся сборочные скрипты, которые обрабатываются плагином.
Обычный путь — сначала гредлом с компа ставим в девелоперский юнит (проверка что все собирается), оттуда ручным запуском сгенерированного гредлом же инсталлятора в эмуляторе терминала ставим в песочнтцу (копия юнита компонентного тестирования). Если там работает, то коммитися в битбакет, оттуда подхватывает дженкинс и кладет поставку в артифактори. А оттуда уже аналитики девопсом ставят куда им надо для тестов.
В целом как везде — есть типовые задачи, а есть такие, над которыми думать надо.
Всегда скучно было рисовать интерфейсы. Ну не мое это. А вот колупаться в потрохах, системных API, быть близко к железу, в том числе и нестандартному, это нравилось и нравится.
Хотя интерфейсы разрабатывать приходилось, и получалось неплохо в плане функциональности и удобства. Но не увлекало. Так что в сторону бэка сместился уже очень давно.
Хотя к вебу никакого отношения не имею и не имел. Долгое время занимался разработкой системы мониторинга инженерного оборудования зданий. Система была гетерогенной, распределенной, построенной на микроядре. Т.е. в центре некое ядро, к нему цепляются и через него общаются удаленные микроконтроллеры (IP-шлюзы, за которыми уже на 485-м интерфейсе висели контроллеры нижнего уровня) и удаленные же интерфейсные клиенты (различной тематической направленности). Там занимался тем самым микроядром — архитектура, протоколы обмена и все прочее.
Сейчас банк. Ядровые функции автоматизированной банковской системы. То, что работает на мейнфрейме IBM i. В общем случае все, что работает на фронтах, общается с АБС через вебсервисы. Которые вызывают некие универсальные процедуры на мейнфрейме, а те уже дергают различные обработчики — вот их и пилим.
Не уверен, что прибыль в данном случае непрямую связана с качеством разработки. Она от рекламы идет. И тормозной сайт, перегруженный трекерами и рекламой может приносить неплохую прибыль, несмотря на то, что с точки зрения кода там все ниже плинтуса.
И никаких стонов, не стоит на свой счет принимать. Просто давно живу, много видел. В том числе и вундеркиндов, блиставших на олимпиадах, но неспособных в срок и без проблем довести до прома несложную задачу.
Я не очень уверен, но порой кажется, что те компании, где на собеседованиях упирают на решение олимпиадных задач, в итоге получают соответствующих сотрудников-«олимпиадников». Олимпиадные задачки они решать могут, а вот заниматься коммерческой разработкой (со всей ее рутиной, workflow, нечеткими формулировками FSD, ответственностью не только за «пуговицы», но за всю задачу целиком) у них не очень получается.
Ну я не знаю за ваш бэк, а у нас голангом не обойдешься. Будте любезны для начала RPG освоить, да еще научиться читать свободно чужой RPG код на древнем fix формате, потом желательно C/C++ потом вникнуть во все «нефункциональные требования» — что можно а что нельзя сточки зрения эффективности.
А чтобы стать действительно хорошим рабаботчиком на бэке, забдте про все фреймоворки — из тут нет и учите потроха AS/400 — что там и как работает. Ибо одним языком тут не обойдешься, придется вникать в системные API.
Ну и примите во внимание, что на сервере кроме вашей еще тысячи задач крутятся и все они между собой взаимодействуют так ли иначе. Один журнальный монитор чего стоит…
Ну и внешние сервисы, да… Скажем, получили некоторую строку, закинули ее в таблицу, она оттуда автоматом через очередь улетает на парсинг в некйи внешний сервис, оттуда опять через очередь прилетает обратно вам… И не дай бог в этом внешнем сервисе что-то поменялось… Вдруг. Разгребать придется опять вам.
Так что везде свои тараканы. Эт только со стороны кажется что все тихо и спокойно.
Но в back end практически не бывает так, чтобы без каких-либо изменений в вашем коде/настройках/Dockerfile/K8s/whatever, вдруг внезапно всё сломалось и перестало работать.
Да ну?
Не, ну наверное, если весь бэк состоит из одного вашего кода, то оно да…
Но ведь бывает и так, что бэк — это сотни а то и тысячи задач, крутящихся на одном мейнфрейме и так или иначе между собой взаимодействующих. И ваша задача может прекрасно работать до тех пор, пока то-то не скатил свой патч, который сломался сам и сломал вашу и еще десяток других задач.
Чтобы этого избежать существует т.н. «интеграционное тестирование» (помимо компонентного, нагрузочного и бизнес).
Для всего этого не нужно использовать «модные» фишки новейших фреймворков и кучу транспайлеров, порождащих в итоге мегабайты кода, с которыми потом еще-еле работают даже топовые системы.
Золотые слова.
Вообще, что-то менять без крайней на то необходимости, да еще использовать при этом какие-то новомодные фичи, которые будут работать только на самых последних версиях клиентских девайсов — классическая чешижопица от нечего делать.
Править только дефекты, допиливать только те фичи, которые реально необходимы.
А по поводу новых фреймворков — в одном месте, где работал, было правило — все новые разработки тестируются на откровенно слабом и старом железе. И если там оно работает и не тормозит, тогда уже переход к тесту на железе реальном. Очень дисциплинировало в плане написания нормального кода. Не борьбы за наносекунды, но просто вдумчивого подхода к тому что делаешь и как делаешь.
Ваша самоуверенность вполне типична для back end разработчика, который заглядывал во front end пару раз, не осилил, и теперь делает очень категоричные заявления о том, что реально нужно для большей части сайтов.
Ну на бэке свои проблемы, поверьте :-) И их хватает.
ну вообще здесь речь идет о SQLRPG — туда возможно засунуть любой SQL запрос.
Ну и никто не заставляет использовать именно SQL. Повторюсь — есть «нативные» функции для доступа к данным. Там вообще хитрая история (описана одним из «отцов-основателей» AS-ки Френком Солтисом в книга «Основы AS/400» — книга сама по себе занимательная т.к. там от истории эволюции System/3 — System/36 -System/38 — AS/400 до описания ее внутреннего мира, что как устроено и как работает) — было две команды, работающие над DB2/400. Одна разрабатывала концепцию DDS (Data Defenition Specifications) — язык описания данных, позволяющий достаточно единообразно описывать физические файлы (хранилища), логические файлы (способы доступа к хранилищам), дисплейные файлы (экранные формы) и принтерные файлы (формы для вывода на печать). Эта команда разработала т.н. «нативный» интерфейс позволяющий получать доступ к данным посредством функций типа read write update delete (в RPG несколько усеченный вариант, полный вариант содержится в RECIO — специфическое расширение C RTL для AS/400 — www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rtref/recioh.htm)
Вторая команда работала над SQL/DB2.
И о чем-то они не договорились. В результате сделали и то и это.
В каких-то случаях удобнее пользоваться SQL, в каких-то нативом, хотя там чтобы написать простенький запрос с парой join'ов, конечно, приходится написать прилично кода.
В ряде случаев натив оказывается быстрее и менее ресурсоемким, в каких-то наоборот.
В том случае, что описываю я, старое решение (на SQL, не прошедшее нагрузку) отрабатывало на тестовом юните за 21мс. А написанное мной нативное решение (хотя и более длинное по коду) там же и на тех же данных — 7мс.
Сейчас вот ждем что нагрузочники скажут. Сам не имею доступа к PEX (инструмент, позволяющий анализировать загрузку процессора).
Тут с производительностью есть особенности чисто AS/400. Там есть понятие «группа активации». Например, в рамках одной задачи (JOB) некая программа вызывается несколько раз. В первый раз для нее создается группа активации (можно ими управлять — наследовать от вызывающей программы, всегда создавать новую, создавать, если еще не создана, именованую под конкретную программу и т.п.)
Фактически группа активации есть некий контейнер внутри задачи, в котором сохраняются все статические и глобальные переменные. Т.е. при первом вызове программы они проинициализаируются, а при втором (если группа активации не была принудительно закртыта) они будут иметь те значения, что остались от предыдущего вызова.
Посему, у каждой программы всегда есть кусок кода, который вызывается только один раз, при создании группы активации. Туда вынгосятся (по возможности) всякие «тяжелые» процедуры, которые можно не делать каждый раз. Если рассмотреть наш вариант с SQL запросом
select FIELD1, FIELD2 from TABLE where FIELD3 = var
где var — переменный параметр, определяемый при каждом запуске.
В SQLRPG это делается так:
strSQL = 'select FIELD1, FIELD2 from TABLE where FIELD3 =?';
exec SQL prepare stmtSQL from :strSQL;
exec SQL declare curSQL cursor for stmtSQL;
exec SQL open curSQL using :var;
exec SQL fetch next from curSQL into :fld1, :fld2
exec SQL close curSQL;
тут var — переменная, содержащая параметр для поиска, fld1 и fld2 — переменные в которые получаем результат из очередной строки выборки.
Так вот. В данном случае exec SQL prepare и exec SQL declare выносятся в тот блок кода, который вызывается один раз при создании группы активации. А в той части кода, что вызывается при каждом запуске программы остается только open с текущим значением параметра, fetch и close.
Но. Беда в том, что с массивом переменных это не работает. Т.е. в случае с
select FIELD1, FIELD2 from TABLE where FIELD3 int ('val1', 'val2', 'val3',...)
конструкция
select FIELD1, FIELD2 from TABLE where FIELD3 int (?)
не пройдет.
и приходится при каждом вызове перестраивать запрос:
strSQL = 'select FIELD1, FIELD2 from TABLE where FIELD3 in (' + varlist + ')';
exec SQL prepare stmtSQL from :strSQL;
exec SQL declare curSQL cursor for stmtSQL;
exec SQL open curSQL;
exec SQL fetch next from curSQL into :fld1, :fld2
exec SQL close curSQL;
Именно это и вызвало возражения со стороны сопровождения — вызов prepare/declare при каждом вызове программы. Причем, той, которая вызывается очень часто (она входит в комплекс проверок клиента)
Т.е. дело не в таблицах, а в невозможности правильного создания динамического параметризированного запроса.
А «нативный» RPG имеет набор функций поиска-чтения-записи-изменения-удаления. В данном случае он работает безо всяких подготовок, просто с разбега фигачит по индексу, а дальше только разгребай результаты.
Хотя в общем случае считается так — если нужно найти одну запись в таблице по индексированным полям, то быстрее использовать нативный chain (чтение записи по значению ключа), а если множественная выборка — использовать SQL. Но бывают и исключения…
У TIMI еще один плюс — в исполняемом коде хранится также и набор MI инструкций и метка для какой платформы из этих инструкций собран исполняемый код. Меняем платформу (скажем с 824 на 924), переносим исполняемый модуль. при первом запуске система сравнивает на какой платформе оно запущено и под какую собрано. если не совпадает, то происходит автоматическая пересборка MI инструкций под текущую платформу.
Т.е. исполняемый код при переносе будет оптимизирован под новую платформу. Это и есть TI.
Тут вопрос не сколько ушло, а сколько не пришло. Хотя, если банк имеет преференции в виде принудительного загона всех бюджетников на зарплатные проекты, то им, конечно, можно особо не дергаться.
Или когда через него в обязательном порядке всякие госзаказы идут.
А если банк работает сам и сам зарабатывает (ну насколько это возможно в наших условиях), то ему приходится заботиться о том, чтобы все работало.
Ну вот из последнего…
От нагрузочного тестирования прилетело
Из PEX статистики видно, что 33% времени и 36% ресурсов CPU тратится на выполнение QSQRPARS, т.е. парсинг статических выражений при подготовке SQL запроса,
Сократить данные русурсозатраты практически до нуля можно путем описания параметров sql запросов через SQL Descriptor Area (SQLDA).
Поскольку это один из наиболее активно используемых сервис модулей, необоснованное повышенное ресурсопотребление является малодопустимым. Просьба инициировать доработку.
А дальше началось…
необходимо, что-то решать в срочном порядке, т.к. до выявления данного дефекта, информация была о том, что доработка будет установлена на бой до нового года. В свою очередь для бизнеса мы стали отдавать требования по продуктам с учетом данной доработки и многие уже перешли, это может вылиться в негатив на стороне клиентов и бизнеса (прецеденты уже есть).
срочность и критичность доработки обозначена подрядчику?? на недели только планы… прогнозно можно понять сроки самого устранения дефектов??
Ну и так далее… «Подрядчик» — ваш покорный слуга. По факту внутри два SQL запроса. Оба содержат WHERE VAR IN (<список значений>). Такие запросы внутри RPG (точнее, SQLRPG — тот же RPG, но с возможностью внутри использовать SQL напрямую) программ не параметризируются (в отличии от запросов типа WHERE VAR =? которые можно один раз подготовить (prepare), а потом многократно открывать OPEN… USING :var с разными параметрами.
В результате все переписано на «нативный» RPG c использованием функций чтения по логическому файлу (индексу).
Сложность только в том, что один запрос простой, просто с JOIN, а второй содержит GROUP BY… HAVING COUNT(*) = MAX(CNT) — вот тут пришлось еще индексированный список (сортированный список с поиском по ключу) подтянуть. Но в результате на тестовом юните старая версия работала 21мсек, новая — 7мсек. Сейчас ждем результаты нагрузочного теста — что они скажут, пропустят или нет…
Это лишь один пример, из последнего. И он сильно не единичный, такое сплошь и рядом у нас. После очередного (ежегодного) аудита на производительность составляется длинный список задач на оптимизацию с замечаниями что не устраивает.
Да, одна из особенностей AS-ки — есть граница, ниже которой нет доступа никому, кроме собственно разработчиков самой ОС. Вообще. Совсем нет. Никак.
Я не настолько силен в глубинных потрохах AS-ки, но, как понимаю, все компиляторы со всех поддерживаемых в ILE языков (а они все встроены в саму ОС) генерируют модули (аналог объектных файлов), которые и есть набор MI инструкций. Т.е. на этапе модуля уже неважно на каком языке он был написан.
А дальше эти модули уже собираются в единый исполныямый объект (PGM).
Собственно, если у нас программа из одного модуля (один исходник), то можем сразу вызвать CRTBNDPGM и получить на выходе PGM объект одной командой.
А если исходников несколько, то сначала для каждого CRTMOD — создаем модуль, а потом CRTPGM из этих модулей (на этом же этапе происходит разрешение внешних ссылок на функции, располложенные в сервисных программах — SRVPGM — некий аналог виндовых DLL)
у нас основной язык RPG, но многие вещи пишутся на С/С++. Там, где это проще и быстрее. И часто бывает что или программа лепится из нескольких модулей — основной на RPG, вспомогательные функции (ну, скажем, парсинг JSON строк) — на С/С++.
Или на С/С++ пишутся сервисные программы (работа сописками, наборами и т.п. к примеру) функции из которых потом используются в RPG программах.
Там все еще веселее — можно использовать функции из C RTL просто правильно объявив прототип для вызова из RPG — оно само их находит.
Так испоользуем memcmp, к примеру, qsort/bsearch, функции для работы с файлами на IFS (интегрированная файловая система — альтернативный доступ к файлам на AS/400).
В общем, тут много всякой специфики, это свой мир и достаточно занимательный.
Когда менял работу, тоже особо не задумывался. Но сложилось удачно — попал в самы глубокий бэк, команда ядровых функций АБС. Что идет от вебсервисов из мобильного клиента, интернет банка, из Пеги, попадает к нам. Ну и то, что работает в фоне — обработчики очереди сообщений, журнальные мониторы, репликаторы, регулярные отчеты…
Есть немного интерфейсов, но онииспецифические — текстовые. Т.н. «опции» для интерактивного ввода и редактирования различных таблиц.
Ставится IBM Installation Manager, а потом уже через него много чего можно поставить. WAS для запуска вебсервисов, которые будут работать с аской, RAD — тоже на базе эклипса, но заточена под разработку вебсервисов.
Access i Client Solution — пакет где эмулятор терминала, интерактивный sql, средство для просмотра спулов (вывод на принтер), средство для работы и файлами бд…
Там очень много софта, позволяющего работать с сервером. А сам сервер… Такая дура размером с большой холодильник. Ниимонитора ни клавиатуры там нет.
Саодостаточность в том, что в нее все необходимое уже интегрировано на уровне самой системы. Т.е. таблица БД это не просто файл, но системный объект.
Там сразу будет и БД и компиляторы и отладчик. И да, есть SEU — редактор для написания кода. Есть интерактивный SQL.
Но это сервер. У него нет монитора, нет клавиатуры. Общение с ним идет черех удаленный терминал IBM5250 или его программный эмулятор. Там все хардкорно — текстовые менюшки и команды языка CL.
Понятно, что через RDi работать удобнее. Хотя можно пользоваться любой средой, есть любители Visual Code, кто-то вообще в Notepad++ пишет. Без разницы, если настроишь гредл.
Есть проект, в нем исходники плюс скрипты под гредл плагин. Написал, потом gradle as400syncdeploy и оно само сгенерируе программу инсталлятор, забросит все на сервер, скомпилирует и запустит там инсталлятор.
Просто в RDi это делается нажатием кнопки, а к другой среде или прикручивать в инструменты или просто запустить из командной строки.
Это нормальная кроссплатформенная разработка. Это свой мир со своими порядками.
System/3 — System/36 — System/38 — AS/400 (Advansed System/400).
Абсолютно ничего общего с win и *nix Это принципиально иная школа осостроения. Одни TIMI (Technology Independant Machine Instructions) чего стоит. Там принципиальнотнет ассемблера. Есть эти самые MI. Каждая из которых работает с объектом (все есть объект). Вся система состоит из объектов. У каждого объекта имя и тип. Файлов как таковых тожеинет — объекты.
Программный объект кроме исполняемого кода содержит еще timi код. И при каждом запуске проверяется — собран ли этот программный побъект под данную архитектуру или нет. Если нет, то исполняемый код автоматически перегенерируется из timi.
Вот заменили у нас сервер с powers 824 на powers 924 (тестовый сервер для разработки). После этого все pgm при первом запуске на новом проце автоматически перегенерировались под него. С оптимизацией под данный конкретный проц.
Де модели памяти — одноуровневая singlelevel и terraspace (ее не разрешают нам). В одноуровневой сквозная адресация памяти и диска. Вот есть такой объект userspace — что-то типа двоичного файла в других ос. На него можно получить указатель и работать с ним как с областью памяти.
Размер указателей, кстати, 16 байт :-)
Концепция групп активации тоже не сразу заходит в голову. Это нечто типа изолированного контейнра памяти. В рамках одной задачи их может быть несколько. Когда запускается новая программа (а она может запускаться из другой программы в рамках одной задачи), она может создавать свою группу активации или запускаться в той, которой работает запускающая программа.
Глобальные и статические переменные будут сохранять свое значение пока группа активации не закроется. Т.е. можно в рамках одной задачи запускать одну программу несколько раз и глобалы и статики будут сохранять свои значения с прошлого запуска…
Если кому интересно — книга одного из отцов-основателей: Френк Солтис «Основы AS/400». От истории создания до внутреннего устройства весьма доходчивым языком.
От ILE (Integrated Language Environment) просто в восторге. Поскольку до этого без малого 30 на С/С++, то возможность часть задачи, там где это удобнее, писать на С, а часть на RPG очень радует. В целом работа с таблицами, строками, экранными и принтерными формами на RPG удобнее, а вот всякие системные вещи типа организации распараллеливания с межпроцессным взаимодействием через сокеты, динамические массивы и списки — это удобнее на С/С++.
Пишешь кусок кода на RPG, кусок на C/C++. Потом каждый кусок компилится в отдельный модуль (аналог объектного файла) и потом уже все модули собираются в единый программный объект.
На бэке — сопровождение «поставку не согласуем т.к. фукнция xxx жрет 30% процссорных ресурсов выделенных заданию что недопустимо».
Или как недавно было — все тесты прошли, включая нагрузку, а на проме упало. По джоблогам и дампам примерно понятна проблема, но воспроизвести не удается никак и нигде. Соответственно, подозрительный участок кода переписан, но внедряться страшно т.к. проверить что помогло не получается.
А модуль работает в таком участке, где лажать нельзя — прокол может обернуться штрафом со многими нулями от регулятора (в лучшем случае).
Пишем в основном на RPG, но бывает и на С, если это удобнее. Достаточно много сервисных программ написано на С/С++.
Средства разработки — RDi (IDE на базе эклипса, заточенная на работу с ас-кой. Т.е. пишешь на обычном компе, потом оно все забрасывает на сервер и там собирает. Для пущего удобства приручен gradle — пишутся сборочные скрипты, которые обрабатываются плагином.
Обычный путь — сначала гредлом с компа ставим в девелоперский юнит (проверка что все собирается), оттуда ручным запуском сгенерированного гредлом же инсталлятора в эмуляторе терминала ставим в песочнтцу (копия юнита компонентного тестирования). Если там работает, то коммитися в битбакет, оттуда подхватывает дженкинс и кладет поставку в артифактори. А оттуда уже аналитики девопсом ставят куда им надо для тестов.
В целом как везде — есть типовые задачи, а есть такие, над которыми думать надо.
Хотя интерфейсы разрабатывать приходилось, и получалось неплохо в плане функциональности и удобства. Но не увлекало. Так что в сторону бэка сместился уже очень давно.
Хотя к вебу никакого отношения не имею и не имел. Долгое время занимался разработкой системы мониторинга инженерного оборудования зданий. Система была гетерогенной, распределенной, построенной на микроядре. Т.е. в центре некое ядро, к нему цепляются и через него общаются удаленные микроконтроллеры (IP-шлюзы, за которыми уже на 485-м интерфейсе висели контроллеры нижнего уровня) и удаленные же интерфейсные клиенты (различной тематической направленности). Там занимался тем самым микроядром — архитектура, протоколы обмена и все прочее.
Сейчас банк. Ядровые функции автоматизированной банковской системы. То, что работает на мейнфрейме IBM i. В общем случае все, что работает на фронтах, общается с АБС через вебсервисы. Которые вызывают некие универсальные процедуры на мейнфрейме, а те уже дергают различные обработчики — вот их и пилим.
И никаких стонов, не стоит на свой счет принимать. Просто давно живу, много видел. В том числе и вундеркиндов, блиставших на олимпиадах, но неспособных в срок и без проблем довести до прома несложную задачу.
А чтобы стать действительно хорошим рабаботчиком на бэке, забдте про все фреймоворки — из тут нет и учите потроха AS/400 — что там и как работает. Ибо одним языком тут не обойдешься, придется вникать в системные API.
Ну и примите во внимание, что на сервере кроме вашей еще тысячи задач крутятся и все они между собой взаимодействуют так ли иначе. Один журнальный монитор чего стоит…
Ну и внешние сервисы, да… Скажем, получили некоторую строку, закинули ее в таблицу, она оттуда автоматом через очередь улетает на парсинг в некйи внешний сервис, оттуда опять через очередь прилетает обратно вам… И не дай бог в этом внешнем сервисе что-то поменялось… Вдруг. Разгребать придется опять вам.
Так что везде свои тараканы. Эт только со стороны кажется что все тихо и спокойно.
Да ну?
Не, ну наверное, если весь бэк состоит из одного вашего кода, то оно да…
Но ведь бывает и так, что бэк — это сотни а то и тысячи задач, крутящихся на одном мейнфрейме и так или иначе между собой взаимодействующих. И ваша задача может прекрасно работать до тех пор, пока то-то не скатил свой патч, который сломался сам и сломал вашу и еще десяток других задач.
Чтобы этого избежать существует т.н. «интеграционное тестирование» (помимо компонентного, нагрузочного и бизнес).
Золотые слова.
Вообще, что-то менять без крайней на то необходимости, да еще использовать при этом какие-то новомодные фичи, которые будут работать только на самых последних версиях клиентских девайсов — классическая чешижопица от нечего делать.
Править только дефекты, допиливать только те фичи, которые реально необходимы.
А по поводу новых фреймворков — в одном месте, где работал, было правило — все новые разработки тестируются на откровенно слабом и старом железе. И если там оно работает и не тормозит, тогда уже переход к тесту на железе реальном. Очень дисциплинировало в плане написания нормального кода. Не борьбы за наносекунды, но просто вдумчивого подхода к тому что делаешь и как делаешь.
Ну на бэке свои проблемы, поверьте :-) И их хватает.
Ну и никто не заставляет использовать именно SQL. Повторюсь — есть «нативные» функции для доступа к данным. Там вообще хитрая история (описана одним из «отцов-основателей» AS-ки Френком Солтисом в книга «Основы AS/400» — книга сама по себе занимательная т.к. там от истории эволюции System/3 — System/36 -System/38 — AS/400 до описания ее внутреннего мира, что как устроено и как работает) — было две команды, работающие над DB2/400. Одна разрабатывала концепцию DDS (Data Defenition Specifications) — язык описания данных, позволяющий достаточно единообразно описывать физические файлы (хранилища), логические файлы (способы доступа к хранилищам), дисплейные файлы (экранные формы) и принтерные файлы (формы для вывода на печать). Эта команда разработала т.н. «нативный» интерфейс позволяющий получать доступ к данным посредством функций типа read write update delete (в RPG несколько усеченный вариант, полный вариант содержится в RECIO — специфическое расширение C RTL для AS/400 — www.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rtref/recioh.htm)
Вторая команда работала над SQL/DB2.
И о чем-то они не договорились. В результате сделали и то и это.
В каких-то случаях удобнее пользоваться SQL, в каких-то нативом, хотя там чтобы написать простенький запрос с парой join'ов, конечно, приходится написать прилично кода.
В ряде случаев натив оказывается быстрее и менее ресурсоемким, в каких-то наоборот.
В том случае, что описываю я, старое решение (на SQL, не прошедшее нагрузку) отрабатывало на тестовом юните за 21мс. А написанное мной нативное решение (хотя и более длинное по коду) там же и на тех же данных — 7мс.
Сейчас вот ждем что нагрузочники скажут. Сам не имею доступа к PEX (инструмент, позволяющий анализировать загрузку процессора).
Фактически группа активации есть некий контейнер внутри задачи, в котором сохраняются все статические и глобальные переменные. Т.е. при первом вызове программы они проинициализаируются, а при втором (если группа активации не была принудительно закртыта) они будут иметь те значения, что остались от предыдущего вызова.
Посему, у каждой программы всегда есть кусок кода, который вызывается только один раз, при создании группы активации. Туда вынгосятся (по возможности) всякие «тяжелые» процедуры, которые можно не делать каждый раз. Если рассмотреть наш вариант с SQL запросом
select FIELD1, FIELD2 from TABLE where FIELD3 = var
где var — переменный параметр, определяемый при каждом запуске.
В SQLRPG это делается так:
strSQL = 'select FIELD1, FIELD2 from TABLE where FIELD3 =?';
exec SQL prepare stmtSQL from :strSQL;
exec SQL declare curSQL cursor for stmtSQL;
exec SQL open curSQL using :var;
exec SQL fetch next from curSQL into :fld1, :fld2
exec SQL close curSQL;
тут var — переменная, содержащая параметр для поиска, fld1 и fld2 — переменные в которые получаем результат из очередной строки выборки.
Так вот. В данном случае exec SQL prepare и exec SQL declare выносятся в тот блок кода, который вызывается один раз при создании группы активации. А в той части кода, что вызывается при каждом запуске программы остается только open с текущим значением параметра, fetch и close.
Но. Беда в том, что с массивом переменных это не работает. Т.е. в случае с
select FIELD1, FIELD2 from TABLE where FIELD3 int ('val1', 'val2', 'val3',...)
конструкция
select FIELD1, FIELD2 from TABLE where FIELD3 int (?)
не пройдет.
и приходится при каждом вызове перестраивать запрос:
strSQL = 'select FIELD1, FIELD2 from TABLE where FIELD3 in (' + varlist + ')';
exec SQL prepare stmtSQL from :strSQL;
exec SQL declare curSQL cursor for stmtSQL;
exec SQL open curSQL;
exec SQL fetch next from curSQL into :fld1, :fld2
exec SQL close curSQL;
Именно это и вызвало возражения со стороны сопровождения — вызов prepare/declare при каждом вызове программы. Причем, той, которая вызывается очень часто (она входит в комплекс проверок клиента)
Т.е. дело не в таблицах, а в невозможности правильного создания динамического параметризированного запроса.
А «нативный» RPG имеет набор функций поиска-чтения-записи-изменения-удаления. В данном случае он работает безо всяких подготовок, просто с разбега фигачит по индексу, а дальше только разгребай результаты.
Хотя в общем случае считается так — если нужно найти одну запись в таблице по индексированным полям, то быстрее использовать нативный chain (чтение записи по значению ключа), а если множественная выборка — использовать SQL. Но бывают и исключения…
Т.е. исполняемый код при переносе будет оптимизирован под новую платформу. Это и есть TI.
Или когда через него в обязательном порядке всякие госзаказы идут.
А если банк работает сам и сам зарабатывает (ну насколько это возможно в наших условиях), то ему приходится заботиться о том, чтобы все работало.
Ну вот из последнего…
От нагрузочного тестирования прилетело
А дальше началось…
Ну и так далее… «Подрядчик» — ваш покорный слуга. По факту внутри два SQL запроса. Оба содержат WHERE VAR IN (<список значений>). Такие запросы внутри RPG (точнее, SQLRPG — тот же RPG, но с возможностью внутри использовать SQL напрямую) программ не параметризируются (в отличии от запросов типа WHERE VAR =? которые можно один раз подготовить (prepare), а потом многократно открывать OPEN… USING :var с разными параметрами.
В результате все переписано на «нативный» RPG c использованием функций чтения по логическому файлу (индексу).
Сложность только в том, что один запрос простой, просто с JOIN, а второй содержит GROUP BY… HAVING COUNT(*) = MAX(CNT) — вот тут пришлось еще индексированный список (сортированный список с поиском по ключу) подтянуть. Но в результате на тестовом юните старая версия работала 21мсек, новая — 7мсек. Сейчас ждем результаты нагрузочного теста — что они скажут, пропустят или нет…
Это лишь один пример, из последнего. И он сильно не единичный, такое сплошь и рядом у нас. После очередного (ежегодного) аудита на производительность составляется длинный список задач на оптимизацию с замечаниями что не устраивает.
Я не настолько силен в глубинных потрохах AS-ки, но, как понимаю, все компиляторы со всех поддерживаемых в ILE языков (а они все встроены в саму ОС) генерируют модули (аналог объектных файлов), которые и есть набор MI инструкций. Т.е. на этапе модуля уже неважно на каком языке он был написан.
А дальше эти модули уже собираются в единый исполныямый объект (PGM).
Собственно, если у нас программа из одного модуля (один исходник), то можем сразу вызвать CRTBNDPGM и получить на выходе PGM объект одной командой.
А если исходников несколько, то сначала для каждого CRTMOD — создаем модуль, а потом CRTPGM из этих модулей (на этом же этапе происходит разрешение внешних ссылок на функции, располложенные в сервисных программах — SRVPGM — некий аналог виндовых DLL)
у нас основной язык RPG, но многие вещи пишутся на С/С++. Там, где это проще и быстрее. И часто бывает что или программа лепится из нескольких модулей — основной на RPG, вспомогательные функции (ну, скажем, парсинг JSON строк) — на С/С++.
Или на С/С++ пишутся сервисные программы (работа сописками, наборами и т.п. к примеру) функции из которых потом используются в RPG программах.
Там все еще веселее — можно использовать функции из C RTL просто правильно объявив прототип для вызова из RPG — оно само их находит.
Так испоользуем memcmp, к примеру, qsort/bsearch, функции для работы с файлами на IFS (интегрированная файловая система — альтернативный доступ к файлам на AS/400).
В общем, тут много всякой специфики, это свой мир и достаточно занимательный.