Почему Google Pub/Sub теряет сообщения при пиковой нагрузке
Потеря сообщений редко выглядит как ошибка в логах — чаще это тишина: обработчик работает, метрики ровные, а часть данных просто не доходит. Подписка хранит сообщение ограниченное время и не ждёт бесконечно: всё, что не подтверждено в срок, удаляется без возврата в очередь.
Первое узкое место — переполнение подписки. На пиковой нагрузке публикация обгоняет обработку: неподтверждённые сообщения и повторы накапливаются, счётчик ожидающих растёт, и обработчик получает больше, чем способен принять. Сообщения не лежат в очереди вечно — хвост всплеска вытесняется раньше, чем до него дойдёт дело.
Второе — таймаут подтверждения. Обработчик забирает сообщение, уходит во внешний вызов или тяжёлый запрос и не успевает подтвердить его в отведённое окно. Сервис считает доставку неуспешной и присылает копию: растут дубли и счётчик доставок, а при отсутствии политики повторов сообщение уходит в очередь недоставленных или стирается молча.
Третий сценарий — «залипшие» обработчики: процесс жив, метрики в норме, но подтверждение не отправляется — потерян контекст, поток заблокирован, контейнер уходит в перезапуск. Сообщение ходит по кругу, пока не истечёт срок хранения, и теряется ровно там, где нужна была диагностика.
Все три сценария сходятся в одной точке — конфигурация подписки, недоставленных сообщений и прав доступа к ним. Разбираем, где именно рвётся поток и как это закрывается.
Измеримый эффект: меньше потерь, дублей и простоев
Настройка подписки меняет то, что видно в метриках. До неё сбой обработчика выглядит как тишина: сообщение доставлено, подтверждено, обработчик упал — и события нет ни в системе, ни в очереди. После настройки всё, что не прошло обработку за заданное число попыток, уходит в dead-letter: они остаются в очереди разбора, а не исчезают.
Дубли — вторая статья потерь. Продлённое окно подтверждения вместе с идемпотентностью по ключу события убирают повторную обработку там, где она не нужна, а оставшиеся повторы становятся безопасными: повторная доставка не создаёт вторую запись и не проводит второе списание. Объём повторной обработки падает, потому что часть сбоев закрывается на стороне подписки.
Третий эффект — время восстановления. Инцидент обнаруживается не по обращению клиента, а по алерту на возраст самого старого неподтверждённого сообщения и на рост dead-letter: дежурный видит проблему в начале, а не в конце. Возврат в работу — переключение подписки на резервный обработчик, это минуты, а не часы.
Уходит целый класс инцидентов: «тихая потеря события», «двойная обработка», «очередь росла сутки, и никто не заметил». Наблюдаемость потока строится на трёх сигналах — возраст неподтверждённых сообщений, число доставок на сообщение, объём dead-letter — и каждый привязан к конкретному действию дежурного. Работаем в инфраструктуре Google Cloud Pub/Sub: правила повторов, права доступа и пороги алертов задаются декларативно и версионируются вместе с кодом сервиса.
Режимы Google Pub/Sub: подписки, подтверждения, повторы
В Google Pub/Sub режим доставки кажется деталью, пока поток один. Как только потребителей становится несколько, он задаёт, какие сбои вы получите: выталкивающая подписка приходит на ваш адрес и требует быстрого ответа, вытягивающая — забирает сообщения сама, поэтому темп, размер пакета и окно обработки подбираются под нагрузку.
Отдельно решаются порядок и подтверждение. Ключ упорядочивания привязывает сообщения к одной очереди — порядок держится внутри ключа, но параллелизм падает. Подтверждение обработки продлевает окно: не успел потребитель — сообщение вернётся в доставку, а после заданного числа попыток уйдёт в очередь недоставленных.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Вытягивающая подписка | свои сервисы, пакетная выборка, высокий темп | нужен цикл опроса и своя логика подтверждений |
| Выталкивающая подписка | события во внешние сервисы, функции без постоянного потребителя | нужен доступный адрес и быстрый ответ, окном подтверждения не управляете |
| Потоковая обработка | непрерывный поток с минимальной задержкой | постоянное соединение, сложнее приостановить и откатить |
| Несколько подписчиков | один поток на разные команды и сценарии | политики повторов и недоставленных у каждой свои, дубли гасит идемпотентность |
| Ключ упорядочивания | важен порядок внутри одной сущности | меньше параллелизма, горячий ключ становится узким местом |
Режим доставки определяет класс отказов, который вы потом разбираете на дежурстве. Ниже — как связываем подписки, права и мониторинг в одну управляемую схему.
Порядок внедрения: от аудита потоков до передачи регламентов
Порядок работ строим так, чтобы каждый шаг заканчивался проверяемым артефактом — схемой, конфигурацией или отчётом о проверке. Начинаем не с настроек, а с аудита потоков: ошибку в схеме обмена сообщениями проще поймать на бумаге, чем в проде под пиковой нагрузкой.
- Аудит потоков. Инвентаризация топиков и подписок Pub/Sub: кто публикует, кто читает, какая задержка допустима, где ошибки не всплывают в метриках.
- Проектирование схемы. Раскладываем потоки по критичности, фиксируем топологию топиков и подписок, срок хранения и отдельный dead-letter-топик для необработанных сообщений.
- Настройка повторов. Политика повторов с задержкой между попытками, число доставок до отправки в dead-letter, поведение при отзыве подтверждения: сбойное сообщение уходит в сторону, а поток продолжает идти.
- Доступы. Сервисные учётные записи и роли по минимально необходимому набору прав: подписчик не публикует, сборщик метрик не читает тела сообщений.
- Проверка на отказах. Отключаем подписчика, отзываем права, ломаем обработчик — смотрим, что происходит с очередью, срабатывают ли повторы, виден ли инцидент в мониторинге.
- Нагрузочная проверка. Гоняем профиль, близкий к пиковому: сколько держит поток без роста задержки, как растёт объём необработанных сообщений, на каком темпе начинаются массовые повторы.
- Мониторинг и передача регламентов. Алерты на возраст самого старого неподтверждённого сообщения, долю сообщений в dead-letter и ошибки публикации; инструкции для дежурной смены и порядок разбора инцидентов.
Комплект поставки: схемы потоков, политики доступа, регламенты
Комплект поставки — это не презентация, а рабочая документация, по которой ваша дежурная смена и инженеры действуют без нас. Все настройки Pub/Sub мы фиксируем в вашем репозитории: схемы, политики и регламенты версионируются, изменения проходят ревью, а не живут в чьей-то памяти или переписке.
- Схема потоков: топология топиков и подписок, кто публикует и кто читает, какие сообщения считаются критичными и какой маршрут проходят при повторных попытках.
- Разделение окружений: тестовый, предпродуктивный и боевой контуры живут в отдельных проектах с независимыми правами и квотами — нагрузочный тест не может вычитать боевые данные.
- Политики доступа: роли на уровне конкретных топиков и подписок, отдельная учётная запись на каждый сервис, принцип минимальных прав вместо одного общего ключа для всех.
- Пороги оповещений: рост числа необработанных сообщений, возраст самого старого из них, доля ошибок доставки и задержка публикации — с указанием нормы и условия вызова дежурного.
- Инструкции для дежурных: пошаговые сценарии на каждый тип сбоя — что смотреть первым, что безопасно перезапустить, когда эскалировать и кому.
- Регламент повторных попыток: сколько раз и с каким интервалом повторяется доставка, при каком числе отказов сообщение уходит в отдельную очередь и как её разбирать.
- Карта владения и порядок изменений: кто отвечает за топик, через какой процесс проходит правка политик и как откатить неудачное изменение за один шаг.
Кейс: потери событий в расчётном контуре — динамика до и после
Поток расчётных событий шёл через шину без разделения приёма и обработки: подписчик подтверждал сообщение сразу после получения, а бизнес-логика отрабатывала следом. Ошибка внешнего модуля или таймаут приводили к тому, что событие считалось доставленным и исчезало — расхождение по сверке всплывало только на следующий день, а поиск причины шёл вручную по логам.
Мы перестроили подтверждения: приём фиксируется отдельно от результата обработки, неудачные попытки уходят в повторную доставку с нарастающей задержкой, а те, что не прошли за отведённое число попыток, попадают в очередь недоставленных сообщений с сохранением исходного тела и заголовков. Доступ сервисов к топикам и подпискам развели по отдельным ролям — учётная запись загрузчика больше не может читать и удалять чужие подписки.
Динамика по метрикам
| Метрика | До работ | После наблюдения |
|---|---|---|
| Доля потерянных расчётных событий | около 2% в сутки | ниже 0.05% |
| Обнаружение сбоя обработки | на утренней сверке | по алерту, минуты |
| Ручной разбор инцидента | разбор всей пачки логов | по идентификатору сообщения |
| Откат на предыдущую схему обработки | перезапуск контура | переключение версии подписчика |
Контур работает на Google Cloud Pub/Sub, и ключевым оказалось не само включение повторов, а согласование окна хранения недоставленных сообщений со временем реакции дежурной смены: если оно короче, письма теряются уже на выходе из очереди. По итогам месяца наблюдения ни один расчётный день не закрылся с расхождением, требующим ручной правки проводок.
Чем отличается недоставленное сообщение от повторного
Граница проходит не по факту неудачной обработки, а по счётчику попыток. Пока предел попыток не исчерпан, сообщение остаётся в подписке и возвращается подписчику повторно — это штатный режим повторной доставки, а не ошибка. Платформа считает сообщение «в работе» до подтверждения, и если подтверждение не пришло за отведённое окно, возвращает его в очередь выдачи с увеличенным номером попытки.
Политика повторов задаёт, как именно это происходит: интервал между попытками и его рост от попытки к попытке. Нарастающая задержка нужна, чтобы падающий сервис не получал поток задач быстрее, чем успевает восстанавливаться, а соседние подписки не забивали общий ресурс. Пока ошибка временная — недоступность зависимости, сетевая заминка, всплеск нагрузки — нескольких циклов хватает, и сообщение уходит в работу.
Отравленные сообщения ведут себя иначе: обработчик падает на каждом повторе по одной и той же причине. Невалидная схема, ссылка на удалённый объект, нарушение инварианта данных — такие сбои детерминированы, и наращивать попытки бессмысленно. Признак ровно один: одинаковый класс ошибки при разных попытках.
Когда счётчик доходит до предела, платформа обязана выбрать: удалить сообщение или отправить его в отдельную очередь разбора. Очередь разбора — это не архив и не свалка, а рабочее место дежурного инженера: туда попадает только то, что требует ручного решения или правки данных. Если её не завести, платформа отбросит сообщение, и следов не останется.
Практический вывод: повторная доставка лечит временные отказы, очередь разбора сохраняет неисправимое автоматически. Настраивайте срок хранения и уведомления в очереди разбора — иначе она сама станет отложенной потерей данных.
Технические сомнения: задержки, дубли, разграничение прав
Задержки доставки — не сбой, а свойство асинхронного обмена. В Google Cloud Pub/Sub путь от публикации до обработки зависит от размера батча, окна подтверждения и скорости самого обработчика. Если подписчик отвечает дольше, чем истекает окно, брокер считает сообщение необработанным и отдаёт его повторно — так появляются дубликаты.
Схему строим так, чтобы повтор был безопасен: идемпотентные операции, ключ дедупликации внутри сообщения, отдельная очередь для того, что не удалось разобрать за заданное число попыток. Права раздаём по сервисным аккаунтам — публикатор только публикует, подписчик только читает и подтверждает, административные роли приложениям не выдаём. Оповещения вешаем на глубину очереди и возраст самого старого неподтверждённого сообщения, а не на счётчик ошибок в логах: именно эти метрики показывают расход ресурсов и отставание обработки.
Сообщения приходят повторно — это ошибка настройки?
Нет, базовая гарантия — доставка минимум один раз. Дубликаты снимаются на стороне обработчика: ключ дедупликации и проверка состояния перед записью. Упорядочивание по ключу включаем там, где порядок важен, но идемпотентность оно не заменяет.
Как понять, что дело в правах, а не в коде?
Отказ публикации и пустая выборка подписчика выглядят похоже. Мы разводим их по журналу доступа: раздельные роли на топик и на подписку сразу показывают, где именно отказ. Доступ выдаём по минимуму необходимого, поэтому расширять его для отладки не приходится.
Настройки влияют на расход ресурсов?
Да. Размер батча, окно подтверждения и срок хранения недоставленных сообщений определяют объём трафика и хранения напрямую. Подбираем их под профиль нагрузки, а не оставляем по умолчанию, и страхуем оповещениями на рост очереди.
Обсудим вашу схему обмена сообщениями и точки отказа
Чтобы вернуться с оценкой и планом, а не с общими словами, нам нужна ваша текущая картина обмена сообщениями. Достаточно выгрузки конфигурации топиков и подписок, схемы потоков между сервисами и описания того, что сейчас происходит с сообщением, которое потребитель не обработал.
- Список топиков и подписок, их назначение, кто выступает издателем и кто потребителем.
- Как настроены повторы и очередь недоставленных сообщений: после какого числа попыток сообщение уходит в dead-letter и кто разбирает её содержимое.
- Роли и права доступа: какие учётные записи публикуют и читают, где полномочия выданы шире, чем требуется.
- Что уже собирается в мониторинге: задержка доставки, объём неразобранной очереди, алерты и кому они приходят.
- Известные точки отказа: всплески нагрузки, дубли, потеря сообщений при перезапуске потребителя, ручные разборы инцидентов.
- Требования к порядку работ: что должно работать без простоя и какие изменения допустимы в рабочее время.
Обсудим задачу в формате разбора: пройдём по схеме обмена, отметим узкие места и точки отказа, зафиксируем, что делаем в первую очередь. Опишите вашу ситуацию — вернёмся с оценкой и планом работ, где будут этапы, зоны ответственности и способ проверки результата.







