четверг, 19 мая 2016 г.

[prog.c++] Еще один real-world пример из SObjectizer: трюки с таймерами

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

Есть такая маленькая, но хорошая библиотечка procxx для запуска дочерних процессов в Unix-ах (я ее слегка доработал напильником, но мой pull request пока не приняли). Запуск какой-нибудь внешней программы и чтение ее выхлопа посредством procxx -- это не просто, а очень просто:

procxx::process some_tool( "some_tool", key, value, key2, value2, ... );
some_tool.exec();

std::string line;
while( std::getline(some_tool.output(), line) )
   handle_line(line);

some_tool.wait();

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

Сделать это дедлайн в SO-5 не сложнее, чем запустить внешний процесс с помощью procxx ;)

среда, 18 мая 2016 г.

[prog.unix] Нужен совет бывалых Unix-оидов

Столкнулся с задачкой, которая не кажется слишком сложной, но почему-то вводит меня в ступор. Может потому, что я не настоящий сварщик Unix-оид :)

Суть в том, что некая программулина с кучей конфигов должна легко тиражироваться на N компьютеров. Исполнимые файлы, so-шки и конфиги для всех машин будут идентичны. Но.

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

После развертывания программулина должна стартовать, взять откуда-то этот идентификатор и затем, во всех коммуникациях с внешним миром должна использовать этот идентификатор. Программулина стартует не под root-ом, а под отдельным пользователем, у которого, по хорошему, вместо шелла будет /usr/sbin/nologin. Ну и, если это важно, запускаться программулина будет посредством runit и егоного chpst (при этом вносить какую-то уникальную информацию в соответствующий run файл не есть хорошо, т.к. хотелось бы, чтобы run-файлы, как и другие исполнимые файлы, были идентичны для всех машин).

Для отладки всего этого дела удобно использовать переменные среды. Т.е. в разных ssh/telnet-сессиях задаю для переменной MY_NODE_ID разные значения и запускаю одну и ту же программу в нескольких копиях. Было бы здорово и при развертывании на реальные машины использовать что-то такое: чтобы идентификатор машины можно было считывать из окружения, а не из файликов в /etc или в /home.

Буду признателен, если кто-нибудь подскажет, в какую сторону копать.

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

пятница, 13 мая 2016 г.

[prog.c++14] Шаблоны против копипасты-5: variadic templates упрощают написание unit-тестов

В процессе развития одной библиотеки появилось время написать unit-тесты. Библиотека представляет из себя сборище классов, экземпляры которых используются в качестве MQ-шных сообщений. В качестве формата данных -- JSON. Нужно проверить правильно ли происходи сериализация и десериализация каждого типа сообщения.

Если использовать тупой метод грубой силы, то получилось бы что-то вроде (названия типов и полей искажены):

четверг, 12 мая 2016 г.

[prog.c++14] Как же выглядят реальные агенты в SObjectizer?

Если не ошибаюсь, уже несколько раз говорил о том, что C++ Actor Framework (он же CAF) производит довольно неоднозначное впечатление. С одной стороны -- это очень удачная попытка изобразить Erlang-овые процессы в виде C++ных акторов. Но, с другой стороны, никогда толком не понимал, как же в CAF-е будет выглядеть что-либо более-менее приближенное к реальности. Т.е. не тестовая программка из категории ping-pong или fixed-stack. А настоящий, живой актор, выполняющий какую-то прикладную логику.

Ведь один из уроков, который мы выучили, уже довольно долго работая с агентами в C++, -- это то, что агенты со временем пухнут в размерах. Тупо код одного агента может занимать не одну сотню строк (а в редких случаях и не одну тысячу строк). Как все это уложить в CAF-овские акторы с кучей лямбда-функций... Это вопрос.

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

среда, 11 мая 2016 г.

[life.book] Дочитал таки "Признание шефа разведки"

Сумел дочитать книгу Боба Вудворда "Признание шефа разведки". Книга толстая, читается трудно -- большое количество имен, дат и событий, ну и структурирован этот объем как-то не очень (мне так показалось). Но было любопытно. В очередной раз подтверждена старая мудрость: ужас советской пропаганды в том, что она говорила правду. Т.е. если сохранились воспоминания о том, что в тогдашних газетах, книгах, документальных фильмах и телепередачах говорилось про вмешательство США во внутренние дела других стран, то никаких откровений в "Признании шефа разведки" не будет -- только подтверждение того, о чем говорилось в СССР раньше. Разве что подоплека событий в Ливии 2011-го года стала еще лучше понятна.

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

воскресенье, 8 мая 2016 г.

[life] Предпраздничное, грустное.

Иногда кажется, что мое поколение -- это чуть ли не последнее поколение, для которого последствия Великой Отечественной Войны ощущались на физическом уровне. Мои родители родились до войны. Их детство пришлось на военные годы, а юношество -- на послевоенные. Дед по отцу погиб в 1941-ом в Ленинграде. Отец смог разыскать его могилу и посетить ее только в 1988-м. Мою маму с семьей отправляли в Германию, им удалось сбежать из обоза, прожить несколько месяцев в лесу и затем тайком вернуться в свой дом.

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

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

Поэтому вполне понятно, что появляются деятели, которые задаются вопросом "а не следовало ли сдать Ленинград?" Или заявляют "Я не понимаю, что такое Великая Отечественная война. Это какой-то странный кусок войны, прихотливо вырезанный пропагандистами из Второй Мировой войны."

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

Разве что можно попросить задуматься. Даже не о том, что подобным образом уже просрали великую страну. А сейчас можно просрать и еще большее: свое моральное право жить на своей земле. Это все высокие материи, кому до них есть дело?

Задуматься нужно о том, что распространяя подобную гадость вы вступаете в войну.

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


Комментов не будет. Этот пост не для обсуждения. А для того, чтобы задуматься.

среда, 4 мая 2016 г.

[prog.c++11] Внес маленькую лепту в развитие spdlog

Есть для C++11 обалденно классная и удобная библиотека для логирования -- spdlog. Небольшая, header-only, под MIT-лицензией. При этом использует фичи еще одной очень хорошей C++ной библиотеки cppformat (он же fmtlib).

Но была в spdlog до недавнего времени небольшая закавыка: если использовать daily_file_sink, то имена суточных лог-файлов строились в виде basname_YYYY-MM-DD_hh-mm.ext. И все, больше никак. Т.е., если программа стартовала в 13:02, то будет создан лог-файл с именем basename_2016-05-02_13-02.ext. А если в 13:04 она рестартует, то рядом появится файл basename_2016-05-02_13-04.ext.

Мне было не очень удобно отлаживаться с таким подходом к логированию. Вместо того, чтобы один раз в отдельной консоли запустить tail -f basename_2016-05-02.ext и следить за тем, что пишется в лог, в том числе и при рестартах, приходится постоянно переключаться между логами.

В итоге я это дело в spdlog::sinks::daily_file_sink подправил и теперь для этого sink-а можно задать свой генератор имен лог-файлов. Делается это через еще один шаблонный параметр для класса daily_file_sink. По умолчанию этот параметр имеет значение spdlog::sinks::default_daily_file_name_calculator. Данный генератор имен суточных лог-файлов создает имена традиционным для spdlog способом -- с добавлением к дате часа и минуты, как показано выше.

Но вторым параметром в daily_file_sink можно подсунуть свой собственный генератор. Например, вот такой:

struct custom_daily_file_name_calculator
{
    static spdlog::filename_t calc_filename(const spdlog::filename_t& basename, const spdlog::filename_t& extension)
    {
        std::tm tm = spdlog::details::os::localtime();
        fmt::MemoryWriter w;
        w.write("{}{:04d}{:02d}{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
        return w.str();
    }
};

После чего создать logger посредством функции spdlog::create с указанием custom_daily_file_name_calculator:

using sink_type = spdlog::sinks::daily_file_sink< std::mutex, custom_daily_file_name_calculator>;
auto logger = spdlog::create<sink_type>("logger", basename, "log", 0, 0, true);

И у вас будут суточные файлы с именами вида logger20160502.log.

Так же в spdlog::sinks добавлен тип dateonly_daily_file_name_calculator, который создает имена лог-файлов вида basename_YYYY-MM-DD.ext. Так что можно сразу брать готовый генератор имен и не писать свой.

Эти изменения уже приняты в spdlog, так что начиная с коммита ef9842c этой функциональностью можно пользоваться прям из коробки ;)