Палантир. Часть 15. Окончательное решение вопроса дублей.
#палантир@eshu_coding
Несмотря на локальную победу над дублями, они продолжали потихоньку попадать в базу.
В далёкие времена, когда я проектировал базу данных, я собирался использовать составной primary key в таблице с сообщениями: временная метка сообщения, id чата и порядковый номер сообщения. Но что-то пошло не так. Стандартный способ защиты таблицы с помощью триггера "before insert" не годился на секционированной таблице.
Делать проверку внутри хранимой процедуры для записи оказалось медленно: проверка занимает около 10 мс, на каждое из примерно 1000 сообщений, прилетающих каждую секунду. База мигом захлёбывается.
В итоге я забил на дубли, к чему это привело можете судить по прошлым постам.
Спасибо доброму человеку @vekhden_speak, он подсказал решение, с помощью которого я окончательно поборол дубли. Как окалось, в самом insert-e можно предотвратить конфликт добавив предложение "on conflict on constraint messages_pkey do nothing".
За сутки вычистив базу от остатков дублей, я дописал хранимую процедуру для записи данных в БД, после чего перешёл к изначальной идее ключей. Вот теперь дубли изжиты окончательно.
#postgresql
#палантир@eshu_coding
Несмотря на локальную победу над дублями, они продолжали потихоньку попадать в базу.
В далёкие времена, когда я проектировал базу данных, я собирался использовать составной primary key в таблице с сообщениями: временная метка сообщения, id чата и порядковый номер сообщения. Но что-то пошло не так. Стандартный способ защиты таблицы с помощью триггера "before insert" не годился на секционированной таблице.
Делать проверку внутри хранимой процедуры для записи оказалось медленно: проверка занимает около 10 мс, на каждое из примерно 1000 сообщений, прилетающих каждую секунду. База мигом захлёбывается.
В итоге я забил на дубли, к чему это привело можете судить по прошлым постам.
Спасибо доброму человеку @vekhden_speak, он подсказал решение, с помощью которого я окончательно поборол дубли. Как окалось, в самом insert-e можно предотвратить конфликт добавив предложение "on conflict on constraint messages_pkey do nothing".
За сутки вычистив базу от остатков дублей, я дописал хранимую процедуру для записи данных в БД, после чего перешёл к изначальной идее ключей. Вот теперь дубли изжиты окончательно.
#postgresql
Telegram
Эшу быдлокодит
Прошу прощения за долгое молчание, было безумно много работы.
Работа над диссертацией продолжается, но пока вяленько, планирую активизироваться на следующей неделе.
За прошедший месяц я реализовал и запустил новый проект: парсер текстов из телеграма, …
Работа над диссертацией продолжается, но пока вяленько, планирую активизироваться на следующей неделе.
За прошедший месяц я реализовал и запустил новый проект: парсер текстов из телеграма, …
Палантир. Часть 16. Клиентская часть для пользователей.
#палантир@eshu_coding
Работа над сборщиком данных в телеграме подошла к финальной стадии:
доделал mvp (минимально жизнеспособный продукт) части для пользователей, на выходе получилось два типа ботов:
Поисковик по телеграму @palantir_search_bot
Сервис оповещений @space_observer_bot
Поисковик просто ищет по скачанной истории сообщений за выбранный интервал времени, выплевывая 1000 самых похожих на запрос результатов.
Сервис оповещений проверяет все входящие сообщения (не старше 6 часов), и если они совпадают с заранее введенным запросом - оповещает пользователей.
Пока что реализована только демо версия, которая отрабатывает по следующему запросу:
Оператор | означает "или", оператор <-> - объединение слов по бокам в фразу. За первый же день работы на небольшую группу бета-тестеров стало очевидно, что все поисковые запросы и оповещения нужно приправлять блокировкой порнухи, ставок на спорт, крипты, политоты и экстремизма, чтобы случайно не заработать себе статью.
Занятной получилась реализация сервиса оповещений. Анализ текста у меня происходит на уровне базы данных, с использованием словарей для полнотекстового поиска. При этом, из базы результат нужно как-то доносить до пользователя.
В итоге родилась идея: приправить основную таблицу триггером after insert, который будет пытаться вставить сообщение, если оно свежее 6 часов, в другую таблицу, получившую название spotter (наводчик).
На таблице spotter висит триггер, который делает select из таблицы queries (хранящей запросы), давая ответ: подходит под запрос или нет.
После этого вызывается функция pg_notify("test", "информация о сообщении"), которая передает информацию о сообщении всем, кто выполнил команду listen "test" и продолжает висеть на связи. В сообщении отправляется ссылка на сообщение и коротенькое превью из 200 первых символов.
Бот-слушатель соответственно рассылает сообщения подписантам.
Теперь для адекватной работы оповещалки (сокращения времени от опубликования до нахождения сообщения до 30-60 минут) нужно в очередной (в 8й) раз переделать менеджер команд сборщикам.
#postgresql
#палантир@eshu_coding
Работа над сборщиком данных в телеграме подошла к финальной стадии:
доделал mvp (минимально жизнеспособный продукт) части для пользователей, на выходе получилось два типа ботов:
Поисковик по телеграму @palantir_search_bot
Сервис оповещений @space_observer_bot
Поисковик просто ищет по скачанной истории сообщений за выбранный интервал времени, выплевывая 1000 самых похожих на запрос результатов.
Сервис оповещений проверяет все входящие сообщения (не старше 6 часов), и если они совпадают с заранее введенным запросом - оповещает пользователей.
Пока что реализована только демо версия, которая отрабатывает по следующему запросу:
'(илон <-> маск) | рогозин | космос | ракета | космическая <-> станция | астероид | галактика | солнечная <-> система | комета | марс | юпитер | сатурн | плутон |венера | солнечные <-> пятна | солнечный <-> ветер | байконур | роскосмос | space <-> x | spacex | орбита | космический <-> мусор | МКС | космонавт | астронавт'
Оператор | означает "или", оператор <-> - объединение слов по бокам в фразу. За первый же день работы на небольшую группу бета-тестеров стало очевидно, что все поисковые запросы и оповещения нужно приправлять блокировкой порнухи, ставок на спорт, крипты, политоты и экстремизма, чтобы случайно не заработать себе статью.
Занятной получилась реализация сервиса оповещений. Анализ текста у меня происходит на уровне базы данных, с использованием словарей для полнотекстового поиска. При этом, из базы результат нужно как-то доносить до пользователя.
В итоге родилась идея: приправить основную таблицу триггером after insert, который будет пытаться вставить сообщение, если оно свежее 6 часов, в другую таблицу, получившую название spotter (наводчик).
На таблице spotter висит триггер, который делает select из таблицы queries (хранящей запросы), давая ответ: подходит под запрос или нет.
После этого вызывается функция pg_notify("test", "информация о сообщении"), которая передает информацию о сообщении всем, кто выполнил команду listen "test" и продолжает висеть на связи. В сообщении отправляется ссылка на сообщение и коротенькое превью из 200 первых символов.
Бот-слушатель соответственно рассылает сообщения подписантам.
Теперь для адекватной работы оповещалки (сокращения времени от опубликования до нахождения сообщения до 30-60 минут) нужно в очередной (в 8й) раз переделать менеджер команд сборщикам.
#postgresql
Палантир. Часть 17. Оптимизация базы данных.
#палантир@eshu_coding
В ближайшее время я планирую запустить поисковик @palantir_search_bot в общее пользование. Пришло время нагрузочного тестирования. Для этого я изобразил отдельный проектик, который имитирует поискового бота.
Для формирования тестовых поисковых запросов я использовал следущий поход: набил штук 100 разных слов (поток сознания там получился занятный, от "фавызм" (именно в таком написании) до хурмы и дыни.
Из этих слов случайным образом формируется пул запросов.
Запрос отправляется на сервер, ожидается ответ. Сразу по получении ответа - посылается следущий запрос. И так в N потоков.
Запустил в 1 поток: всё отлично.
Запустил в 10 потоков: всё отлично, постгрес не замечает нагрузки.
Запустил в 30 потоков. Постгрес не замечает нагрузки. Проходит пара минут и всё виснет.
Полез смотреть на сервер - 8 Гб оперативки кончились, вместе с 10 Гб файла подкачки, все съел постгрес, у которого установлен размер кэша в 2 Гб. Но каждое отдельное подключение, через которое шел поток данных, отъелось и в итоге память кончилась.
Оставил на ночь - постгрес не отвис: память утекла.
Итого, корень зла оказался в том, что подключения к постгресу, когда живут долгое время, не освобождают съеденную оператианую память (или освобождают не всю).
#postgresql
#палантир@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
#палантир@eshu_coding
Подключения к базе данных устанавливаются относительно продолжительное время, потому принято пользоваться ими длительное время. Мои боты, которых я писал летом 2020 висят на постоянных подключениях иногда по нескольку месяцев и проблем не было.
Но как через подключения потекли значительные объемы данных, проблемы резко возникли. А с учётом того, что в поисковике ожидается много пользователей, подключений используется тоже много (в среднем 200-300, с максимальным лимитом в 1500).
Проблему утечки памяти из прошлого поста я решил переработкой менеджера подключений. Некоторое время назад я написал класс, который ведёт учёт всех подключений к базе (чтобы не выйти за установленное максимальное число), держит несколько готовых подключений в горячем резерве и закрывает те, которые больше не нужны. Добавил к нему функцию закрытия подключений, которые живут и используются слишком долго и в которых мог накопиться мусор.
В принципе, в ConnectionString, строке, которой описывается подключение к PostgreSQL, есть группа параметров, отвечающих за тот самый пул подключений, который я сам наколхозил. И время жизни соединения там тоже можно задать. Но соединение там рвется только при неактивности в течение N секунд, а мне нужен костыль, ограничивающий время жизни вне зависимости от активности использования.
По хорошему, хранением соединений должна заниматься библиотека, с помощью которой я цепляюсь к базе данных - Npgsql. Но вот функции горячего резерва N подключений и безболезненного ограничения максимального числа в ней нет: мой менеджер подключений ждёт пока освободится слот, а Npgsql кидает исключение, что мне в этом проекте неудобно.
В итоге я пришел к следующим настройкам пула: 200 подключений в резерве, время жизни подключения 30 секунд, проверка и обслуживание пула раз в 3 секунды.
#csharp
Telegram
Эшу быдлокодит
Прошу прощения за долгое молчание, было безумно много работы.
Работа над диссертацией продолжается, но пока вяленько, планирую активизироваться на следующей неделе.
За прошедший месяц я реализовал и запустил новый проект: парсер текстов из телеграма, …
Работа над диссертацией продолжается, но пока вяленько, планирую активизироваться на следующей неделе.
За прошедший месяц я реализовал и запустил новый проект: парсер текстов из телеграма, …
Палантир. Часть 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
#палантир@eshu_coding
В настоящий момент Master сервер,на котором осуществляется поиск, представляет собой следующее:
8 Гб оперативки, 4 ядра CPU, 1.5 Тб SSD диск. ОС - 18я серверная убунта, база данных - PostgreSQL 13.3. Объем базы - чуть больше 1 Тб, около 800 млн строк в основной таблице, по которой и осуществляется полнотекстовый поиск. Принцип формирования запросов я рассказывал выше.
После устранения утечки памяти (рассказывал выше) и оптимизации конфигурации Postgres по части памяти, сервис свободно держит 100 RPS (запросов в секунду).
В рамках экспериментов я выкручивал мощность машины до 16 ядер и 64 Гб оперативки. В таком сетапе удерживается нагрузка в 500-750 RPS.
Постгрес могёт однако.
#postgresql
Telegram
Эшу быдлокодит
Палантир. Часть 17. Оптимизация базы данных.
#палантир@eshu_coding
В ближайшее время я планирую запустить поисковик @palantir_search_bot в общее пользование. Пришло время нагрузочного тестирования. Для этого я изобразил отдельный проектик, который имитирует…
#палантир@eshu_coding
В ближайшее время я планирую запустить поисковик @palantir_search_bot в общее пользование. Пришло время нагрузочного тестирования. Для этого я изобразил отдельный проектик, который имитирует…
❤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
#палантир@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
Telegram
Эшу быдлокодит
Егор Рогов из Postgres Professional подробно и доступно рассказывает теорию и практику работы с PostgreSQL:
📌 Индексы
- Механизм индексирования
- Интерфейс метода доступа, классы и семейства операторов
- Hash
- B-tree
- GiST
- SP-GiST
- GIN
- RUM
- BRIN…
📌 Индексы
- Механизм индексирования
- Интерфейс метода доступа, классы и семейства операторов
- Hash
- B-tree
- GiST
- SP-GiST
- GIN
- RUM
- BRIN…
Палантир. Часть 21. Боты, рефакторинг.
#палантир@eshu_coding
Идея с Final State Machine оказалась удачной, но первая реализация естественно вышла комом. Как только появилось понимание архитектуры, которую я хочу видеть в ботах, я сел и провел глобальный рефакторинг, оставив только логику и некоторые удачные модули, а заодно сменил базу, с которой работают боты, на MongoDB. У них в нынешнем виде вся логика работы с базой сводится к двум операциям: вставить/прочитать информацию по известному id.
Выбор базы обусловлен следующими причинами:
1. Операции без сложной логики "вставить/прочитать" в ней работают ощутимо быстрее, чем в постгресе.
2. У монги достаточно агрессивный механизм кеширования, который удобен для логики работы бота: Монга как хомяк набивает оперативку "горячей" информацией до тех пор, пока не выйдет за установленный лимит или не съест всю память. Информации там не будет очень много (перерасход 1-2 Гб оперативки я даже не замечу), а вот дополнительная скорость доступа к данным о юзерах, активных в настоящий момент не повредит.
В целом, MongoDB мне понравилась, с третьего подхода. Теперь хотя бы понятно, где она в тему, и в чём лучше PostgreSQL: скорость работы на некоторых операциях и простота использования.
При этом, главная её особенность, я бы сказал киллер-фича - простота построения распределенного хранилища, которое просто расширять и администрировать, мной пока не используется.
#палантир@eshu_coding
Идея с Final State Machine оказалась удачной, но первая реализация естественно вышла комом. Как только появилось понимание архитектуры, которую я хочу видеть в ботах, я сел и провел глобальный рефакторинг, оставив только логику и некоторые удачные модули, а заодно сменил базу, с которой работают боты, на MongoDB. У них в нынешнем виде вся логика работы с базой сводится к двум операциям: вставить/прочитать информацию по известному id.
Выбор базы обусловлен следующими причинами:
1. Операции без сложной логики "вставить/прочитать" в ней работают ощутимо быстрее, чем в постгресе.
2. У монги достаточно агрессивный механизм кеширования, который удобен для логики работы бота: Монга как хомяк набивает оперативку "горячей" информацией до тех пор, пока не выйдет за установленный лимит или не съест всю память. Информации там не будет очень много (перерасход 1-2 Гб оперативки я даже не замечу), а вот дополнительная скорость доступа к данным о юзерах, активных в настоящий момент не повредит.
В целом, MongoDB мне понравилась, с третьего подхода. Теперь хотя бы понятно, где она в тему, и в чём лучше PostgreSQL: скорость работы на некоторых операциях и простота использования.
При этом, главная её особенность, я бы сказал киллер-фича - простота построения распределенного хранилища, которое просто расширять и администрировать, мной пока не используется.
Telegram
Эшу быдлокодит
Прошу прощения за долгое молчание, было безумно много работы.
Работа над диссертацией продолжается, но пока вяленько, планирую активизироваться на следующей неделе.
За прошедший месяц я реализовал и запустил новый проект: парсер текстов из телеграма, …
Работа над диссертацией продолжается, но пока вяленько, планирую активизироваться на следующей неделе.
За прошедший месяц я реализовал и запустил новый проект: парсер текстов из телеграма, …
Палантир. Часть 22. Бот-агрегатор.
#палантир@eshu_coding
Ранее я описывал механизм работы "оповещалки" о происходящем в телеграме по ключевым словам: мной пишется запрос для полнотекстового поиска, который через каскад триггеров вызывает функцию pg_notify и сообщает внешним сервисам, что искомая фраза найдена.
Изначально этот функционал крутился в основной базе, на которой и так складирование всей информации и обслуживание поисковика. Чтобы разгрузить базу, я использовал следующий трюк: самодельную подписку на gRPC. Все желающие сообщений могут постучаться с запросом на сервер, после чего между ними и сервером повиснет gRPC канал, через который master-сервер потоком сливает все сообщения, только что пришедшие от сборщиков данных.
А на другом конце провода - практически полный клон master сервера, я его назвал NotificationProvider, с практически идентичной базой, в которой и происходит анализ. Но оповещение вылетает не в бота на прямую, а публикуется в брокер сообщений RabbitMQ, к которому уже цепляются боты - подписчики. RabbitMQ, NotificationProvider и PostgreSQL запущены с помощью Docker-compose и работают как единый организм.
Такой подход позволяет плодить ботов-агрегаторов в неограниченных количествах, хотя сейчас их всего 6 штук.
#палантир@eshu_coding
Ранее я описывал механизм работы "оповещалки" о происходящем в телеграме по ключевым словам: мной пишется запрос для полнотекстового поиска, который через каскад триггеров вызывает функцию pg_notify и сообщает внешним сервисам, что искомая фраза найдена.
Изначально этот функционал крутился в основной базе, на которой и так складирование всей информации и обслуживание поисковика. Чтобы разгрузить базу, я использовал следующий трюк: самодельную подписку на gRPC. Все желающие сообщений могут постучаться с запросом на сервер, после чего между ними и сервером повиснет gRPC канал, через который master-сервер потоком сливает все сообщения, только что пришедшие от сборщиков данных.
А на другом конце провода - практически полный клон master сервера, я его назвал NotificationProvider, с практически идентичной базой, в которой и происходит анализ. Но оповещение вылетает не в бота на прямую, а публикуется в брокер сообщений RabbitMQ, к которому уже цепляются боты - подписчики. RabbitMQ, NotificationProvider и PostgreSQL запущены с помощью Docker-compose и работают как единый организм.
Такой подход позволяет плодить ботов-агрегаторов в неограниченных количествах, хотя сейчас их всего 6 штук.
Telegram
Эшу быдлокодит
Прошу прощения за долгое молчание, было безумно много работы.
Работа над диссертацией продолжается, но пока вяленько, планирую активизироваться на следующей неделе.
За прошедший месяц я реализовал и запустил новый проект: парсер текстов из телеграма, …
Работа над диссертацией продолжается, но пока вяленько, планирую активизироваться на следующей неделе.
За прошедший месяц я реализовал и запустил новый проект: парсер текстов из телеграма, …
Палантир. Часть 23. Логика команд сервисам.
#палантир@eshu_coding
Как и обещал в конце поста, я таки переделал логику работы приказов для сборщиков.
Телеграм имеет следующую особенность: простой доступ к данным (по id, без суточных лимитов) возможен только когда аккаунт "знаком" с запрашиваемым объектом. Хэш "знакомства" хранится в базе сессии. Про найденное решение для хранения сессий я тоже писал ранее.
Для максимально оперативной подгрузки обновлений чатов и каналов нужно, чтобы каждый из сборщиков имел в сессии записи о максимально возможном числе каналов и чатов, тогда работа по выгрузке будет распределена равномерно.
Кроме того, сборщики периодически приходят в негодность (баны, взломы, повреждение сессии), что также обуславливает необходимость отдельного "культивирования" подробной сессии. Для этих целей примерно 50% от суточного лимита в 200 тяжелых запросов идет к уже известным каналам, а еще 50 - тратятся на расширение охвата. И всё это приправлено костылями для максимального размытия пика нагрузок во времени.
Теперь, когда сборщик стучится за приказом, с вероятностью около 5% ему выдаётся запрос с суточным лимитом, а дальше с вероятностью 50 на 50 он запрашивает GetFullChannel или на новый канал или на уже известный. В итоге, когда очередной сборщик отваливается, отряд не замечает потерю бойца, а мониторинг некоторых каналов и чатов осуществляется буквально в реальном времени.
#палантир@eshu_coding
Как и обещал в конце поста, я таки переделал логику работы приказов для сборщиков.
Телеграм имеет следующую особенность: простой доступ к данным (по id, без суточных лимитов) возможен только когда аккаунт "знаком" с запрашиваемым объектом. Хэш "знакомства" хранится в базе сессии. Про найденное решение для хранения сессий я тоже писал ранее.
Для максимально оперативной подгрузки обновлений чатов и каналов нужно, чтобы каждый из сборщиков имел в сессии записи о максимально возможном числе каналов и чатов, тогда работа по выгрузке будет распределена равномерно.
Кроме того, сборщики периодически приходят в негодность (баны, взломы, повреждение сессии), что также обуславливает необходимость отдельного "культивирования" подробной сессии. Для этих целей примерно 50% от суточного лимита в 200 тяжелых запросов идет к уже известным каналам, а еще 50 - тратятся на расширение охвата. И всё это приправлено костылями для максимального размытия пика нагрузок во времени.
Теперь, когда сборщик стучится за приказом, с вероятностью около 5% ему выдаётся запрос с суточным лимитом, а дальше с вероятностью 50 на 50 он запрашивает GetFullChannel или на новый канал или на уже известный. В итоге, когда очередной сборщик отваливается, отряд не замечает потерю бойца, а мониторинг некоторых каналов и чатов осуществляется буквально в реальном времени.
Telegram
Эшу быдлокодит
Прошу прощения за долгое молчание, было безумно много работы.
Работа над диссертацией продолжается, но пока вяленько, планирую активизироваться на следующей неделе.
За прошедший месяц я реализовал и запустил новый проект: парсер текстов из телеграма, …
Работа над диссертацией продолжается, но пока вяленько, планирую активизироваться на следующей неделе.
За прошедший месяц я реализовал и запустил новый проект: парсер текстов из телеграма, …
Палантир. Часть 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
По завершении ботов-агрегаторов проект я считаю завершенным, время описать что же получилось и подвести итоги.
На картинке:
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, потому выборка записей по конкретным значениям этих полей мгновенная. Полнотекстовый поиск же штука относительно медленная.
Запрос вида
где @@ , грубо говоря, оператор соответствия текста запросу, на всей базе выполняется секунд 20.
Судя по всему, Postgres проверяет все записи на соответствие запросу, а затем уже применяет фильтр по id.
Для ускорения запроса мне на помощь пришли Common Table Expressions (CTE). Суть их - в поэтапном выполнении сложного запроса. Если переписать запрос с использованием CTE, скорость выполнения становится стабильно около 300 мс.
Переписанный запрос:
#кодинг
#postgresql
Делал я полнотекстовый поиск в базе проекта #палантир@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
Палантир. Часть 25. Рефлексия год спустя.
#палантир@eshu_coding
Описание получившейся системы можно посмотреть по ссылке. Одной из некритичных ошибок была попытка сделать шину данных из костылей и велосипедов на gRPC вместо использования стандартного решения типа RabbitMQ.
Возможно, я сколько-то выиграл в производительности, но ценой этого был отказ от персистентности пропускаемых через шину данных и использование страшненьких костылей для подключения дополнительных потребителей данных.
Если мне надо раздвоить поток данных, уведя копию на другой сервис - в RabbitMQ требуется просто подключиться к нему, указав источник данных и новую очередь.
Мне пришлось вкрячивать внутрь приемника данных от сборщиков сервис подписок, чтобы данные сразу по получении рассылались всем кто подключен. Персистентность? Гарантированная доставка? Не, это не наш метод.
В принципе, ошибка крайне неэстетичная, но на функциональность особого влияния не оказывающая.
#палантир@eshu_coding
Описание получившейся системы можно посмотреть по ссылке. Одной из некритичных ошибок была попытка сделать шину данных из костылей и велосипедов на gRPC вместо использования стандартного решения типа RabbitMQ.
Возможно, я сколько-то выиграл в производительности, но ценой этого был отказ от персистентности пропускаемых через шину данных и использование страшненьких костылей для подключения дополнительных потребителей данных.
Если мне надо раздвоить поток данных, уведя копию на другой сервис - в RabbitMQ требуется просто подключиться к нему, указав источник данных и новую очередь.
Мне пришлось вкрячивать внутрь приемника данных от сборщиков сервис подписок, чтобы данные сразу по получении рассылались всем кто подключен. Персистентность? Гарантированная доставка? Не, это не наш метод.
В принципе, ошибка крайне неэстетичная, но на функциональность особого влияния не оказывающая.
👍4🔥1
Палантир. Часть 26. Рефлексия год спустя.
#палантир@eshu_coding
Описание получившейся системы можно посмотреть по ссылке. Намного менее приятной ошибкой было скатывание к монолиту. Центральный сервис (БД на postgresql + надстройка на шарпе) у меня выполнял четыре основные функции:
1. Прием и укладка данных в БД
2. Анализ БД и выдача заданий сборщикам
3. Поисковые кэш и запросы к нему.
4. Анализ потока входящей информации на наличие ключевых слов и выдача оповещений при обнаружении.
Данные попадали в надстройку над БД, проталкивались в базу огромными транзакциями, заодно обновляя служебные таблицы для выдачи заданий сборщикам. Индекс для полнотекстового поиска был построен прямо в основной таблице, поисковые запросы соответственно летели к ней. Триггер, срабатывающий на вставление новых данных, запускал цепочку других триггеров, в которых осуществлялся анализ добавляемого текста. В случае соответствия забитым в базу поисковым паттернам - база отправляла во внешний мир оповещение с помощью функции pg_notify.
В итоге такой подход вылился в жуткую боль как по администрированию, так и по поддержке. В конечном итоге, я вытащил функционал (4) в отдельный сервис, но боль от этого не сильно уменьшилась.
Как на мой нынешний взгляд надо было сделать:
1. Использовать RabbitMQ вместо самопала на gRPC (см прошлый пост)
2. Отработать запись данных в БД. Накрыть тестами функционал.
3. Запустить отдельным сервисом постановку задач сборщикам. Накрыть тестами функционал. Скорее всего, после окончательной отладки этот сервис сольётся с п. 2. Я начал делать их вместе, в итоге так и не смог до конца искоренить дублирование выгружаемых сообщений.
4. Завести отдельную пару сервис + бд для поискового кэша. Сделать как обновление в реальном времени, отведя с помощью RabbitMQ поток данных в сторону + предусмотреть функционал "перелива" данных из основной базы. Теперь я не привязан в PostgreSQL rum индексу на базе дефолтного словаря! И можно экспериментировать с поисковыми движками как душе угодно, не нарушая функциональность основного сервера. Хоть эластик попробовать, хоть сделать прослойку на питоне для умной обработки текста готовыми инструментами.
5. Отвести поток данных для анализа в реальном времени (для выдачи оповещений пользователям) и экспериментировать сколько душе угодно.
P.S. "Отвести поток данных" в случае RabbitMQ значит добавить несколько символов в месте подключения клиентской библиотеки.
P.P.S. Никто не мешает после отладки функционала по отдельности вернуться к монолиту, например для повышения быстродействия. Если закладывать такую возможность на старте - это дело 1-2 дней.
#палантир@eshu_coding
Описание получившейся системы можно посмотреть по ссылке. Намного менее приятной ошибкой было скатывание к монолиту. Центральный сервис (БД на postgresql + надстройка на шарпе) у меня выполнял четыре основные функции:
1. Прием и укладка данных в БД
2. Анализ БД и выдача заданий сборщикам
3. Поисковые кэш и запросы к нему.
4. Анализ потока входящей информации на наличие ключевых слов и выдача оповещений при обнаружении.
Данные попадали в надстройку над БД, проталкивались в базу огромными транзакциями, заодно обновляя служебные таблицы для выдачи заданий сборщикам. Индекс для полнотекстового поиска был построен прямо в основной таблице, поисковые запросы соответственно летели к ней. Триггер, срабатывающий на вставление новых данных, запускал цепочку других триггеров, в которых осуществлялся анализ добавляемого текста. В случае соответствия забитым в базу поисковым паттернам - база отправляла во внешний мир оповещение с помощью функции pg_notify.
В итоге такой подход вылился в жуткую боль как по администрированию, так и по поддержке. В конечном итоге, я вытащил функционал (4) в отдельный сервис, но боль от этого не сильно уменьшилась.
Как на мой нынешний взгляд надо было сделать:
1. Использовать RabbitMQ вместо самопала на gRPC (см прошлый пост)
2. Отработать запись данных в БД. Накрыть тестами функционал.
3. Запустить отдельным сервисом постановку задач сборщикам. Накрыть тестами функционал. Скорее всего, после окончательной отладки этот сервис сольётся с п. 2. Я начал делать их вместе, в итоге так и не смог до конца искоренить дублирование выгружаемых сообщений.
4. Завести отдельную пару сервис + бд для поискового кэша. Сделать как обновление в реальном времени, отведя с помощью RabbitMQ поток данных в сторону + предусмотреть функционал "перелива" данных из основной базы. Теперь я не привязан в PostgreSQL rum индексу на базе дефолтного словаря! И можно экспериментировать с поисковыми движками как душе угодно, не нарушая функциональность основного сервера. Хоть эластик попробовать, хоть сделать прослойку на питоне для умной обработки текста готовыми инструментами.
5. Отвести поток данных для анализа в реальном времени (для выдачи оповещений пользователям) и экспериментировать сколько душе угодно.
P.S. "Отвести поток данных" в случае RabbitMQ значит добавить несколько символов в месте подключения клиентской библиотеки.
P.P.S. Никто не мешает после отладки функционала по отдельности вернуться к монолиту, например для повышения быстродействия. Если закладывать такую возможность на старте - это дело 1-2 дней.
Telegram
Эшу быдлокодит
Палантир. Часть 24. Итоги, общая архитектура проекта.
#палантир@eshu_coding
По завершении ботов-агрегаторов проект я считаю завершенным, время описать что же получилось и подвести итоги.
На картинке:
DataFair - мастер сервер
DataLoader - сборщик
Пунтиром…
#палантир@eshu_coding
По завершении ботов-агрегаторов проект я считаю завершенным, время описать что же получилось и подвести итоги.
На картинке:
DataFair - мастер сервер
DataLoader - сборщик
Пунтиром…
👍3💩1
Эшу быдлокодит
Палантир. Часть 26. Рефлексия год спустя. #палантир@eshu_coding Описание получившейся системы можно посмотреть по ссылке. Намного менее приятной ошибкой было скатывание к монолиту. Центральный сервис (БД на postgresql + надстройка на шарпе) у меня выполнял…
В общем, если бы я строил Палантир сейчас, у меня вышло бы что-то подобное. Несколько проще, чем на схеме по ссылке, да и технологически беднее.
#палантир
#палантир
👍3
В прошлом году итоги я подвёл в ноябре при запуске #палантир@eshu_coding, после него ничего особо интересного не было.
В этом году никаких проектов не под NDA, я в общем-то не делал. Если без подробностей, то вот краткие итоги личностного роста:
1. Научился готовить Tarantool, прошёлся по куче граблей, примерно понял, где он реально нужен, и главное, где не нужен. Пришлось поверхностно познакомиться и с механизмом построения кластеров: репликация, шардирование, вот это вот всё.
2. Нырнул в MongoDB на уровне, отличном от "положил как в помойку, достал по id/однострочному запросу". Многоступенчатые запросы, подписки на обновления коллекций, TTL, транзакции, materialised view. Впервые завел реплика-сет вместо отдельностоящего инстанса.
3. Построил пару обменников на RabbitMQ, пришло осознание проглоченного и применённого на коленке в 2021.
4. Влюбился в систему сбора метрик Prometheus. Если раскидать их по приложению и заодно подключить сбор с компонентов системы (баз данных и т.д.), получается крайне информативно. Дефолтный визуализатор так себе, но основные функции выполняет.
5. Познакомился с EKL стеком - сбор и визуализация логов. Особой любви не случилось, уж больно оно огромно и прожорливо. Можно кстати использовать ELK для построения красивых дашбордов по данным из Prometheus-а, но это мне пока не особо нужно.
6. Прогресс как шарписта у меня вышел так себе. Научился красиво описывать rest api с помощью Swagger, да в общем-то и всё. Ну ещё окончательно освоил разработку с использованием докера: приложение сразу запускается и отлаживается в контейнере, окружённое соседними сервисами. При прогоне тестов также активно использую песочницу, поднятую в docker-compose.
P.S. NoSQL я накушался досыта, 2023 - время вернуться к истокам - C# + PostgreSQL.
В этом году никаких проектов не под NDA, я в общем-то не делал. Если без подробностей, то вот краткие итоги личностного роста:
1. Научился готовить Tarantool, прошёлся по куче граблей, примерно понял, где он реально нужен, и главное, где не нужен. Пришлось поверхностно познакомиться и с механизмом построения кластеров: репликация, шардирование, вот это вот всё.
2. Нырнул в MongoDB на уровне, отличном от "положил как в помойку, достал по id/однострочному запросу". Многоступенчатые запросы, подписки на обновления коллекций, TTL, транзакции, materialised view. Впервые завел реплика-сет вместо отдельностоящего инстанса.
3. Построил пару обменников на RabbitMQ, пришло осознание проглоченного и применённого на коленке в 2021.
4. Влюбился в систему сбора метрик Prometheus. Если раскидать их по приложению и заодно подключить сбор с компонентов системы (баз данных и т.д.), получается крайне информативно. Дефолтный визуализатор так себе, но основные функции выполняет.
5. Познакомился с EKL стеком - сбор и визуализация логов. Особой любви не случилось, уж больно оно огромно и прожорливо. Можно кстати использовать ELK для построения красивых дашбордов по данным из Prometheus-а, но это мне пока не особо нужно.
6. Прогресс как шарписта у меня вышел так себе. Научился красиво описывать rest api с помощью Swagger, да в общем-то и всё. Ну ещё окончательно освоил разработку с использованием докера: приложение сразу запускается и отлаживается в контейнере, окружённое соседними сервисами. При прогоне тестов также активно использую песочницу, поднятую в docker-compose.
P.S. NoSQL я накушался досыта, 2023 - время вернуться к истокам - C# + PostgreSQL.
Telegram
Эшу быдлокодит
Палантир. Часть 24. Итоги, общая архитектура проекта.
#палантир@eshu_coding
По завершении ботов-агрегаторов проект я считаю завершенным, время описать что же получилось и подвести итоги.
На картинке:
DataFair - мастер сервер
DataLoader - сборщик
Пунтиром…
#палантир@eshu_coding
По завершении ботов-агрегаторов проект я считаю завершенным, время описать что же получилось и подвести итоги.
На картинке:
DataFair - мастер сервер
DataLoader - сборщик
Пунтиром…
👍9❤1
Sphagnum. Часть 2. Зачем, а главное нафига?
#sphagnum@eshu_coding
1. Саморазвитие. Читать умные книги это очень познавательно, но намного лучше я усваиваю информацию, когда решаю какую-то задачу, интересную мне. А тут ожидается и изучение существующих архитектурных решений, и выдавливание максимума производительности из c# кода.
2. Для встраиваемого брокера сообщений в экосистеме .Net вполне найдется место. Я вижу как минимум две ниши:
a) Организация сетевого взаимодействия в геймдеве на Unity или в мобильной разработке с использованием MAUI.
b) Онлайн анализ протекающих через брокер данных: например поиск ключевых слов в потоке логов/текстов, которые отправляются на архивирование. Можно конечно ответвить поток данных на отдельного потребителя, осуществляющего анализ. Но в случае цунами из данных, анализ может произойти слишком поздно. А со встроенным брокером - просто скопировать данные (или вообще ссылку на них) внутри процесса. Если бы я запускал #палантир@eshu_coding сейчас, я бы точно не отказался от такого инструмента.
#sphagnum@eshu_coding
1. Саморазвитие. Читать умные книги это очень познавательно, но намного лучше я усваиваю информацию, когда решаю какую-то задачу, интересную мне. А тут ожидается и изучение существующих архитектурных решений, и выдавливание максимума производительности из c# кода.
2. Для встраиваемого брокера сообщений в экосистеме .Net вполне найдется место. Я вижу как минимум две ниши:
a) Организация сетевого взаимодействия в геймдеве на Unity или в мобильной разработке с использованием MAUI.
b) Онлайн анализ протекающих через брокер данных: например поиск ключевых слов в потоке логов/текстов, которые отправляются на архивирование. Можно конечно ответвить поток данных на отдельного потребителя, осуществляющего анализ. Но в случае цунами из данных, анализ может произойти слишком поздно. А со встроенным брокером - просто скопировать данные (или вообще ссылку на них) внутри процесса. Если бы я запускал #палантир@eshu_coding сейчас, я бы точно не отказался от такого инструмента.
Telegram
Эшу быдлокодит
Sphagnum. Часть 1. Начало.
#sphagnum@eshu_coding
Начинаю новый пет-проект: свой брокер сообщений. Обозвал я его пока Sphagnum, проект планируется в качестве чисто образовательного, под лицензией MIT. Что я хочу получить на выходе:
1. По основному функционалу…
#sphagnum@eshu_coding
Начинаю новый пет-проект: свой брокер сообщений. Обозвал я его пока Sphagnum, проект планируется в качестве чисто образовательного, под лицензией MIT. Что я хочу получить на выходе:
1. По основному функционалу…
👍4
Послушал открытый урок Otus по некоторым особенностям оптимизации работы кластеров PostgreSQL. В целом, получилось очень познавательно: некоторые из услышанных вещей, упомянутых лишь краем, вызвали реакцию "хм, а так было можно?"
Вот перечень запомнившихся моментов:
1. Оптимизация скорости работы базы данных за счёт разнесения разных таблиц по разным дискам с помощью указания табличных пространств (tablespace). Я про что-то подобное мельком читал во время работы над Палантиром #палантир@eshu_coding, но воспринимал эти махинации исключительно с т.з. размещения большой базы, не влезающей на диск.
2. Оптимизация путем указания стоимости операции одиночного чтения с произвольного места диска. С классических hdd намного быстрее читать последовательно записанные данные. Потому в случае соотношения размера таблицы к размеру индекса 4 к 1 читать с hdd всю таблицу может быть быстрее, чем сканировать индекс и брать нужное из таблицы. А вот на SSD такой разницы нет, и выставив параметр конфигурации random_page_cost=1 мы ощутимо ускорим чтение данных.
3. Махинации с типом репликации. В случае, когда производительность на запись/изменение совсем поджимает, можно пожертвовать надёжностью реплицирования, но выиграть до порядка по быстродействию, переключив репликацию с синхронной на асинхронную. А ещё можно делать реплику, на которую изменения с мастера будут применяться с установленной задержкой, например - час. И в случае катастрофы, например - удаления базы целиком, будет какое-то время оживить систему с меньшими потерями, чем из вчерашнего бэкапа.
4. Узнал про возможность конкуррентного переиндексирования. Предположим, индекс раздулся и начал тупить и есть много места. Можно его удалить и перестроить с нуля. Но "на живую" это приведет к большим проблемам. А оказывается можно запустить фоновое переиндексирование.
5. Узнал, что в постгрес таки есть механизм подсказок планировщику запросов, что ему делать. Для этого нужно расширение pg_hint_plan.
6. Услышал оговорку "обычно балансировку запросов между мастером и репликами у нас делают сами приложения". А так было можно?! Полез читать - оказывается, у стандартного шарпового коннектора Npgsql и балансировщик и координатор выборов нового мастера в случае сбоя есть под капотом.
Курс я конечно покупать не буду, но за 1.5 часа времени информация получена очень полезная.
#postgresql
Вот перечень запомнившихся моментов:
1. Оптимизация скорости работы базы данных за счёт разнесения разных таблиц по разным дискам с помощью указания табличных пространств (tablespace). Я про что-то подобное мельком читал во время работы над Палантиром #палантир@eshu_coding, но воспринимал эти махинации исключительно с т.з. размещения большой базы, не влезающей на диск.
2. Оптимизация путем указания стоимости операции одиночного чтения с произвольного места диска. С классических hdd намного быстрее читать последовательно записанные данные. Потому в случае соотношения размера таблицы к размеру индекса 4 к 1 читать с hdd всю таблицу может быть быстрее, чем сканировать индекс и брать нужное из таблицы. А вот на SSD такой разницы нет, и выставив параметр конфигурации random_page_cost=1 мы ощутимо ускорим чтение данных.
3. Махинации с типом репликации. В случае, когда производительность на запись/изменение совсем поджимает, можно пожертвовать надёжностью реплицирования, но выиграть до порядка по быстродействию, переключив репликацию с синхронной на асинхронную. А ещё можно делать реплику, на которую изменения с мастера будут применяться с установленной задержкой, например - час. И в случае катастрофы, например - удаления базы целиком, будет какое-то время оживить систему с меньшими потерями, чем из вчерашнего бэкапа.
4. Узнал про возможность конкуррентного переиндексирования. Предположим, индекс раздулся и начал тупить и есть много места. Можно его удалить и перестроить с нуля. Но "на живую" это приведет к большим проблемам. А оказывается можно запустить фоновое переиндексирование.
5. Узнал, что в постгрес таки есть механизм подсказок планировщику запросов, что ему делать. Для этого нужно расширение pg_hint_plan.
6. Услышал оговорку "обычно балансировку запросов между мастером и репликами у нас делают сами приложения". А так было можно?! Полез читать - оказывается, у стандартного шарпового коннектора Npgsql и балансировщик и координатор выборов нового мастера в случае сбоя есть под капотом.
Курс я конечно покупать не буду, но за 1.5 часа времени информация получена очень полезная.
#postgresql
🔥7
Попробую посмешить богов и в этом году. Планы на 2024 год.
1. Читать умные книжки:
- Рихтер
- Внедрение зависимостей на платформе .Net (Симан, ван Дерсен)
- Чистая архитектура (Роберт Мартин)
- Высоконагруженные приложения (Мартин Клеппман)
2. Осознать до конца некоторые архитектурные загибы:
- Аспекто-ориентированное программирование
- Акторы
- Углубиться в DDD
3. На практике освоить магию Source Generators - на этапе компиляции c# проекта, компилятор по заранее заданным правилам анализирует созданное им, на основании анализа генерирует некоторое количество нового c# кода, компилирует снова вместе с генеренным кодом и так сколько настроишь раз.
4. Таки потыкать алгоритмы, задачки на литкоде.
5. Добраться до кода с пет проектом #sphagnum.
6. Добраться до кода с пет проектом #cthulhu
7. Есть желание переписать и перезапустить #палантир@eshu_coding.
1. Читать умные книжки:
- Рихтер
- Внедрение зависимостей на платформе .Net (Симан, ван Дерсен)
- Чистая архитектура (Роберт Мартин)
- Высоконагруженные приложения (Мартин Клеппман)
2. Осознать до конца некоторые архитектурные загибы:
- Аспекто-ориентированное программирование
- Акторы
- Углубиться в DDD
3. На практике освоить магию Source Generators - на этапе компиляции c# проекта, компилятор по заранее заданным правилам анализирует созданное им, на основании анализа генерирует некоторое количество нового c# кода, компилирует снова вместе с генеренным кодом и так сколько настроишь раз.
4. Таки потыкать алгоритмы, задачки на литкоде.
5. Добраться до кода с пет проектом #sphagnum.
6. Добраться до кода с пет проектом #cthulhu
7. Есть желание переписать и перезапустить #палантир@eshu_coding.
tesera.ru
Ктулху / Trail of Cthulhu | Tesera
Trail of Cthulhu — настольная ролевая игра, приключения в мире Лавкрафтовского ужаса, разыгрываемые по правилам «СЫЩИК» (GUMSHOE).
👍3🔥1
Пожалуй пришло время обновить закреплённый пост. Каналу уже 5 лет, с прошлого закрепа изменилось многое.
Датасаенс, питон и наука были заброшены. В настоящий момент я работаю сеньор C# разработчиком в одном из российских банков.
За прошедшие 5 лет я сменил 4 места работы:
1. Фирма, занимающая АСУ ТП в области учёта ресурсов.
2. Медтех стартап в Сколково, делали системы поддержки принятия врачебных решений.
3. Сеть общепита, делал бэкенд службы доставки.
4. Банк, текущее место работы. Работаю в домене клиентских карточек.
Мой технологический стек:
C#, PostgreSQL. Плотно работал с MongoDB, RabbitMQ, Tarantool, умею строить базовую инфраструктуру: логи (Loki), метрики (Prometheus), девопсятина (gitlab, gitea, github actions, docker).
Поверхностно знаком с Apache Kafka, MS SQL и фронтовыми фреймворками - React.js и AvaloniaUI.
По образованию я инженер-оптик, потому часть базы приходится добирать на ходу. В планах закрыть гештальт по алгоритмам и двигаться в сторону архитектуры.
Далее будет навигация по каналу.
Общие теги:
#csharp@eshu_coding - общий тег для постов про разные аспекты разработки на языке программирования c#
#postgresql@eshu_coding - разные интересные моменты про PostgreSQL.
#devops@eshu_coding - мои эксперименты в девопсятине и инфраструктуре.
#mongodb@eshu_coding - записки про MongoDB.
#tarantool@eshu_coding - заметки про Tarantool.
Pet - проекты:
#палантир@eshu_coding - завершенный проект, которым я занимался весь 2021 год - поисковик по телеграму.
#sphagnum@eshu_coding - попытка написать свой брокер сообщений, пока застопорилась на стадии изучения теории и прототипирования по причине нехватки времени.
Книги:
#рихтер@eshu_coding - заметки и конспекты по основополагающей книге про C# - CLR via C#. Программирование на платформе Microsoft .NET Framework 4.5 на языке C#, Джеффри Рихтер. Хоть .NET 4.5 вышел до моего появления в IT, внутренности платформы во основном остались прежними.
Конспекты прослушанных выступлений на конференциях:
#dotnext@eshu_coding - Dotnext 2023
#highload@eshu_coding - Highload++ 2024
Шпаргалки и мои заметки для подготовки к собеседованиям #собес@eshu_coding
Природа и путешествия #природа@eshu_coding #путешествие@eshu_coding
Датасаенс, питон и наука были заброшены. В настоящий момент я работаю сеньор C# разработчиком в одном из российских банков.
За прошедшие 5 лет я сменил 4 места работы:
1. Фирма, занимающая АСУ ТП в области учёта ресурсов.
2. Медтех стартап в Сколково, делали системы поддержки принятия врачебных решений.
3. Сеть общепита, делал бэкенд службы доставки.
4. Банк, текущее место работы. Работаю в домене клиентских карточек.
Мой технологический стек:
C#, PostgreSQL. Плотно работал с MongoDB, RabbitMQ, Tarantool, умею строить базовую инфраструктуру: логи (Loki), метрики (Prometheus), девопсятина (gitlab, gitea, github actions, docker).
Поверхностно знаком с Apache Kafka, MS SQL и фронтовыми фреймворками - React.js и AvaloniaUI.
По образованию я инженер-оптик, потому часть базы приходится добирать на ходу. В планах закрыть гештальт по алгоритмам и двигаться в сторону архитектуры.
Далее будет навигация по каналу.
Общие теги:
#csharp@eshu_coding - общий тег для постов про разные аспекты разработки на языке программирования c#
#postgresql@eshu_coding - разные интересные моменты про PostgreSQL.
#devops@eshu_coding - мои эксперименты в девопсятине и инфраструктуре.
#mongodb@eshu_coding - записки про MongoDB.
#tarantool@eshu_coding - заметки про Tarantool.
Pet - проекты:
#палантир@eshu_coding - завершенный проект, которым я занимался весь 2021 год - поисковик по телеграму.
#sphagnum@eshu_coding - попытка написать свой брокер сообщений, пока застопорилась на стадии изучения теории и прототипирования по причине нехватки времени.
Книги:
#рихтер@eshu_coding - заметки и конспекты по основополагающей книге про C# - CLR via C#. Программирование на платформе Microsoft .NET Framework 4.5 на языке C#, Джеффри Рихтер. Хоть .NET 4.5 вышел до моего появления в IT, внутренности платформы во основном остались прежними.
Конспекты прослушанных выступлений на конференциях:
#dotnext@eshu_coding - Dotnext 2023
#highload@eshu_coding - Highload++ 2024
Шпаргалки и мои заметки для подготовки к собеседованиям #собес@eshu_coding
Природа и путешествия #природа@eshu_coding #путешествие@eshu_coding
❤7👍6🔥2👎1
И начну я серию постов к собеседованиям #собес с базовой базы: первых четырёх нормальных форм (НФ) реляционки. Вообще, я как-то больше по денормализации, потому попробую снабдить каждую из НФ короткими ремарками, как я её попирал.
Первая нормальная форма: в таблицах есть первичные ключи, отсутствуют дубли, к тому же нет составных данных, то есть не допускается хранение данных в одной записи в виде массива или просто утрамбованных в текстовый тип данных с разделителем. Про то, как я мучался с дублями в таблице в ~500Гб можно почитать тут. А еще у меня на одном из рабочих проектов был опыт, когда одно из хранимых мной полей могло быть long, long[], guid или objectId (монговский формат "уникального" id). Сохранение в виде строки не проходило по требованиям к объему базы, раскидывание по разным строкам - по требованиям к производительности. В итоге я изобразил свой бинарный формат и хранил тупо байтики.
Поясню: long - id сущности из другой системы, например - "инфаркт" (система была для медтеха). long[] - жесткая сцепка двух понятий, например - "инфаркт в анамнезе", что несет несколько другой смысл для принятия врачебных решений (делалась СППВР).
Вторая нормальная форма. База пребывает в первой форме, а в дополнение - данные во всех столбцах зависят от первичного ключа целиком. Нарушал, сознательно, получилось удачно. Таблица, в которой хранятся логи того, что произошло с заказом. Ключ у таблицы составной, пусть будет id заказа + id изменения, уникальный в рамках данного заказа. Мне понадобилось иметь быстрый доступ к статусу заказа, ну я и стал его проставлять во всех записях, относящихся к заказу. В результате можно получить некоторый выигрыш в получении статуса: указываем id заказа, после чего первая встреченная нами запись гарантировано содержит нужную нам информацию. Вообще, это дикость и варварство, не надо так делать, но в том конкретном случае получилось отлично за счёт некоторых нюансов, про которые я когда-нибудь напишу отдельно.
Третья нормальная форма + нормальная форма Бойса-Кодда - база пребывает в первой и второй НФ, к тому же отсутствует один из любимых (в т.ч. мной) способов посрезать углы: вместо внешнего ключа, указывающего на таблицу-справочник, писать значение из несостоявшегося справочника прямо в основную таблицу. Встречал подобное десятки раз, обычно так сохраняют статусы, меняющиеся со временем или обвешивают записи системой тегов для группировки или фильтрации. И сам так делал и буду делать, если дозволяют принятые стандарты.
Для себя не вижу смысла разделять их между собой. С т.з. формального определения разница существенная, а вот стандартное нарушение едино.
Четвертая нормальная форма - база пребывает во всех предшествующих нормальных формах, а в дополнение в таблицах отсутствуют данные, зависящие только от первичного ключа, но при этом не имеющие между собой логических связей. Этот принцип я нарушал множество раз и с особым цинизмом. Один из примеров - был у меня маленький проект, связанный с системой построения отчётов FastReports (я писал о ней). Мне нужно было решить проблему хранения файлов отчётов и информации о них: название, тег (идентификатор отчёта, по которому его запрашивает фронт), группа, сам файл макета отчёта, сериализованный в base64 (да, хранить такое в базе - дикость, но иногда приходится).
По хорошему, у меня должно было быть три или четыре таблицы, связанные внешними ключами, что-то вроде: reports_info, reports_tags, reports_groups, reports_base64_files. Но я все утрамбовал в одну таблицу, вынимая по тегу отчёт, имеющий самую свежую дату. Примитивно и сердито.
Из кучи просмотренного в интернете по нормальным формам, самой толковой оказалась информация на сайте ИТМО: первая и вторая, третья, четвертая.
Первая нормальная форма: в таблицах есть первичные ключи, отсутствуют дубли, к тому же нет составных данных, то есть не допускается хранение данных в одной записи в виде массива или просто утрамбованных в текстовый тип данных с разделителем. Про то, как я мучался с дублями в таблице в ~500Гб можно почитать тут. А еще у меня на одном из рабочих проектов был опыт, когда одно из хранимых мной полей могло быть long, long[], guid или objectId (монговский формат "уникального" id). Сохранение в виде строки не проходило по требованиям к объему базы, раскидывание по разным строкам - по требованиям к производительности. В итоге я изобразил свой бинарный формат и хранил тупо байтики.
Поясню: long - id сущности из другой системы, например - "инфаркт" (система была для медтеха). long[] - жесткая сцепка двух понятий, например - "инфаркт в анамнезе", что несет несколько другой смысл для принятия врачебных решений (делалась СППВР).
Вторая нормальная форма. База пребывает в первой форме, а в дополнение - данные во всех столбцах зависят от первичного ключа целиком. Нарушал, сознательно, получилось удачно. Таблица, в которой хранятся логи того, что произошло с заказом. Ключ у таблицы составной, пусть будет id заказа + id изменения, уникальный в рамках данного заказа. Мне понадобилось иметь быстрый доступ к статусу заказа, ну я и стал его проставлять во всех записях, относящихся к заказу. В результате можно получить некоторый выигрыш в получении статуса: указываем id заказа, после чего первая встреченная нами запись гарантировано содержит нужную нам информацию. Вообще, это дикость и варварство, не надо так делать, но в том конкретном случае получилось отлично за счёт некоторых нюансов, про которые я когда-нибудь напишу отдельно.
Третья нормальная форма + нормальная форма Бойса-Кодда - база пребывает в первой и второй НФ, к тому же отсутствует один из любимых (в т.ч. мной) способов посрезать углы: вместо внешнего ключа, указывающего на таблицу-справочник, писать значение из несостоявшегося справочника прямо в основную таблицу. Встречал подобное десятки раз, обычно так сохраняют статусы, меняющиеся со временем или обвешивают записи системой тегов для группировки или фильтрации. И сам так делал и буду делать, если дозволяют принятые стандарты.
Для себя не вижу смысла разделять их между собой. С т.з. формального определения разница существенная, а вот стандартное нарушение едино.
Четвертая нормальная форма - база пребывает во всех предшествующих нормальных формах, а в дополнение в таблицах отсутствуют данные, зависящие только от первичного ключа, но при этом не имеющие между собой логических связей. Этот принцип я нарушал множество раз и с особым цинизмом. Один из примеров - был у меня маленький проект, связанный с системой построения отчётов FastReports (я писал о ней). Мне нужно было решить проблему хранения файлов отчётов и информации о них: название, тег (идентификатор отчёта, по которому его запрашивает фронт), группа, сам файл макета отчёта, сериализованный в base64 (да, хранить такое в базе - дикость, но иногда приходится).
По хорошему, у меня должно было быть три или четыре таблицы, связанные внешними ключами, что-то вроде: reports_info, reports_tags, reports_groups, reports_base64_files. Но я все утрамбовал в одну таблицу, вынимая по тегу отчёт, имеющий самую свежую дату. Примитивно и сердито.
Из кучи просмотренного в интернете по нормальным формам, самой толковой оказалась информация на сайте ИТМО: первая и вторая, третья, четвертая.
Telegram
Эшу быдлокодит
Палантир. Часть 14. Дубли в базе. Боль и страдания.
#палантир@eshu_coding
Одной из первых проблем были дубли в данных: одно и то же сообщение засасывалось более одного раза.
В какой-то момент я принял решение просто наплевать на них: ну есть у меня 15%…
#палантир@eshu_coding
Одной из первых проблем были дубли в данных: одно и то же сообщение засасывалось более одного раза.
В какой-то момент я принял решение просто наплевать на них: ну есть у меня 15%…
🔥11