Спасение платформы для запуска рекламы от критических тайм-аутов
Когда бизнес зависит от скорости обработки данных, каждая задержка в работе сервиса может обернуться потерянными деньгами. Именно с такой проблемой столкнулся наш клиент — платформа для запуска рекламных кампаний и анализа эффективности.
Внезапные тайм-ауты, падения серверов и перегруженная база данных делали работу пользователей невозможной. Ситуация требовала срочного вмешательства, но причина сбоя была неочевидной. Рассказываем, как мы нашли узкое место в системе, восстановили её работоспособность и заложили основу для дальнейшей оптимизации.
Этот кейс будет полезен собственникам, которые планируют масштабировать свои сервисы, и IT-директорам, принимающим решение об архитектуре проекта. Кейс поможет увидеть узкое горлышко в архитектуре и заложить возможность роста сервиса на этапе проектирования.
Клиент
Платформа для запуска рекламных кампаний и анализа эффективности.
Задача
В платном digital-продвижении важно не просто запускать рекламу, но и вовремя корректировать стратегию, если она не даёт результатов. Чем дольше проблемы остаются незамеченными, тем больше бюджета утекает впустую. Чтобы избежать этого, маркетологи используют сервисы аналитики, которые помогают отслеживать эффективность кампаний в реальном времени и принимать решения на основе данных.
Один из таких сервисов обратился к нам в SimbirSoft с серьёзной проблемой: их платформа начала заметно замедляться. На тот момент продукту было уже около четырёх лет, и сначала он работал без сбоев. Но с ростом числа пользователей и увеличением объёмов данных начали появляться тревожные сигналы.
Пользователи жаловались, что личный кабинет загружается с задержками, отчёты формируются слишком долго, а иногда процесс и вовсе обрывается ошибками Timeout, 500, 502 или 503. В критические моменты сервис и вовсе мог не загружать часть данных, а рекламодателям приходилось ждать ответа системы слишком долго. Это означало, что они не могли оперативно оценить эффективность своих кампаний, вовремя отключить неработающие объявления или перераспределить бюджет. По сути, деньги, затраченные на рекламу, просто сгорали.
Проблему нужно было решать быстро, иначе сервис рисковал потерять доверие клиентов и деньги. Подобный сервис должен работать 24 на 7, иначе пострадают все участники процесса: владельцы бизнеса, пользователи сервиса, конечные покупатели и пользователи услуг, в частности, если рекламная кампания работает некорректно, они могут получать нерелевантные объявления или видеть их слишком часто.
Как мы обнаружили проблему
Первые сигналы о проблеме начали поступать от пользователей: они жаловались на зависания личного кабинета и сбои в работе системы. Дополнительные тревожные звоночки появились в дашборде “Нагрузка на серверы” и потребление ресурсов заметно возросли.
Команда backend-разработки начала детальный анализ, используя инструменты мониторинга, такие как Grafana и метрики обработки запросов (скорость выполнения запросов, количество ошибок от сервиса, количество запросов в этот момент) и Jaeger (позволяет детально отслеживать путь запроса в распределённой системе, чтобы выявить узкие места и задержки). Кроме этого, использовали Zabbix для мониторинга ресурсов сети и железа. Проблему удалось обнаружить не сразу: нужно было раскопать цепочку вызовов сервисов и проанализировать, какой сервис подвергается сильной нагрузке и по какой причине.
Первое, что бросилось в глаза, — нестабильность в обработке запросов. Тайм-ауты возникали в среднем раз в 4–10 запросов, а в пиковые моменты система работала с перебоями. Интересно, что в выходные тайм-аутов фиксировалось меньше, но эффект был нестабильным: сервис работал в разных часовых поясах, и классического "времени минимальной нагрузки" у него просто не было.
Разбирая метрики и трассировку запросов, мы выявили узкое горлышко — каскадное замедление сервисов. Пока было непонятно, что именно перегружает систему: ресурсы расходовались слишком быстро, а тайм-ауты появлялись без очевидной закономерности. Дальше выяснилось, что один из сервисов в архитектуре влиял на всю систему. Он перегружал главный сервис, из-за чего сложные аналитические запросы выполнялись дольше обычного.
Почему возникла проблема
Эта проблема долгое время оставалась незаметной, потому что на старте у заказчика было мало данных в базе, и всё работало стабильно. Но со временем нагрузка выросла, и система уже не справлялась так же легко, как раньше.
Разобравшись с узким горлышком, мы увидели, что причина проблемы — архитектурное упущение: изначальный просчёт в проектировании сервисно-ориентированной архитектуры (SOA) из-за недостатка компетенций в проектировании распределенных систем, который привёл к каскадному замедлению системы. Когда один из сервисов перегружался, его задержки накапливались, и это цепной реакцией сказывалось на работе всей платформы. Это лишний раз доказывает, как важна экспертиза архитектора при проектировании системы — без неё не стоит начинать.
Кроме архитектурных проблем, в системе обнаружились и неоптимальные моменты в базе данных. В частности, отсутствовали корректные индексы и эффективные структуры данных, из-за чего сложные аналитические запросы выполнялись дольше, чем могли бы.
Такое случается редко. Обычно замедление системы происходит постепенно, и у команды есть время на масштабирование. Но здесь сбои начали проявляться резко, без плавного ухудшения, что делает кейс достаточно нетипичным.
Решение
Проблему нужно было решать быстро и чётко. Ожидания заказчика по срокам реализации — 1 месяц. Получилось 2,5 месяца из-за анализа ситуации и онбординга DevOps-инженера. Но вложения в адаптацию специалиста — ключевой шаг в работе над проектом.
Решение проблемы потребовало поэтапного подхода, чтобы минимизировать риски и избежать дополнительных сбоев. Работали так:
-
Развернули реплицированную базу данных, которая в реальном времени получала копию данных с основного сервиса. Это позволило протестировать изменения, не затрагивая работающую систему.
-
Развернули реплику на новый узел основного сервиса, а следом — копию проблемного сервиса. Таким образом, создали изолированную среду, где мы могли безопасно проверять гипотезы и искать оптимальный способ распределения нагрузки. Реплики обоих приложений развернули на отдельном сервисе с помощью NGINX.
- Плавно перенаправили часть трафика (10-20% пользователей) на новую реплику приложения. Это нужно было, чтобы проверить, как система поведет себя под нагрузкой, и убедиться, что изменения действительно улучшают ситуацию.
Когда тестирование подтвердило работоспособность решения, полностью перевели трафик на новые изолированные реплики. Это позволило снять нагрузку с основного сервиса и стабилизировать работу системы без резких переключений и рисков для пользователей.
При обсуждении сначала предлагали использовать Kubernetes — инструмент, который автоматически распределяет нагрузку, следит за состоянием сервисов и упрощает развертывание обновлений. Он мог бы значительно упростить обновление компонентов системы и управление репликами.
Однако на тот момент сервисы работали без Kubernetes, и его внедрение требовало бы серьёзной перестройки инфраструктуры, что заняло бы слишком много времени. Из-за срочности проблемы от этого варианта пришлось отказаться.
Результат
Чтобы репликация состоялась, предварительно создали контуры и настроили базы данных для распределения нагрузки. Примерно месяц ушло на онбординг DevOps-инженера, настройку инструментов сбора метрик и поиск проблем. Еще 1,5 месяца — на реализацию проекта с последующей оптимизацией работы базы данных и системы в целом.
После перенаправления трафика на изолированные реплики мы провели нагрузочное тестирование всех запросов проблемного сервиса. Оно помогло выявить расхождения между заявленным и фактическим временем обработки данных. На этом этапе мы начали точечно оптимизировать проблемные участки.
В одних случаях решение оказалось простым — добавление индексов, изменение схемы базы данных (например, уменьшение нормализации или замена типа поля). В других потребовалось внедрение кэширования для часто используемых данных, чтобы снизить нагрузку на базу. А в самых сложных случаях пришлось полностью переписывать бизнес-логику, чтобы исключить неэффективные запросы.
Отдельное внимание уделили транзакциям. Мы пересмотрели места, где использовалась блокировка, и либо убрали её, если она была не нужна, либо заменили на оптимистичное конкурентное управление (Optimistic Concurrency Control, OCC), чтобы избежать лишних задержек в системе.
Планы на развитие
Мы продолжили работу по оптимизации хранилища, добавив сегментирование данных для улучшения производительности.
После внесённых изменений сервис стал работать стабильнее, а жалобы пользователей на ошибки и медленную работу прекратились. Однако это было лишь временным решением, позволяющим быстро восстановить работоспособность системы.
В планах — комплексный аудит архитектуры и её перепроектирование. Одним из ключевых шагов будет вынесение критических секций в отдельные сервисы, которые могли бы гарантировать бесперебойную работу и снизить риски каскадных сбоев. Сейчас архитектура осталась без изменений, за исключением временной репликации, но предстоит большая работа по редизайну.
Основные задачи на ближайшую перспективу:
- Рефакторинг проблемного сервиса, который перегружал главный сервис
- Оптимизация кода и хранимых процедур в базе данных
- Рефакторинг общего сервиса, чтобы повысить его стабильность и производительность
- Отдельное внимание уделим внедрению Rate Limiting — механизма ограничения количества запросов в секунду от одного сервиса. Это позволит предотвратить перегрузку системы в будущем и избежать повторения подобных проблем. В сочетании с оптимизацией базы данных такие меры обеспечат стабильность в работе сервиса.
Если вы столкнулись с проблемами, описанными в кейсе, или похожими, и хотите их избежать на старте разработки — прочитайте чек-лист от наших экспертов