Минутка просвещения

Читать в телеге. Когда-то там были посты не только от меня.

Таймеры на сервисных тасках в Camunda

В Camunda таймеры не работают на сервисных тасках. Т.е. такой таймер

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

Что делать?

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

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

Третий вариант — таймауты можно обрабатывать внутри логики делегата и кидать ошибку/эскалацию самостоятельно.

Наконец, можно отрефакторить схему так, чтобы таймер не был на сервисной таске. Однако банально выделить его в параллельную ветку с каким-нибудь условием не получится: параллельные ветки на схеме в движке выполняются на самом деле последовательно.

СсылкаКомментировать

OffsetDateTime и Hibernate

При работе с датами Hibernate стоит быть аккуратным: он все типы конвертирует в java.sql.Timestamp, который по смыслу идентичен java.time.Instant. Поэтому информация о временной зоне будет потеряна: при чтении из базы в какой-нибудь OffsetDateTime будет подставлена системная зона. Так что проще сразу маппить на Instant во избежание недоразумений. Матерые Java-чемпионы так и говорят, что OffsetDateTime и ZonedDateTime для JPA не очень полезны. И вообще, можно стрельнуть себе в ногу с ними.

Можно еще выбрать в самой базе тип “timestamp with time zone”, если он поддерживается (потому что в SQL стандарте его нет), но, во-первых, Hibernate пофигу на это отличие, а во-вторых, в этом типе на самом деле не хранится информация о самой зоне, см., например, документацию Postgresql:

All timezone-aware dates and times are stored internally in UTC. They are converted to local time in the zone specified by the TimeZone configuration parameter before being displayed to the client.
СсылкаКомментировать

DSL для роутинга

В Spring 5.2 можно писать в функциональном стиле не только для WebFlux, но и для обычного MVC. Т.е. вместо анноташек над контроллерами можно писать что-то вроде

fun routes() = router {
    accept(ALL).nest {
        GET("/hello/{name}") {
            val name = it.pathVariable("name")
            ok().body("Hello, $name!")
        }
    }
}

где лямбды принимают ServerRequest и возвращают ServerResponse. Такой подход можно использовать для генерации эндпоинтов из конфига.

Подобный подход очень напоминает Ktor:

    routing {
        get("/hello/{name}") {
            val name = call.parameters["name"]
            call.respondText("Hello $name!")
        }
    }
СсылкаКомментировать

Модели памяти языков программирования

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

В хардварной части в основном рассматриваются разные ситуации с гонками и последовательной согласованностью (Sequential consistency). Раскрывается мысль, что даже на уровне железа есть приколы с многопоточностью. Получается, что очень похожие проблем с параллелизмом случаются в разных контекстах и на разных уровнях абстракций: в процессоре, языке программирования, узлах распределенной БД, микросервисах. И методы решения похожи: куча очередей (как в x86), куча синхронизирующихся копий состояния (как в ARM). Разумеется, x86 и ARM дают разные гарантии консистентности. Под конец статьи рассказывается о модели синхронизации DRF (data-race-free). Вкратце суть такая: кроме чтения и записи, для памяти есть еще операция синхронизации. Сихнронизация служит “барьером”: операции чтения и записи можно перемешивать как угодно, но нельзя перемещать через барьер. И в 1990 доказали, что если писать программу без гонок (нормально синхронизировать чтение-запись между потоками), то тогда на железо, соответствующее DRF, будет выполнять с последовательной согласованностью, т.е. как будто она вся выполнялась в одном потоке. И это очень классный результат, потому что позволил абстрагироваться от кучи проблем, написанных в начале статьи, и дать хоть какой-то простор для оптимизаций компилятору.

В программной части больше интересного. Например, Java, оказывается, первый язык, в котором была попытка прописать поведение многопоточных программ на уровне спецификации (1996). С первого раза не получилось, но в 2004 в Java 1.5 добавили фич и она стала поддерживать модель DRF, причем в этом активно участвовала одна из авторов оригинальной статьи. Отношение happens-before как реализация DRF — важная часть модели памяти Java. Однако создатели в 2010 году признали, что у этой реализации есть баги, хоть она и является хорошим компромиссом между сложностью и надежностью.

В C++11 использовали модель Java как основу (sic!), пытались сделать проще, но в итоге только усложнили и она стала менее полезной для программистов. Ну и undefined behaviour добавили, куда ж без него, и стремные атомики, которые особо ничего не гарантируют. Однако в Си, Rust и Swift использовали модель C++11 (последние два — потому что LLVM и интеграция). JavaScript пошел своим путем: его модель памяти совместима с C++11, но ближе к Java (место для вашей шутки про название языка).

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

СсылкаКомментировать

Запуск графического приложения с админскими правами

Раньше для этого можно было вызвать команду с gksu, например gksu kse. Однако, gksu убрали из дистрибутивов, т.к. он не безопасен.

Альтернатива — использовать политики (для параноиков) или pkexec. Но есть проблемка: при подтверждении запуска виден только исполняемый файл, но не его аргументы. Т.е. окно авторизации для pkexec sh -c 'echo hello' и для pkexec sh -c 'rm -rf /' будет выглядеть одинаково. Решить это можно через политики, но писать душный xml — скучно, поэтому можно извратиться по-другому.

Можно отредактировать сообщение сервиса polkit, вставив туда шаблон $(command_line). Для этого:

  1. Берем файл с переводом для британского языка и распаковываем его msgunfmt /usr/share/locale-langpack/en_GB/LC_MESSAGES/polkit-1.mo -o polkit-1.po
  2. Редактируем polkit-1.po любым текстовым редактором, добавляя в msgstr Authentication is needed to run '$(program)' ('$(command_line)'), msgid не трогаем.
  3. Пакуем отредактированный файл обратно, в директорию со своей локалью (у меня это en_US) msgfmt polkit-1.po -o /usr/share/locale/en_US/LC_MESSAGES/polkit-1.mo
  4. Рестартуем сервис polkit, чтобы изменения применились: systemctl restart polkit

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

СсылкаКомментировать

Неявная конкатенация строк в Python

В питоне есть баг фича, что строковые литералы, написаные рядом, автоматически соединяются в один:

>>> print("ololo" "alala")
ololoalala

>>> ["1" "2", "3"]
['12', '3']

Иногда это может очень бесить (и не только меня, но PEP с предложением выпилить это наследие Си отклонили). Теоретически, помочь могут линтеры: pylint или flake8. А тут можно посмотреть примеры, почему такое поведение может быть полезным.

СсылкаКомментировать

Тестирование распределенных систем

В продолжение постов про Jepsen и чек-листы по архитектуре микросервисовресурс, на котором собрана полезная информация про тестирование распределенных систем (в т.ч. всеми любимых микросервисов).

СсылкаКомментировать

Уровни языков программирования

Был в Академгородке на выставке “Наука Сибири”, там попалась книжка 2015 года про парадигмы программирования. В оглавлении встретились “языки низкого уровня”, “языки высокого уровня” и … “языки сверхвысокого уровня”. “О таком я еще не слышал, — подумал я, — наверно, это про какой-нибудь Haskell или Prolog, которые сильно абстрагированы от железа. Или про DSL?”

Но оказалось, что сибирские ученые таким языком считают Sisal — еще один достаточно специфичный язык для параллельных вычислений, который позволяет не заботится о синхронизации и разбивке на отдельные процессоры/компьютеры. Он является “функциональным потоковым языком, ориентированным на написание больших научных программ”, “одним из самых известных потоковых языков промышленного уровня”, “позиционируется как замена языка Fortran для научных применений” и позволяет “предоставить широкому кругу прикладных программистов, не имеющих достаточного доступа к параллельным вычислительным системам, но являющихся специалистами в своих прикладных областях, возможность быстрой разработки высококачественных переносимых параллельных алгоритмов на своем рабочем месте”. Создан язык в 1983 году, вторая версия вышла в 1991, а в ИСИ СО РАН развивают этот язык после третьей версии, уже даже cloud-версия есть. По синтаксису напоминает Pascal.

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

Любопытно, что Википедия языками сверхвысокого уровня считает Python и Visual Basic. Правда, это термин из 90х, когда Си был языком высокого уровня (а сегодняшний программист скорее выделит для него “средний” уровень). А еще можно почитать про поколения языков программирования, где первое поколение — машинные коды, третье — С++, Java и т.п., а пример четвертого — 1C. Такие дела :)

СсылкаКомментировать

Альтернативы классическому менторству

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

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

СсылкаКомментировать