Telegram Web Link
Палантир. Часть 14. Дубли в базе. Боль и страдания.
#палантир@eshu_coding

Одной из первых проблем были дубли в данных: одно и то же сообщение засасывалось более одного раза.

В какой-то момент я принял решение просто наплевать на них: ну есть у меня 15% дублирующихся данных, да и черт с ними. Но тут случился эпик фейл.

Запись данных в БД существенно опережала индексацию сообщения в поиске. В какой-то момент механизм защиты от дублей дал сбой и в базу поперли дубли. Некоторые сообщения дублировались по 20 раз.

Я решил проиндексировать вообще все сообщения: загрузка опережала поисковую индексацию на 250 млн сообщений. Удалил индекс со столба с полнотекстовым данными, подождал примерно сутки и решил запускать индексацию (часа 3-4) по моим оценкам.

Через 4.5 часа создание индекса рухнуло с ошибкой "слишком много дублей". И вот я остался с почти терабайтной базой с морем дублей и без рабочего поиска.

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

Нагуглил запрос, который угробит все дубли в таблице. Запустил. Спустя 6 часов PostgreSQL съел 8 Гб оперативки, 10 Гб файла подкачки, после чего ушёл в Out Of Memory.

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

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

А дальше запрос, который удаляет из исходной таблицы все строки, id которых нет во вспомогательной. Молотило оно больше суток, но мусор почистило.

Итого, в базе осталось около 550 млн уникальных сообщений. Дублей было около 250 тысяч (в какой-то момент закачка обновлений сошла с ума и выкачивала вместо обновления всю историю чата повторно в течение двух недель).

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

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

Итого на борьбу с дублями я потратил дней пять. И хорошо, что это - мой личный проект.

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

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

Спасибо за обратную связь!
Палантир. Часть 15. Окончательное решение вопроса дублей.
#палантир@eshu_coding

Несмотря на локальную победу над дублями, они продолжали потихоньку попадать в базу.

В далёкие времена, когда я проектировал базу данных, я собирался использовать составной primary key в таблице с сообщениями: временная метка сообщения, id чата и порядковый номер сообщения. Но что-то пошло не так. Стандартный способ защиты таблицы с помощью триггера "before insert" не годился на секционированной таблице.

Делать проверку внутри хранимой процедуры для записи оказалось медленно: проверка занимает около 10 мс, на каждое из примерно 1000 сообщений, прилетающих каждую секунду. База мигом захлёбывается.

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

Спасибо доброму человеку @vekhden_speak, он подсказал решение, с помощью которого я окончательно поборол дубли. Как окалось, в самом insert-e можно предотвратить конфликт добавив предложение "on conflict on constraint messages_pkey do nothing".

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

#postgresql
Палантир. Часть 16. Клиентская часть для пользователей.
#палантир@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
Егор Рогов из Postgres Professional подробно и доступно рассказывает теорию и практику работы с PostgreSQL:

📌 Индексы

- Механизм индексирования
- Интерфейс метода доступа, классы и семейства операторов
- Hash
- B-tree
- GiST
- SP-GiST
- GIN
- RUM
- BRIN
- Bloom

📌 Изоляция и многоверсионность

- Изоляция, как ее понимают стандарт и PostgreSQL
- Слои, файлы, страницы — что творится на физическом уровне
- Версии строк, виртуальные и вложенные транзакции
- Снимки данных и видимость версий строк, горизонт событий
- Внутристраничная очистка и HOT-обновления
- Обычная очистка (vacuum)
- Автоматическая очистка (autovacuum)
- Переполнение счетчика транзакций и заморозка

📌 Журналирование

- Буферный кеш
- Журнал предзаписи — как устроен и как используется при восстановлении
- Контрольная точка и фоновая запись — зачем нужны и как настраиваются
- Настройка журнала — уровни и решаемые задачи, надежность и производительность.

📌 Блокировки:

- Блокировки отношений
- Блокировки строк
- Блокировки других объектов и предикатные блокировки
- Блокировки в оперативной памяти

📌 Запросы

- Этапы выполнения запросов
- Статистика
- Последовательное сканирование
- Индексное сканирование
- Соединение вложенным циклом, а также будет продолжение про соединение хешированием / слиянием и сортировку

А еще у Postgres Professional есть учебные курсы, которые доступны всем желающим.
Создавая базу знаний, описывающую реальность, какой 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
Палантир. Часть 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
Продолжу затронутую уважаемым @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
Наверное главное открытие прошлого года для меня - оркестрация контейнеров с помощью docker compose (в прошлом посте с его помощью я поднимал тестовую инфраструктуру).

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

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

Даже отдельные контейнеры я тоже стараюсь запускать для отладки через compose: для полноты счастья в нем очень удобный механизм передачи переменных окружения, который в случае стандартного докера откровенно убог и неудобен.
2025/07/07 17:30:14
Back to Top
HTML Embed Code: