вторник, 1 января 2030 г.

О блоге

Более двадцати лет я занимался разработкой ПО, в основном как программист и тим-лид, а в 2012-2014гг как руководитель департамента разработки и внедрения ПО в компании Интервэйл (подробнее на LinkedIn). В настоящее время занимаюсь развитием компании по разработке ПО stiffstream, в которой являюсь одним из соучредителей. Поэтому в моем блоге много заметок о работе, в частности о программировании и компьютерах, а так же об управлении.

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

понедельник, 31 декабря 2029 г.

[life.photo] Характерный портрет: вы и ваш мир моими глазами. Безвозмездно :)

Вы художник? Бармен или музыкант? Или, может быть, коллекционер? Плотник или столяр? Кузнец или слесарь? Владеете маленьким магазинчиком или управляете большим производством? Реставрируете старинные часы или просто починяете примус? Всю жизнь занимаетесь своим любимым делом и хотели бы иметь фото на память?

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

вторник, 27 июля 2021 г.

[prog.c++] Первые впечатления от C++ных короутин и зачем мне все это нужно?

Я тут с прошлой недели пытаюсь въехать в тему короутин, которые завезли в C++20. Пока еще не вкурил. В голове все еще каша, но хотя бы местами начинают формироваться отдельные лоскуты, которые я надеюсь в скором будущем сшить в общую картину. Поскольку процесс этот на удивление тяжелый и долгий, то не грех поделиться какими-то предварительными впечатлениями.

В C++20 нет короутин

Понимаю, что на этот счет есть разные мнения, но лично мне после знакомства с тем, что включили в C++20, проще считать, что в C++20 короутин нет.

Есть механизм поддержки короутин со стороны языка.

А вот самих короутин нет.

ИМХО, это большая разница.

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

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

Изучение механизма поддержки короутин в C++20 -- это та еще головоломка

Мягко говоря, это просто какой-то трындец.

Я вкуриваю эти самые короутины уже пять дней. И все еще никак.

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

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

Проблема в моем случае в том, что моих мозгов на осознание всей гениальности замысла не хватает. Что расстраивает.

По большому счету детали механизма поддержки короутин в C++20 мало кому будут нужны

Расчет комитета по стандартизации C++ был в том, чтобы добавить в язык сам механизм. Чтобы те или иные реализации подтянулись со временем.

Как я понимаю, в Asio-1.19 эта реализация уже есть.

Вероятно, со временем и другие реализации для других прикладных ниш подтянутся.

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

Смущает, правда, наличие "закона текущих абстракций", согласно которому понимание того, что же происходит "под капотом" все же желательно. Но насколько все это будет актуально покажет только время. Будем посмотреть.

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

Зачем мне это все?

Во-первых, это все равно будущее программирования на C++. Не удивлюсь, если через всего пять лет использование короутин будет обыденной и повседневной рутиной. Так что нужно подготовиться к грядущим изменениям.

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

Короутины в SObjectizer

Пока не видно, чтобы короутины нужны были где-то в потрохах SObjectizer-а.

Но вот где короутины явно могут помочь, так это в упрощении синхронного взаимодействия между агентами.

Сейчас если агент A хочет синхронно обратиться к агенту B (т.е. в обработчике событий E отослать сообщение агенту B и тут же, прямо внутри E дождаться ответа), то программист должен гарантировать что A и B работают на разных контекстах.

Тогда как в случае с короутинами можно сделать так, чтобы обработчик событий E сам был короутиной. Вызов операции ожидания ответа от B реализовывался бы через co_await. Например, что-то вроде:

so_5::awaitable_t A::evt_E(mhood_t<some_msg>) {
  // Создаем message chain для ответного сообщения.
  so_5::mchain_t reply_chain = so_5::create_mchain(so_environment());
  // Отсылаем сообщение агенту B и передаем в нем reply_chain, чтобы
  // агент B знал, куда отсылать ответ.
  so_5::send<some_request>(B_mbox, ..., reply_chain, ...);
  ...
  // Здесь мы хотим получить ответ.
  co_await so_5::receive(from(reply_chain).handle_n(1), ...);
}

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

А это позволяет запросто привязать агентов A и B к общему рабочему контексту. При этом они смогут "синхронно" общаться друг с другом.

Что очень круто, т.к. раньше такое в принципе было невозможно.

Короутины в RESTinio

А вот в RESTinio для применения короутин открывается гораздо больший простор. ИМХО.

Короутины в реализации самого RESTinio

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

Короутины в реализации request_handler-ов

Очевидное применение короутин для упрощения жизни пользователям RESTinio -- это представление request_handler-ов в виде короутин.

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

В случае же, если request_handler будет представлен в виде короутины, то внутри request_handler-а можно инициировать асинхронные операции посредством co_await. Что, потенциально, сделает написание асинхронных обработчиков входящих запросов гораздо более простым занятием.

Поддержка исходящих соединений в RESTinio?

На данный момент RESTinio позволяет работать только с входящими HTTP-запросами, а исходящие запросы не поддерживаются в принципе. Т.е. RESTinio не может сейчас использоваться в качестве HTTP-клиента.

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

А вот применение короутин, потенциально, может этот момент прояснить. Например, что-то вроде:

auto reply = co_await client.http_post(restinio::async, "https://google.com", ...);

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

Я не хочу сказать, что наличие короутин делает добавление поддержки исходящих HTTP-запросов в RESTinio простым и понятным делом. Но внушает надежду, что такая поддержка может быть сделана красиво и удобно для пользователя.

Вместо заключения

В ближайший день-два я попробую написать пост, в котором расскажу, что именно затрудняет для меня разбирательство с механизмом короутин в C++20. Это нужно лично мне, т.к. надеюсь, что в процессе написания текста информация в мозгах структурируется чуть лучше. Но может кому-то это так же будет интересно.


Возможно, кто-то использует RESTinio в своих проектах и планирует со временем переходить на C++20. Если так, то вы можете быть заинтересованы в том, чтобы в RESTinio появилась поддержка короутин. Ускорить этот процесс можно за счет финансирования наших работ по RESTinio. Например, в виде договора между вашей компанией и СтифСтримом.

Тоже самое касается и SObjectizer-а.

вторник, 20 июля 2021 г.

[prog.thoughts] Мое текущее отношение к поддержке исключений в C++

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

Попробую рассказать о том, как я сам сейчас отношусь к существующей в C++ поддержке исключений.

Ключевое

Хорошо, что исключения в C++ есть. Как уже говорилось в предыдущем посте, лично я плохо себе представляю как писать надежный код без использования исключений при программировании в стиле "современного C++".

Плохо то, что текущая реализация исключений:

понедельник, 19 июля 2021 г.

[prog.experience] Несколько слов о работе с исключениями в arataga

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

Особенности arataga и их влияние на работу с исключениями

Во-первых, изначально проект arataga разрабатывался в сжатые сроки и требовалось как можно быстрее получить работающий прототип чтобы оценить перспективность всей затеи. Т.е. ключевым фактором являлся "time to market". Писать на C++ в условиях горящих сроков -- то еще занятие. Поэтому для нас важно было использовать практики, которые бы обеспечивали нам и достаточно высокую скорость разработки кода, и достаточную степень его надежности и корректности.

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

В-третьих, архитектура arataga подразумевала, что будет всего один процесс, который будет обслуживать все подключения. Откуда следует то, что принцип fail-fast в смысле "если возникла непредвиденная неприятность, то просто грохаем весь процесс" здесь был бы не очень уместен. Подключений десятки тысяч, с каким-то из них точно что-то пойдет не так. Ронять все приложение вместе со всеми остальными подключениями не есть хорошо. Как следствие, нам пришлось разбираться с тем, чтобы неожиданные исключения, с которыми мы не знаем что делать, не всегда вели к краху всего приложения, а в каких-то местах просто игнорировались бы.

В-четвертых, хотя arataga и должен был работать в режиме 24/7, но жестких требований к тому, чтобы arataga не падал в любых условиях, у нас не было. Эпизодические падения в непредвиденных ситуациях, скажем, раз в неделю, никого бы не напрягали. Поэтому мы могли не заморачиваться на тотальный контроль исключений и в каких-то местах могли позволить себе смотреть на ситуацию так: "ну, если уж здесь что-то вылетит, то пусть уж лучше все просто навернется и рестартует".

В-пятых, в arataga активно используются сторонние библиотеки, которые либо не очень приветствуют выброс исключений в callback-ах (как Asio, например), либо же вообще этого не приемлют (как, например, чисто C-шная http-parser, которая про C++ные исключения ни сном, ни духом). Но callback-и в эти сторонние библиотеки передаются C++ные, в которых активно используется C++ный код, бросающий исключения. И это нужно было как-то брать под контроль.

пятница, 2 июля 2021 г.

[prog.experience] Маленькая история про улучшение навигации по логам в arataga

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

Если кто-то не в курсе, то arataga -- это прототип производительного прокси-сервера, который мы делали для одного клиента, пока этот самый клиент не потерял интерес к данной разработке.

Пару месяцев назад представилась возможность погонять arataga под реальной нагрузкой. Чем мы с удовольствием воспользовались. Выявили несколько косяков, кое что поменяли. Но, в общем, в очередной раз убедились, что arataga работает.

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

Грубо говоря, ищешь в логе ситуацию, помеченную как WARN или ERR, а затем смотришь, что предшествовало и/или следовало за этой ситуацией.

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

Изначально казалось, что каждая строка лога содержит достаточное количество информации для идентификации конкретного подключения. Вот, например:

[2021-04-16 08:43:02.756] [arataga] [error] socks-3001-192.168.1.178-io_thr_1-v1: connection (3001,38) => socks5: PDU with auth methods too long, methods: 1, bytes read: 22
[2021-04-16 08:43:02.756] [arataga] [debug] socks-3001-192.168.1.178-io_thr_1-v1: connection (3001,38) removed (protocol_error), connections: 0/200

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

  • socks-3001-192.168.1.178-io_thr_1-v1 -- это имя агента, который обслуживает конкретную точку входа. Имя составное и сообщает максимум информации об этой точке входа и об агенте: socks -- говорит о точке входа по протоколу SOCKS, точка входа открыта на порту 3001 адреса 192.168.1.178, точка входа привязана к io_thread с порядковым номером 1. Ну и это первая версия данного агента (если агент пересоздается в результате изменения конфигурации, то номер его версии будет увеличиваться);
  • (3001,38) -- это идентификатор соединения внутри конкретной точки входа (38-ое соединение на порту 3001).

По идее, если искать подстроки "socks-3001-192.168.1.178-io_thr_1-v1" и "(3001,38)", то в логе будет найдено все, что касается данного конкретного подключения на данной конкретной точке входа.

Проблема, однако, в том, что искать по двум подстрокам не удобно. Гораздо удобнее искать всего одну подстроку. А именно "(3001,38)" вместо "socks-3001-192.168.1.178-io_thr_1-v1".

Это проблема проявилась практически сразу, еще когда мы сами занимались отладкой первых версий arataga на собственных тестовых стендах. Собственно, поэтому и появилась в логе отдельная связка из номера порта и номера соединения на этом порту (та самая подстрока вида "(3001,38)"). Это помогало нам в нашей автономной отладке, т.к. у нас не было повторений номеров портов на точках входа.

Но когда arataga поработала под реальной нагрузкой в условиях, когда есть сотни точек входа с одинаковым номером TCP-порта (на разных IP-адресах), то выяснилось, что подстроки вида "(3001,38)" уже не позволяют легко отыскивать все, что касается конкретного подключения. Слишком уж много оказывалось вхождений "(3001,38)", относящихся к совсем другим точкам входа.

Поэтому arataga была немного доработана дла того, чтобы от пары (порт, номер соединения) перейти к триплету (уникальный ID, порт, номер соединения), где "уникальный ID" был бы неким более коротким и удобным синонимом для "socks-3001-192.168.1.178-io_thr_1-v1".

В результате сейчас в arataga записи в логе имеют вид:

[2021-04-29 09:04:34.558] [arataga] [error] http-5500-192.168.1.161-io_thr_0-v1: connection (6498_2185,5500,6) => update request-target failure: unable to parse request-target, http_parser_parse_url result: 1

Где подстрока "(6498_2185,5500,6)" уже однозначно указывает на строчки лога, относящиеся к конкретному соединению на конкретной точке входа (уникальный ID вида "6498_2185" назначен агенту "http-5500-192.168.1.161-io_thr_0-v1" при его создании):

[2021-04-29 09:04:33.981] [arataga] [info] http-5500-192.168.1.161-io_thr_0-v1: created, acl_id_seed: 6498_2185


Пора переходить к итогу и к морали.

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

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

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


Спасибо тем, кто продолжает читать этот блог. Писать что-то интересное и, надеюсь, полезное, становится все сложнее. Но я буду пытаться продолжать это делать. В частности, есть желание поделиться впечатлением от использования noexcept в коде arataga. Надеюсь, что смогу найти время для того, чтобы написать соответствующую заметку.

четверг, 1 июля 2021 г.

[life.cinema] Очередной кинообзор (2021/06)

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

Но, чтобы убить всякую интригу и не порождать напрасных ожиданий, скажу кратко: глянуть можно разве что "Кислород" и, если вам понравилась первая часть "Тихого места", то "Тихое место 2" (но лучше не ждать чего-то выдающегося). И все, сорян.

Фильмы

Кислород (Oxygène, 2021). Был приятно удивлен. Не ждал от продукции Netflix чего-нибудь толкового. Но у них получилось. Посмотрел с удовольствием.

Тихое место 2 (A Quiet Place Part II, 2021). Если вам понравился первый фильм, то можно посмотреть и второй. Ну а если первый восторга не вызывал, то и второй точно не вызовет. Как по мне, то вторая часть смотрится даже хуже, чем первая. Плюс у них серьезный косяк с приглашением на роль сына того же актера, что и в первой части (мальчик за 3 года сильно вырос и в продолжении это выглядит несуразно).

Бесконечность (Infinite, 2021). Красочно и местами даже динамично. Но блин, насколько же все тупо! Хотя, если этот фильм ориентирован на зрителей в возрасте 10 лет, то может и нормально для такой аудитории.

Дальний космос (Stowaway, 2021). Вот реально был неплохой заход на то, чтобы снять достойную космическую фантастику. Но, во-первых, получилось слишком нудно и затянуто. Во-вторых, почему-то в фильме не уделили никакого внимания тому, как "безбилетник" вообще попал на борт. В-третьих, почему-то сопереживать персонажам не хочется. Может быть потому, что в какие-то моменты они ведут себя как идиоты.

Первый клон (Seobok, 2021). По описанию и трейлеру ожидал, что будет бодрый фантастический боевик. Оказались фантастически нудные сопли. Смотреть можно разве что тем, кто интересуется корейским кино.

Ледяной драйв (The Ice Road, 2021). Халтура. Как по задумке, так и по исполнению.

Игры шпионов (The Courier, 2020). Не зашло. Смотреть было не интересно (особенно после прочтения вот такого взгляда на значимость предателя Олега Пеньковского). Такое ощущение, что основная цель съемок сего фильма была в том, чтобы дать Камбербэтчу повторить подвиг Кристиана Бейла в "Машинисте". Ну а появление в фильме гостиницы под названием "Отель Василий" просто подвело жирную черту под всем этим безобразием.

Сериалы

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

Призрак (2019). Редкая дрянь. Смело можно не смотреть.