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

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

Итак, мне было интересно, например, чтобы irssi или hexchat выводили в desktop notification все реплики участников в определённых каналах. Большая часть задачи решается запросто. Однако есть один момент - сам по себе процесс отображения уведомления (через консольную notify-send) может занять пару-тройку секунд и если дожидаться окончания работы notify-send то клиент на это время не отображает ничего и в него нельзя впечатать ничего. Теряется интерактивность приложения.

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

"Да запросто", подумал я. Действительно, почему бы не взять перловый встроенный модуль threads? Взял, запользовал. И заметил, что hexchat довольно быстро нажирается памяти и занимает по 700+ мегабайт. Зато интерактивность не страдает, всё работает на ура. Ну и ладно, а что там с irssi? да всё тоже самое, вот 1 в 1, только память она зажирает не так быстро.

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

Что делать? А что в таких случаях делают? Запускают отдельно процессы-обработчики и в них пушат данные по мере их прибытия. Логично возникает вопрос: как быстро избавляться от данных и в то же время не нагружать систему? Как сделать асинхронную передачу информации для обработчиков?

Как вариант - использовать fifo... Идея классная, но fifo у нас есть только на nix-ах, а хотелось бы не только nix для этого использовать, ведь эти все клиенты есть и на windows, а там всё работает... эммм... по-другому.

Другой вариант - складывать данные куда-то в файл, но тут возможны race condition-ы и всякие другие нюансы.

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

Вариант номер четыре - key-value базюлька. Redis, CouchDB, mongo, Memcache. По сути у нас и есть k-v данные. Отталкиваемся от простоты реализации. Memcache есть везде, правда под венду его ещё поискать надо, но в принципе найти можно. И размер у программы невелик - менее полумегабайта бинарей и больше ничего - вообще шикарное решение.

Ну, что же выбрали memcache. И не даром: для того perl-а, который идёт с hexchat-ом модулей-биндингов для БД вообще нету. Есть, конечно, вариант собрать нужные модули самостоятельно, у них где-то в доках даже было что-то на эту тему и вродебы даже относительно подробно... но... ну его нафиг, нужно чтобы работало с наименьшими усилиями, так что я взял модуль Socket и решил воспользоваться текстовым вариантом протокола memcache (https://github.com/memcached/memcached/blob/master/doc/protocol.txt). Получилось всё неплохо: простецкий модуль для hexchat-а, который пихает key (суффикс + md5 от строки ссылки) и value (ссылку) в memcache, а дальше молотилка выгребает всё это из memcache-а, попутно удаляя вынутые item-ы из базки и скачивает нужные url-ы. С notification-ами тоже просто - там просто другой суффикс.

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

Next Post