Попал в подкаст «За чашкой чая», эпизод 16. Можно слушать на любых доступных платформах: Anchor, Apple, Yandex.
Под катом текстовая расшифровка моей части. Хотите — слушайте, хотите — читайте.
против Энтропии
Эффект старой плёнки.
Есть в SAP HCM стандартная транзакция PAR1 (Гибкие данные сотрудников). Но при внедрении у пользователей были хотелки, которые в стандарте невозможны. Скопировали транзакцию в Z-область. Допилили её так, как хотелось пользователям. Добавили инфотипы, ещё кое-что по мелочи. Дело нехитрое.
Затемнение.
Прошло десять лет.
В обычной жизни мы не паримся с памятью в ABAP, сборщика мусора как бы нет, глобальной памяти нет.
В самом простом случае у нас есть то, что называется транзакцией (не путать с транзакцией базы данных), что можно определить как «приложение» на нашем собственно говоря сервере приложений. Транзакция всегда ассоциирована с определённой именованной программой.
И вот пока приложение работает, оно потребляет память в виде переменных, экземпляров классов, массивов (также известных как «внутренние таблицы»). И если посмотреть на стек вызовов, то он упирается во входную точку начала этой программы. Ничего ABAPовского за пределами этой точки не существует. Одно приложение не может получить доступ к данным другого приложения, только через какие-то обходные манёвры, например: через постоянное хранилище (СУБД), через взаимные блокировки и прочие костыли. Как только приложение завершает работу, вся его память высвобождается. А приложения долго не работают, потому что они уже в некоторой степени атомарны (то есть как бы уже не монолит, но ещё не микросервисы).
Есть уже случае чуть посложнее, например: есть веб-сервис, слушающий входящие соединения, или одно «приложение» вызывает другое «приложение» через SUBMIT REPORT . Но подход вы уже уловили, это и называется SAP LUW, то есть Logical Unit of Work. Получается, что стек вызовов и контекст существуют неразрывно только внутри него.
И как обычно бывает, изредка появляется необходимость перепрыгнуть заборы.
Один из известных костылей -оператор IMPORT/EXPORT MEMORY ID, который позволяет перебрасывать переменные (структуры, массивы) между приложениями, но только если они существуют в одной пользовательской сессии. Например Приложение-А вызывает Приложение-Б, Приложение-Б генерирует данные, закидывает их в ячейку памяти X, после чего завершает работу, управление возвращается в Приложение-А, которое считывает данные из ячейки памяти X, профит!
Разговор был бы не так интересен, если бы мы остановились на этом. Но перепрыгнув забор, мы обнаруживаем ещё один забор!
А если мы хотим перебросить данные между разными пользовательскими сессиями?
Для примера кейс будет таким: есть чистый слушающий HTTP-сервис, в котором мы хотим организовать что-то вроде сессии.
HTTP-сессии в ABAP могут быть как stateless, так и stateful. По сути их отличие в том, что этот пресловутый SAP LUW растягивается на всю пользовательскую сессию (stateful) или только на один http-вызов (stateless). В stateful-режиме сервис работает со своими причудами, например: обновлённый код сервиса не подхватывается на лету, чтобы изменения в сервисе вступили в силу нужно завершить сессию. Так же и в обычном ABAPe: пока запущена программа со всеми диалоговыми шагами обновлённый код не подхватывается — необходимо перезайти в транзакцию. Получается в рамках HTTP необходимо сделать log-off, что очень редко случается целенаправленно в жизненных реалиях, гораздо чаще сессия умирает по таймауту.
И вот кривая дорожка сомнительных архитектурных решений привела нас к небольшому челенджу — организовать хранение данных сессии при stateless-сессии HTTP.
Окей, вызов принят. Представим типичный сценарий:
Обыкновенным ABAPом красные места можно тупо реализовать через БД или другой как-мы говорим-у нас-в-деревне «персистент сторэдж», мы же это умеем. Но мы же дошли досюда не за простыми решениями.
Существует возможность создать экземпляр объекта и положить его в какую-то суперглобальную область памяти, существующую вечно внутри сервера приложений. Соответственно, в любой момент можно попробовать взять этот объект во временное пользование (на чтение или на изменение). И это будет тот-же-самый-объект со всеми его атрибутами. Он будет существовать до перезагрузки сервера или до его ручного уничтожения через кнопку в админке (SHMM). Вот вкратце история. А под катом немного подробностей.
Вот такой Бумеранг Добра:
Всем добра и любви.
Есть в мире доброхоты, которые лучше вас знают, в каких группах вам участвовать.
Если вас добавили в группу Телеграм, но вас это не обрадовало, то отметьте эту группу как спам, а после этого самостоятельно покиньте группу. Вполне вероятно, что этого хватит на неделю-другую, а затем вас снова добавят.
Если вам и этого будет мало, то в настройках безопасности есть настройка, кому можно вас добавлять в группы. По умолчанию там может стоять «Everybody»(Все, Кто угодно), но вы можете выставить значения «My contacts»(Мои контакты).
Шлю всем лучи здоровья.
Удивительно, но в течение нескольких минут после отправки такого сообщения в группу моё сообщение подчистили и меня заблокировали, видимо навсегда.
Интересно, можно ли как-то выяснить, кто добавляет меня в группу?
PS. Иногда, раз в год, бывает неудобно, но есть варианты. Вам могут прислать специальную ссылку, по которой вы можете вступить в группу самостоятельно.
PPS. Если группа совсем левая, то один из косвенных способов — посмотреть, кто из ваших не очень давних контактов является её членом. Если там только одна риэлторша Гульчехра, то есть шансы что это она. Просто она так любит благотворительность, что всех своих контактов пытается добавить в группы, где это промотируется.
PPPS. Отписался от многих каналов в телеге, через специальную прослойку я конвертирую Телеграм в RSS потоки (https://rsshub.app/) и добавляю их себе в Feedly. URL выглядит примерно так: https://rsshub.app/telegram/channel/<ChannelName>.
Таким образом, в телеге остались только те каналы, в которых мне требуется интерактив, их совсем немного. Так что читаю каналы только тогда, когда сам захочу. Пока работает.
PPPPS. Говорят, что «всем добра» это пассивная агрессия. Скорее всего, так оно и есть.
При разработке часто бывает необходимость вести некоторые настройки или конфигурации.
На первом уровне это хардкод внутри программы:
Хардкод вполне оправдан, если:
Вторым уровнем может возникнуть классическая настроечная таблица + сгенерированное ведение в транзакции SM30. Впрочем тут тоже можно сделать шаг вправо и влево:
Такие настройки уже вполне можно передать в руки настройщиков и пользователей.
Доходит иногда до смешного, даже в стандарте можно наблюдать целые настроечные таблицы в виде одной колонки («Активировать»), соответственно в таблице только одна строка со значеним — флажок стоит или нет.
Третьим уровнем могут часто возникать разные велосипеды для организации небольших настроек.
Часто можно заметить, что в системе есть много разных стандартных узкоспециализированных хранилищ, которые можно использовать в собственных корыстных целях, например:
Но некоторые приходят к мысли, что следует разработать некоторое универсальное хранилище настроек (Z-разработка), вроде большой таблицы в стиле ключ-значение или даже нечто похожего на реестр Windows, навскидку:
Добро пожаловать под кат, представляю вам свой велосипед, там есть пара занятных моментов. Почему свой? Да потому что у остальных есть фатальный недостаток, очевидно. (далее…)
Два момента, которые меня особенно зацепили. Профессиональная деформация?
Блокировка аккаунта на сутки после трёх неверных попыток входа
В чистом виде — очень сомнительное решение. Таким образом можно очень просто блокировать аккаунты неугодных людей (так называемая DoS-атака). В реальной жизни такое можно встретить только во внутренних корпоративных системах, потому что источник атаки легко локализуется в локальной сети.
Но в сети Интернет атакующего найти гораздо сложнее. Разумная практика — блокировать IP-адрес на сутки, но тогда по сюжету героиня могла бы подбирать пароли с десятков устройств плюс подключить VPN-сервисы. Но вот это было бы уже не так интересно для развития сюжета, потому что у человека не было бы Проблемы.
Восстановление пароля в открытом виде
В конце сюжетной линии героине присылают пароль от аккаунта в открытом виде.
Разумная практика — не хранить пароли в открытом виде, использовать только солёные хеши и всё такое. В реальной жизни администратор может сбросить пароль, то есть просто установить новый, но старый пароль никто узнать не может.
Но по сюжету героине прислали «значащий» пароль для владельца аккаунта, следовательно совершенно очевидно, что это реальный старый пароль.
Для завершения сюжетной линии — красивый штрих, но IRL такого не бывает.
Бонус — запись звука с мобильного телефона
Администратор социальной сети активирует закладку в мобильном приложении и запускает трансляцию звука с устройства преступника на терминал администратора.
А вот это уже более реальный сценарий. Очевидно, что приложение скорее всего запросило права на запись звука при установке, так как запись звука нужна для отправки звуковых сообщений — обычная практика в мессенджерах/социальных сетях.
Реально ли такие закладки встретить в реальной жизни — вопрос непростой. Об этом открыто никто никогда не расскажет, а болтовне анонимов в интернете верить нельзя. Разве что умные парни сделают реверс-инжиниринг и вскроют фичу доказательно.
Могу только утверждать следующее. Звук не записывается и не передаётся в сколько-нибудь значимых объёмах. Ограничивает авторов приложений и связывает руки примерно следующее:
Устав от простыней в стиле:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
LOOP AT SCREEN. IF screen-name = 'ZSS-PERID'. screen-input = 0. ENDIF. IF zss-pernr IS NOT INITIAL. IF screen-name = 'ZSS-PERNR' OR screen-name = 'ZSS-LNAME' OR screen-name = 'ZSS-FNAME'. screen-input = 0. ENDIF. ENDIF. IF screen-name = 'ZSS-STELL'. screen-input = 0. ENDIF. ... IF screen-name = 'ZSS-TAXNM'. IF ... . screen-required = 0. ELSE. screen-required = 1. ENDIF. ENDIF. MODIFY SCREEN. ENDLOOP. ... LOOP AT SCREEN. IF screen-group1 = 'M1'. IF ... screen-input = 0. ELSE. screen-input = 1. ENDIF. MODIFY SCREEN. ENDIF. ENDLOOP. ... |
я решил обкатать новый подход.
Первым делом декларативное определение, например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
mt_profile_readonly = VALUE #( ( mask = '*' action = zcl_screen=>readonly ) ). mt_profile_employee = VALUE #( ( mask = '*' action = zcl_screen=>readonly ) ( mask = '*QKTGR*' action = zcl_screen=>obligatory ) ( mask = '*POSID*' action = zcl_screen=>input ) ). mt_profile_contractor = VALUE #( ( mask = '*' action = zcl_screen=>readonly ) ( mask = '*QKTGR*' action = zcl_screen=>input ) ( mask = '*TAXNM*' action = zcl_screen=>obligatory ) ( mask = '*TDATE*' action = zcl_screen=>input ) ). mt_profile_visitor = VALUE #( ( mask = '*' action = zcl_screen=>input ) ( mask = '*PERID*' action = zcl_screen=>invisible ) ( mask = '*PERNR*' action = zcl_screen=>invisible ) ( mask = '*STELL*' action = zcl_screen=>invisible ) ). |
Применение на практике сводится к следующему:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
MODULE screen_3000 OUTPUT. go_person_controller->process_screen( ). ENDMODULE. ... METHOD process_screen. IF m_mode = mc_display. zcl_screen=>apply_profile( mt_profile_readonly ). ELSE. IF m_data-pernr IS NOT INITIAL. IF is_employee( EXPORTING iv_pernr = m_data-perid ). zcl_screen=>apply_profile( mt_profile_employee ). ELSE. zcl_screen=>apply_profile( mt_profile_contractor ). ENDIF. ELSE. zcl_screen=>apply_profile( mt_profile_visitor ). ENDIF. ENDIF. ENDMETHOD. |
Пробую пока покатать-проверить, насколько такой подход применим в условиях, приближенным к боевым. Нужно больше мест применения, чтобы определить какой подход даст лучшую читаемость, компактность и проверяемость. И потихотньку можно добавить мяса.
Раннее утро. Птички чирикают.
Сижу, никого не трогаю, читаю утреннюю газету.
SAP вещает:
сморите как всё было плохо, какое всё разное, инконсистенси, то да сё, фу-фу-фу
вот мы вам сделаем Fiori 3, там будет всё такое консистентное, прям ух-ты-красота
современные технологии, двадцать первый век, а о цене давайте не будем говорить
И неожиданно нахлынуло чувство какой-то органичной целостности этого мира.
(далее…)