Неделя 125. Великое подключение

Неделя 125. Великое подключение

Разбор этой статьи

AI-подкаст BotsellerНеделя 125. Великое подключение
0:00 / 0:00

Эту тему разобрали в подкасте. Слушай параллельно с чтением.

314 коммитов. 13 репозиториев. Одна предновогодняя неделя.

22 декабря 2025 года мы начали операцию, которую потом между собой назвали «Великое подключение». Цель: связать все сервисы Botseller единой системой наблюдаемости. За шесть дней мы подключили трассировку, сломали прод, починили прод, обнаружили, что трассировка создала собственный баг, параллельно запустили абонементы и два платёжных провайдера. И закрыли критическую уязвимость.

314 коммитов, 13 репозиториев, 10 затронутых сервисов, 174 активных бота. Аномалия пятницы: 87 коммитов за один день.

Пятница 26 декабря стала рекордным днём: 87 коммитов. Предновогодняя пятница. Когда весь мир закупает оливье, мы деплоим мониторинг.

Великое подключение

К декабрю 2025-го Botseller обслуживал 174 активных бота. Это было достаточно много, чтобы не знать точно, что происходит внутри. Каждый сервис писал логи, но связи между ними не было.

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

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

Идея была простой: собрать все данные в одно место. Трейсы, логи, метрики. Из всех сервисов. Через единый коллектор.

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

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

22 декабря мы начали с баз данных и кэша. 24 декабря добавили буферный коллектор, который решил проблему зависимости сервисов от порядка запуска. 25 декабря подключили трассировку очередей сообщений. 26 декабря случился массовый деплой: дашборды, корреляция трейсов с логами, фронтенд-мониторинг. В этот день хранилище трейсов упало из-за нехватки памяти при 174 ботах. Мы увеличили память до 1.5 ГБ и продолжили.

27 декабря отлаживали корреляцию: единый формат имён сервисов, лимиты потоков, извлечение идентификаторов из логов.

Цепная реакция микросервисов: 1 изменение инфраструктуры = 5-8 обновлений сервисов. Внедрение трассировки затронуло 10 сервисов одновременно.

За неделю мы затронули 10 сервисов одновременно. Каждое изменение в инфраструктуре порождало волну обновлений в 5-8 сервисах. Это была цепная реакция, но управляемая.

Когда трассировка создаёт собственные баги

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

Инцидент 1: когда инструмент ломает систему. Telegram-бот получает сообщения потоком, а не отдельными HTTP-запросами. Инструментация назначила один глобальный идентификатор сотням разных сообщений.

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

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

Зная будущее, я вижу в этом закономерность. Любой инструмент, который ты добавляешь в систему, сам становится частью системы. И может её сломать. Наблюдаемость не исключение. Она не бесплатна и не безопасна. Её нужно тестировать так же тщательно, как продуктовый код.

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

HOTFIX: одно переименование, один час простоя

22 декабря мы решили унифицировать имена контейнеров. Локальные и продовые должны были совпадать. Логичное решение: меньше путаницы, проще деплой.

Инцидент 2: один час простоя из-за переименования. Унификация имён контейнеров через 6 репозиториев. Прод упал. Переименование в микросервисах - это не рефакторинг. Это миграция.

Мы переименовали контейнер фронтенда. Изменение в шести репозиториях одновременно. Задеплоили.

Прод упал.

Причина: другие сервисы ссылались на контейнер по старому имени. Переименование разорвало связи, которые мы не учли. Мы откатили в тот же день. Контейнер получил обратно старое имя. Урок: даже косметическое изменение в инфраструктуре из шести сервисов может уронить всё. Переименование - не рефакторинг. Это миграция.

Критическая уязвимость: утечка данных

Инцидент 3: утечка данных и IDOR. Один пользователь мог видеть ботов другого через API. Незаметная при малом количестве пользователей, но критически опасная при 174 ботах.

В тот же день, 22 декабря, мы закрыли уязвимость в фильтрации ботов. Один пользователь мог видеть ботов другого пользователя через API. Классическая IDOR-уязвимость, которая существовала незаметно, пока количество пользователей было маленьким.

При 174 активных ботах это уже было опасно. Фикс занял один коммит, но нашёлся он не случайно. Именно работа над наблюдаемостью заставила нас пересмотреть, что и кому отдаёт API.

Абонементы: от нуля до рабочего состояния за два дня

Параллельный трек: абонементы за 2 дня. UI, привязка к клиенту, валюты, остаток визитов, автоматизация записи. 48 часов (23-24 декабря).

23 и 24 декабря, параллельно с «Великим подключением», мы построили систему абонементов. Полностью: страница выбора, привязка к клиенту, выбор валюты, остаток визитов, продление, автоматическое создание записи при оплате.

Зачем это было нужно прямо сейчас. Один из клиентов запускался в январе. Ему нужны были абонементы для своего бизнеса. У нас было два дня до того, как команда уйдёт на новогодние. Мы успели.

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

Два платёжных провайдера для двух рынков

Диверсификация рынков: два эквайринга. Латвийский провайдер (инвойсы, уникальные ссылки) и российский провайдер (полная интеграция, уведомления). Один провайдер не может обслужить оба рынка.

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

Латвийский провайдер: инвойсы, генерация платёжных ссылок, исправление ошибки с кэшированием ссылок (каждый клиент должен получать свою, а не предыдущую).

Российский провайдер: полная интеграция логики оплаты, уведомления клиенту при каждой транзакции.

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

Урок недели: грязная работа - самая ценная

Сейчас, оглядываясь на неделю 125, я вижу её иначе, чем тогда.

Иллюзия «грязной работы»: хаос недели 125 стал фундаментом для чистой раскатки недели 126. Сломанный прод, баги в трассировке и падения хранилища были необходимой платой за масштабирование.

Тогда это были шесть дней хаоса. Сломанный прод. Баг в инструменте для поиска багов. Crash loop хранилища при 174 ботах. Шесть репозиториев, обновлённых одновременно. 87 коммитов в пятницу перед Новым годом.

Сейчас я вижу, что именно эта грязная работа дала фундамент для всего, что произошло дальше. Неделя 126 была чистой раскаткой трассировки на все сервисы. Но чистой она стала только потому, что на неделе 125 мы прошли через все грабли: научились буферизовать данные, починили корреляцию, нашли баг с одинаковыми идентификаторами, увеличили память под нагрузку 174 ботов.

Фреймворк грязной работы: хаос неизбежен (планируйте откат), волновой эффект (1 изменение = 5-8 обновлений), инструмент = часть системы (мониторинг нужно мониторить).

Фреймворк грязной работы:

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

Волновой эффект. Одно изменение в инфраструктуре порождает обновления в 5-8 сервисах. Это не баг процесса, это природа микросервисов. Если вы к этому не готовы, вы не готовы к инфраструктурным изменениям.

Инструмент = часть системы. Всё, что вы добавляете для мониторинга, само становится источником багов. Трассировка создала баг с одинаковыми идентификаторами. Хранилище трейсов упало из-за нехватки памяти. Мониторинг нужно мониторить.

Что дальше

Окупаемость инвестиций: через две недели CASCADE случайно стирает 700 тысяч записей. Трассировка, рождённая в хаосе недели 125, спасает систему от слепого восстановления.

На следующей неделе, неделе 126, мы завершим «Великое подключение» чистой раскаткой трассировки на все пять сервисов. А через неделю после этого CASCADE стирает 700 тысяч записей. И трассировка, рождённая в хаосе недели 125, спасёт нас от слепого восстановления.

Иногда самая грязная неделя в истории продукта оказывается самой важной.

Иногда самая грязная неделя в истории продукта оказывается самой важной.

FAQ

Почему 314 коммитов за одну неделю?

60% коммитов связаны с внедрением наблюдаемости. Каждое изменение в инфраструктуре мониторинга порождало обновления в 5-8 сервисах, создавая волновой эффект. Плюс параллельно шла работа над абонементами и платёжными провайдерами.

Как трассировка может сама создать баг?

Трассировка добавляет новый код в систему. Этот код работает с контекстом запросов, идентификаторами, логами. Если контекст устроен не так, как ожидалось (например, в Telegram-боте сообщения приходят потоком, а не отдельными запросами), инструментация может назначить один идентификатор всем сообщениям. Результат: вместо точной карты запросов вы получаете бесполезную кашу.

Зачем было нужно два платёжных провайдера одновременно?

Botseller работал с клиентами в разных странах. Латвийскому клиенту нужен латвийский эквайринг, российскому - российский. Один провайдер не может обслужить оба рынка.

Как переименование контейнера может уронить прод?

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

Сколько ботов обслуживал Botseller к декабрю 2025 года?

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

Что такое IDOR-уязвимость?

IDOR (Insecure Direct Object Reference) - это когда один пользователь может получить доступ к данным другого, просто изменив идентификатор в запросе. В нашем случае один пользователь мог видеть ботов другого через API. Опасная уязвимость, которая часто остаётся незамеченной, пока пользователей мало.