Telegram Web
Палантир. Часть 23. Логика команд сервисам.
#палантир@eshu_coding

Как и обещал в конце поста, я таки переделал логику работы приказов для сборщиков.

Телеграм имеет следующую особенность: простой доступ к данным (по id, без суточных лимитов) возможен только когда аккаунт "знаком" с запрашиваемым объектом. Хэш "знакомства" хранится в базе сессии. Про найденное решение для хранения сессий я тоже писал ранее.

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

Кроме того, сборщики периодически приходят в негодность (баны, взломы, повреждение сессии), что также обуславливает необходимость отдельного "культивирования" подробной сессии. Для этих целей примерно 50% от суточного лимита в 200 тяжелых запросов идет к уже известным каналам, а еще 50 - тратятся на расширение охвата. И всё это приправлено костылями для максимального размытия пика нагрузок во времени.

Теперь, когда сборщик стучится за приказом, с вероятностью около 5% ему выдаётся запрос с суточным лимитом, а дальше с вероятностью 50 на 50 он запрашивает GetFullChannel или на новый канал или на уже известный. В итоге, когда очередной сборщик отваливается, отряд не замечает потерю бойца, а мониторинг некоторых каналов и чатов осуществляется буквально в реальном времени.
Палантир. Часть 24. Итоги, общая архитектура проекта.
#палантир@eshu_coding

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

На картинке:
DataFair - мастер сервер
DataLoader - сборщик
Пунтиром обведен docker-compose, в котором живет Observer - анализатор входящих данных в реальном времени.
SearchBot - поисковик @palantir_search_bot
ObserverBot - нижеперечисленные боты-агрегаторы:
@space_observer_bot
@gas_news_bot
@poland_belaruss_migr_bot
@investment_trends_bot
@Biden_smm_assistant_bot
Цветом выделены те сервисы, число которых может быть любым или близко к этому.

Итого, за время работы над проектом, начиная с 1 марта 2021 изучено:
1. Немного администрирования Linux
2. Docker, Docker-compose
3. CD/CI Github Actions
4. gRPC
5. PostgreSQL во всех позах
6. RabbitMQ
7. MongoDB
8. Подход к построению приложений Dependency Injection
9. Телеграмная кухня, обладающая своей спецификой
10. Полнотекстовый поиск
Произошло моё первое практическое знакомство с оптимизацией запросов. Нормально пользоваться планом я пока не научился, но первый медленный запрос к базе успешно оптимизирован.

Делал я полнотекстовый поиск в базе проекта #палантир@eshu_coding, приправленный дополнительным параметром: id юзера или канала, по которому осуществляется поиск. Эти поля у меня проиндексированы индексом hash, потому выборка записей по конкретным значениям этих полей мгновенная. Полнотекстовый поиск же штука относительно медленная.

Запрос вида

select * from messages where full_text_req @@ post_text and chat_id = id;

где @@ , грубо говоря, оператор соответствия текста запросу, на всей базе выполняется секунд 20.

Судя по всему, Postgres проверяет все записи на соответствие запросу, а затем уже применяет фильтр по id.

Для ускорения запроса мне на помощь пришли Common Table Expressions (CTE). Суть их - в поэтапном выполнении сложного запроса. Если переписать запрос с использованием CTE, скорость выполнения становится стабильно около 300 мс.

Переписанный запрос:

with sel as (select * from messages where chat_id = id) select * from sel where text @@ full_text_request;

#кодинг
#postgresql
🔥1
Продолжу затронутую уважаемым @ssleg тему тестов. Я наконец повзрослел и осознал логику организации функциональных и интеграционных тестов.

Если коротко - используем CD/CI (в моем случае Github Actions) для прогона обычных unit-тестов, завязанных на внешние сервисы. Нужные элементы - базы данных/другие микросервисы - поднимаем на отдельном сервере с помощью Docker из того же скрипта CD/CI. По завершении тестов - убираемся за собой.

Для иллюстрации создал репозиторий на гитхабе и настроил конвейер CD/CI.

Суть происходящего: проверяется взаимодействие самописного сервиса на c#, MongoDB и Tarantool-а. В моём случае - на идентичность тестовых данных, положенных в обе базы из моего сервиса.

Скрипт CD/CI состоит из трех частей (job-ов):
1. Развертывание инфраструктуры (deploy_infrastructure). Я тупо клонирую на сервер репозиторий и поднимаю прямо в папке решения всю систему с помощью docker-compose up -d --build. У меня на сервере запускаются 3 сервиса: мой, тарантул и монга.

2. Собственно тестирование (build_and_run_test). Если инфраструктура была развёрнута - запускаются все тесты, реализованные в соответствующих проектах, в т.ч. завязанные на базы данных.

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

При воспроизведении тесты живут в чем-то вроде виртуалки, предоставляемой Github-ом. Известны параметры подключения к сервисам, запущенным на предыдущей стадии, потому с ними можно провзаимодействовать и провалидировать результат.

Если всё ок - job загорается зелёным, нет - красным. К результату можно привязывать различные действия: пропускать коммит в репозиторий, начинать деплой в прод и т.д.

3. Зачистка следов (clear_infrastructure). Вне зависимости от результатов прошлой стадии, останавливаются и удаляются все запущенные на этапе 1 сервисы.

Имея простенький сервер-полигон за 130р/месяц и бесплатный Github actions можно реализовать практически любые свои фантазии по покрытию кода тестами: у меня тестируется только класс для работы с базами.

P.S. Синтаксис в скрипте вида ${{MAIN_HOST}} - это использование секретов Github actions: хранилища конфиденциальных данных, которым не место в репозитории. Добавляются они через настройки. Туда удобно помещать ssh ключи, адреса серверов, пароли и т.д.

#кодинг
#автотесты
#devops
👍1
Наверное главное открытие прошлого года для меня - оркестрация контейнеров с помощью docker compose (в прошлом посте с его помощью я поднимал тестовую инфраструктуру).

В самом начале разработки проекта в Visual Studio я теперь сразу создаю файл docker-compose.yml и запускаю проект для отладки всегда через него, вместе с настроенными игрушечным базами данных, полностью согласованными портами и строками подключения.

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

Даже отдельные контейнеры я тоже стараюсь запускать для отладки через compose: для полноты счастья в нем очень удобный механизм передачи переменных окружения, который в случае стандартного докера откровенно убог и неудобен.
WTFPL - по-моему самая правильная лицензия для пет проектов:). Надо будет потом её добавить везде в свой репозиторий.
👍13💩1
Продолжаю своё знакомство (первый опыт контакта был описан в посте про функциональные и интеграционные тесты) с детищем mail.ru - in-memory NoSQL базой данных Tarantool.

In-memory значит, что все данные она держит в оперативной памяти, то есть работает как кэш. Изначально это и был кэш над mysql. Постепенно тарантул мутировал и превратился в настоящего Арагога: под капотом интерпретатор языка Lua для написания хранимок, но можно запросы делать и на sql (к NoSQL базе, да).

Основные отличия от главного конкурента - кэша Redis - состоят в том, что Redis умеет работать как кэш для MongoDB, но при этом тарантул позволяет делать намного более сложные выборки: Redis работает как ключ - значение, то есть, выражаясь языком SQL, у него есть только индексированный primary key.

Тарантул же умеет строить сложносочинённые индексы из многих частей (до 256). Так что если нужны сложные но очень быстрые выборки - тарантул ваш выбор. Для пользователя тарантул не слишком дружелюбен. Впрочем, возможно дело в том, что официальной библиотеки для работы с тарантулом на c# от mail нету. Есть некая опенсорсная поделка, которая работает, и работает нормально, но в использовании она какая-то неудалая.

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

Индексы в тарантуле могут строиться как по одной "колонке", так и по многим. Составные индексы позволяют делать запросы как по части индекса, так и по значениям для всей строки целиком.

Предположим, хранятся у нас данные вида:
Id, Дата рождения, Фамилия, Имя, Отчество, Должность, Статус (работает/не работает), произвольная информация в следующих колонках. Индекс типа TREE (бинарное дерево под капотом) строится по всем полям, кроме Id. Он сможет отвечать на следующие запросы:
Фамилия
Фамилия + Имя
Фамилия + Имя + Отчество
Фамилия + Имя + Отчество + Должность
Фамилия + Имя + Отчество + Должность + Статус

В вот Фамилия + Должность - уже нет, для такого нужно создавать отдельный индекс или отправляться писать sql-запрос, который отрабатывает медленнее чем lua.

Первое знакомство с тарантулом скорее положительное, работает шустро. Главная претензия к шарповой обертке, там при написании кода получается дикая лапша, а еще без плясок с бубнами мне не удалось отправить в тарантул Guid (uuid, уникальный 128 байтовый идентификатор): превратил в строку, отправил в таком виде на сервер, там распарсил Guid обратно, что конечно дикость, но пока сойдет.

#tarantool
#кодинг
👍5
Начал знакомство с фронтендом: мир ReactJS ждёт.

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

Выбрал самый популярный из двух используемых для работы фреймворков (Vue и ReactJS).

Начал знакомство со среды разработки и на этом чуть не сломался: стандартный Visual Studio Code прям очень удручил, в итоге поставил WebStorm и дело сразу пошло.
🔥4👍1💩1
Только я начал нырять в зловонные глубины сайтоделания, как мой хороший знакомый фронтенд-программист завел свой дневничок.

Всецело поддерживаю, дело очень хорошее. Особенно хороша обратная связь от более опытных программистов и возможность вернуться на полгода назад и вспомнить, о чем вообще думал тогда.
Не прошло и двух месяцев, как я начал работать с Тарантулом, и только сейчас я наткнулся на красивое: https://try.tarantool.io/

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

#tarantool
👍6
Переписал бота для обратной связи. Старая версия, написанная полтора года назад, перестала работать. С ходу обновить базовую библиотеку для работы с телегой не удалось, потому решил переписать бота с нуля, благо все грабли уже пройдены. Итог - первый мой проект без говнокода (репозиторий), ну если только чуть чуть в юнит тестах.

В качестве базы данных использовал MongoDB. Вместо хранимок, продуманной схемы базы данных, primary и foreign ключей у меня теперь класс на 30 строк, позволяющий положить и прочитать любой другой класс из монги.

Update отдельных полей не предусмотрен, только insert, replace или find. Логика для замены и поиска передаётся в виде лямбда выражения. "Таблицы" - монговские коллекции - создаются по ситуации.

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

#кодинг
👍8🔥3
Прошли два месяца работы с тарантулом, и я наконец осознал (и отладил) адекватный способ хранить там слабо структурированную информацию: отправлять туда массив байтов byte[] из c#, а внутри тарантула соответствующее поле - string.

Принимать - тоже byte[]. string в тарантуле лежит просто как произвольная последовательность байт. Соответственно, никто не мешает хранить в этой куче байтов guid, который иначе без лишних преобразований из c# в тарантул не прокинуть.

Или - сериализовать в байты структуру (struct в c#, что-то типа класса, но хранится в стеке), или, вообще, использовать protobuf.

#tarantool
#кодинг
👍3
В условиях микросервисной архитектуры важную роль играют брокеры сообщений - обычно RabbitMQ или Kafka.

В кратце, соотношение плюсов и минусов между такое:
RabbitMQ - просто и сердито.
Kafka - сложнее, но лучше масштабируемо + возможность хранить историю прошедших сообщений.

Узнал еще преимущество Kafka: она оказывается умеет напрямую работать с базами данных: MongoDB, Tarantool, PostgreSQL, без самописных прослоек.
👍3
Впервые нашёл применение такому паттерну синхронизации межпоточного взаимодействия как семафор. Если в 2х словах, он ограничивает число потоков, проходящих через него. Поставил условные 50, и если к нему придут одновременно 1000 потоков, возникнет затор из ожидающих.

Я умудрился положить монгу большим числом параллельных запросов, при некоторых условиях их выстреливало больше 1000 сразу, после чего монга начинала кричать "остановись, демон" и кидала исключение. Семафор на 60 потоков меня спас, теперь всё отлично.
👍3
Погружаюсь в зловонную яму бездны фронтенда, а если точнее - React.js

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

Пока псевдоООП кажется каким-то уродством, но под него больше примеров и учебников, так что пока кушаю кактус.

#front
#react
👍5
В одном чате была затронута тема электробезопасности, потому выложу сюда байку по теме.

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

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

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

Одногруппники мне не поверили, потому трое по очереди брались за стенд под напряжением одной рукой и за "землю" - второй стенд - другой. Все соответственно получили. Откопали мультиметр - померяли - 220В.

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

Причина нашлась быстро: для простоты подключения земли к стендам его сделали в виде обычной пары вилка-розетка и вот наконец настал момент, когда вилку на одном из стендов воткнули не туда.
🔥19
Эшу быдлокодит
Погружаюсь в зловонную яму бездны фронтенда, а если точнее - React.js Вообще забавно, вроде бы фреймворк и фреймворк. Но по факту - отдельный язык для описания юзер интерфейса. При том, внутри самого реакта есть как минимум два совершенно разных (стилистически)…
Превращение человека в дачника во фронтендера стремительно и необратимо (с)

Вроде бы начал осознавать функциональные компоненты вместе с хранилищем состояний - Redux-ом. А React-то прикольный! Похоже, в стадиях заражения js-ом как мыслевирусом я сразу перепрыгнул на 5ю ступень.

На задворках сознания появилось желание вернуться к диссертации и сделать UI десктопного приложения на связке react + electron.

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

Класс интерпретатора js внутри c# с вероятностью 10% создаётся косячным и при попытке использования (то есть интерпретации километрового js-скрипта) плюёт пустой эксепшн (без описания проблемы).

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

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

И вот наступил момент запуска в прод, 64 бита и нагрузка. Пиковая нагрузка, внезапно съедается 100% ЦПУ, машина виснет (эксепшн - довольно тяжёлая операция). На поиск проблемы у меня-джуна ушло около месяца.

P.S. Это было в легаси коде.

#байки
🔥3
Эшу быдлокодит
Тут в одном чатике помянули, что в программировании чудес не бывает, в отличие от электроники. Небольшой пример из тёмного прошлого. Класс интерпретатора js внутри c# с вероятностью 10% создаётся косячным и при попытке использования (то есть интерпретации…
Да, починилось всё очень просто: вместо создания экземпляра интерпретатора для каждого запуска скрипта (хз зачем так было сделано) я стал использовать синглтон, который создавался рекурсивно и проверялся простым скриптом "а = 0".
2025/07/10 08:18:48
Back to Top
HTML Embed Code: