Telegram Web Link
Создавая базу знаний, описывающую реальность, какой id вы присвоили бы понятию "религия"?

Я нашёл это понятие под id 666, у добавившего всё хорошо с чувством юмора.

P.S. на другие интересные сочетания цифр ничего не нашлось.
Экспериментирую сейчас с MongoDB.
Установил, разрешил подключения из внешнего мира, сижу, отлаживаюсь.

И тут в монге появляется новая база данных с сообщением, что все мои данные архивированы, плоти биткоины за расшифровку.

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

Разрешив подключение из внешнего мира я забыл вырубить возможность подключиться просто по 27017 порту, хорошо, что сейчас, а не на проде. Заодно и уползу на другой порт.
Forwarded from Kedr to Earth | Земля, я Кедр ( Yuri Ammosov)
Кажется, пора учредить премию "Безумный банщик", и собирать туда самые идиотские и нелепые причины, за что люди попадали в бан на фейсбуке.
Начну.
1) за преследование и оскорбление гравитации путём именования её собакой женского пола (пока что первое место, история произошла на днях с френдессой, у меня глаза как выпали на клавиатуру, так и лежат)
2) за употребление слова "хатифнатт" (пост удалили, выкатили предупреждение)
3) за употребление словосочетания "этрусский скот" в беседе об античной истории
4) за ироничное цитирование одного товарища (товарища оставили, мне выкатили предупреждение)
5) за фотографии статуй из Летнего сада
6) за фотографии маленьких детей на пляже - потому что детская порнография
7) за цитаты из классической литературы (не помню, какие, но довольно известные и вполне невинные)
8) за фразу "Резать, не дожидаясь перитонита"
9) за фото кильки в булке (порно)
10) за анонс лекции с изображением распятия из-за "демонстрации жестокости"
11) за "грузинских с--обак", хотя имелись в виду именно животные вида Canis lupus familiaris, обитающие в Грузии
12) за цитату статьи Даля о глаголе "майданить"
13) за скриншот антисемитского высказывания в свой адрес (автор скриншота забанен не был)
14) бан на неделю за публикацию перевода фразы, автоматически сделанного фейсбуком (и неверного, насколько я понимаю)
15) за фото своего кобеля, где чуть-чуть видны его яйца - порнография (видимо, на собаку мужского пола желательно надевать трусы перед тем, как сделать фото - прим. Улисса)
16) за слово а р * б в адрес лошади - разжигание
17) за комментарий о том, что мне не нравятся чёрные собаки - расизм. К комментарию, где я говорю, что белые собаки мне тоже не нравятся, у фб претензий нет.
18) за фото обычной морской черепахи. Потому что обнажёнка. Апелляцию не удовлетворили.
19) человека забанили на 30 дней за эпитет "жирненькая" по отношению к традесканции.
20) забанили бизнес-аккаунт магазина шмоток. На фото была куртка из коричневой кожи, обвинили в работорговле. Апелляцию не удовлетворили.
21) В группе томатоводов китайская хохлатая сожрала рассаду баклажанов. Ну народ ржал и обсуждал особенности лысых китайских с-бак. Фб забанил полгруппы вместе с админами
22) за поговорку "Россия родина слонов"
23) за комментарий о том, что автор не может ни одну нацию назвать тупой
24) за упоминание поэта начала 20 века, псевдоним которого состоял из имени Саша и наименования одного неполиткорректного цвета
25) за частое редактирование поста (чую, доиграюсь я сама с этим постом в итоге ^^)

https://facebook.com/story.php?story_fbid=10215431865910389&id=1849806973
Палантир. Часть 17. Оптимизация базы данных.
#палантир@eshu_coding

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

Для формирования тестовых поисковых запросов я использовал следущий поход: набил штук 100 разных слов (поток сознания там получился занятный, от "фавызм" (именно в таком написании) до хурмы и дыни.

Из этих слов случайным образом формируется пул запросов.

Запрос отправляется на сервер, ожидается ответ. Сразу по получении ответа - посылается следущий запрос. И так в N потоков.

Запустил в 1 поток: всё отлично.
Запустил в 10 потоков: всё отлично, постгрес не замечает нагрузки.
Запустил в 30 потоков. Постгрес не замечает нагрузки. Проходит пара минут и всё виснет.

Полез смотреть на сервер - 8 Гб оперативки кончились, вместе с 10 Гб файла подкачки, все съел постгрес, у которого установлен размер кэша в 2 Гб. Но каждое отдельное подключение, через которое шел поток данных, отъелось и в итоге память кончилась.

Оставил на ночь - постгрес не отвис: память утекла.

Итого, корень зла оказался в том, что подключения к постгресу, когда живут долгое время, не освобождают съеденную оператианую память (или освобождают не всю).

#postgresql
Палантир. Часть 18. Оптимизация базы данных.
#палантир@eshu_coding

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

Но как через подключения потекли значительные объемы данных, проблемы резко возникли. А с учётом того, что в поисковике ожидается много пользователей, подключений используется тоже много (в среднем 200-300, с максимальным лимитом в 1500).

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

В принципе, в ConnectionString, строке, которой описывается подключение к PostgreSQL, есть группа параметров, отвечающих за тот самый пул подключений, который я сам наколхозил. И время жизни соединения там тоже можно задать. Но соединение там рвется только при неактивности в течение N секунд, а мне нужен костыль, ограничивающий время жизни вне зависимости от активности использования.

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

В итоге я пришел к следующим настройкам пула: 200 подключений в резерве, время жизни подключения 30 секунд, проверка и обслуживание пула раз в 3 секунды.

#csharp
Палантир. Часть 19. Результаты нагрузочного тестирования.
#палантир@eshu_coding

В настоящий момент Master сервер,на котором осуществляется поиск, представляет собой следующее:
8 Гб оперативки, 4 ядра CPU, 1.5 Тб SSD диск. ОС - 18я серверная убунта, база данных - PostgreSQL 13.3. Объем базы - чуть больше 1 Тб, около 800 млн строк в основной таблице, по которой и осуществляется полнотекстовый поиск. Принцип формирования запросов я рассказывал выше.

После устранения утечки памяти (рассказывал выше) и оптимизации конфигурации Postgres по части памяти, сервис свободно держит 100 RPS (запросов в секунду).

В рамках экспериментов я выкручивал мощность машины до 16 ядер и 64 Гб оперативки. В таком сетапе удерживается нагрузка в 500-750 RPS.

Постгрес могёт однако.

#postgresql
1
Палантир. Часть 20. Ускорение поиска.
#палантир@eshu_coding

Оценить результат вы можете в боте: @palantir_search_bot

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

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

А потом в какой-то момент мне стукнуло в голову решение: основная таблица - messages - у меня секционирована по месяцам.

Секционирование - разбиение одной большой таблицы на группу по-меньше. Для каждой из таблиц существует отдельный индекс, что позволяет существенно ускорить работу: работать со 100 индексами 10-ти гигабайтных таблиц быстрее, чем с одним монстроиндексом терабайтной таблицы.

Соответственно, таблица messages представляет собой около 200 таблиц вида messages_01_2021, messages_02_2021 и так далее, с 2014 по 2030 год.

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

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

Также был доработан поисковый запрос на уровне c# - теперь он проводится в 3 этапа:
1. Запрос в последний месяц
2. Запрос в предпоследний месяц
3. Запрос во всю остальную базу, если нужно.

Результаты замеров скорости отклика на поиске глубиной в месяц:
1. Без кеширования - среднее время 200 мс, максимальное - 15 секунд
2. С закешированными индексами последних двух месяцев - среднее время 120 мс, максимальное - 6 секунд
3. С закешированными таблицами messages_10_2021 и messages_11_2021 - среднее время 80 мс, максимальное - 1.5 секунды

Под вариант "удобно использовать" подходит исключительно 3й, потому у сервера теперь 32 Гб оперативки (+3 тысячи к месячной плате)

#postgresql
Палантир. Часть 21. Боты, рефакторинг.
#палантир@eshu_coding

Идея с Final State Machine оказалась удачной, но первая реализация естественно вышла комом. Как только появилось понимание архитектуры, которую я хочу видеть в ботах, я сел и провел глобальный рефакторинг, оставив только логику и некоторые удачные модули, а заодно сменил базу, с которой работают боты, на MongoDB. У них в нынешнем виде вся логика работы с базой сводится к двум операциям: вставить/прочитать информацию по известному id.

Выбор базы обусловлен следующими причинами:
1. Операции без сложной логики "вставить/прочитать" в ней работают ощутимо быстрее, чем в постгресе.
2. У монги достаточно агрессивный механизм кеширования, который удобен для логики работы бота: Монга как хомяк набивает оперативку "горячей" информацией до тех пор, пока не выйдет за установленный лимит или не съест всю память. Информации там не будет очень много (перерасход 1-2 Гб оперативки я даже не замечу), а вот дополнительная скорость доступа к данным о юзерах, активных в настоящий момент не повредит.

В целом, MongoDB мне понравилась, с третьего подхода. Теперь хотя бы понятно, где она в тему, и в чём лучше PostgreSQL: скорость работы на некоторых операциях и простота использования.

При этом, главная её особенность, я бы сказал киллер-фича - простота построения распределенного хранилища, которое просто расширять и администрировать, мной пока не используется.
Палантир. Часть 22. Бот-агрегатор.
#палантир@eshu_coding

Ранее я описывал механизм работы "оповещалки" о происходящем в телеграме по ключевым словам: мной пишется запрос для полнотекстового поиска, который через каскад триггеров вызывает функцию pg_notify и сообщает внешним сервисам, что искомая фраза найдена.

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

А на другом конце провода - практически полный клон master сервера, я его назвал NotificationProvider, с практически идентичной базой, в которой и происходит анализ. Но оповещение вылетает не в бота на прямую, а публикуется в брокер сообщений RabbitMQ, к которому уже цепляются боты - подписчики. RabbitMQ, NotificationProvider и PostgreSQL запущены с помощью Docker-compose и работают как единый организм.

Такой подход позволяет плодить ботов-агрегаторов в неограниченных количествах, хотя сейчас их всего 6 штук.
Палантир. Часть 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
2025/07/09 20:16:03
Back to Top
HTML Embed Code: