Serverless-функция может запускаться собственным результатом: обработка файла пишет новый объект в тот же watched prefix, обновление записи вызывает тот же change stream, а webhook отвечает действием, которое рождает повторное событие. Цикл быстро увеличивает расходы, нагрузку и число побочных операций. Сначала нужно остановить источник повторов, не потеряв исходные события.

Ограничьте reserved concurrency или временно отключите trigger, сохранив очередь and logs для расследования. Найдите общий correlation ID и цепочку parent event to child event. Не удаляйте все сообщения до классификации и не включайте trigger обратно без защиты от самопорождения.

Что проверить в первую очередь

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

  • Проверьте invocation count, concurrency, error rate и источник trigger.
  • Сравните входной resource and output resource функции.
  • Найдите retry policy, max attempts, DLQ and event age.
  • Проверьте, сохраняется ли original event ID и обрабатывался ли он ранее.

Почему возникает проблема

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

  • Функция пишет результат в тот же bucket prefix, который слушает trigger.
  • Обновление служебного поля снова соответствует фильтру change event.
  • Ошибка после внешнего эффекта вызывает повтор всей функции без идемпотентности.
  • Два сервиса взаимно вызывают webhooks при каждом обновлении статуса.
  • Poison message возвращается из DLQ в основную очередь автоматически.

Пошаговая диагностика

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

  • Постройте граф нескольких последовательных event ID and resource versions.
  • Проверьте trigger filters и фактические пути or event types.
  • Сравните время внешнего эффекта с моментом исключения функции.
  • Запустите один сохраненный event в изолированном тестовом окружении.
  • Посчитайте уникальные бизнес-операции и общее число invocations.

Как разорвать рекурсивную цепочку

Источник и результат должны различаться по namespace, типу события или явному признаку origin. Кроме фильтра необходима идемпотентность: повтор одного исходного event не должен повторять необратимое действие.

  • Trigger принимает только входной prefix or event type.
  • Output записывается в отдельный prefix и содержит origin metadata.
  • Idempotency store фиксирует event ID and business operation до эффекта.
  • Retry ограничен, а постоянные ошибки переходят в quarantine or DLQ.

Как исправить проблему

Исправление делите на небольшие обратимые изменения. Сначала устраните подтвержденную причину, затем повторите исходный сценарий и проверьте соседние функции. Массовое обновление данных запускайте на ограниченной выборке с отчетом и только после сверки расширяйте на весь объем. Не отключайте авторизацию, проверку сертификатов, валидацию или аудит ради быстрого исчезновения симптома.

  • Разделите input and output prefixes or event types.
  • Добавьте фильтр, который исключает записи, созданные самой функцией.
  • Сохраните idempotency key и результат внешней операции.
  • Ограничьте retries, event age and concurrency, настройте DLQ с ручным replay.
  • После исправления повторяйте сохраненные события небольшими пакетами и следите за invocation ratio.

Безопасный порядок внедрения

  • Сохраните затрагиваемые данные, конфигурацию и текущие журналы, затем проверьте возможность реального восстановления.
  • Повторите проблему на тестовом объекте без реальных списаний, рассылок и необратимых изменений клиентских данных.
  • Зафиксируйте изменение в системе контроля версий или журнале работ вместе с причиной, ожидаемым эффектом и планом отката.
  • Проведите тест на нормальном сценарии, ошибочном вводе, повторном запросе, параллельной операции и временной недоступности зависимости.
  • После выпуска наблюдайте полный пользовательский путь, логи и метрики, а не только один успешный запрос.

Как проверить результат

Разовый успешный тест недостаточен. Повторите операцию, проверьте крайние значения, одновременные действия и восстановление после перезапуска или временного сбоя. Для важного сценария сохраните автоматический тест либо короткий регрессионный чек-лист. Итог должен подтверждаться не только интерфейсом, но и состоянием базы, очереди, внешнего сервиса и журналом действий.

  • Один входной объект создает ожидаемое число вызовов и один результат.
  • Повтор того же event ID не повторяет платеж, письмо или запись.
  • Ошибка зависимости переходит в ограниченный retry, затем в DLQ.
  • Включение trigger не вызывает резкого роста concurrency and costs.

Типичные ошибки при исправлении

  • Только увеличивать timeout or memory функции.
  • Удалять trigger и очередь без сохранения исходных событий.
  • Фильтровать по filename в коде после запуска, продолжая платить за все invocations.
  • Считать at-least-once доставку редкой аномалией и не делать эффекты идемпотентными.

Как предотвратить повторение

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

  • Проверяйте event graph и output destinations при code review.
  • Устанавливайте budgets, concurrency limits и алерты invocation anomaly.
  • Тестируйте повтор одного события и сбой после внешнего эффекта.
  • Поддерживайте runbook остановки trigger, анализа DLQ и контролируемого replay.

Что контролировать после выпуска

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

Что подготовить для технического разбора

  • Описание ожидаемого и фактического поведения с точной последовательностью действий.
  • Время проблемы, идентификатор тестового объекта и версии затронутых компонентов.
  • Фрагменты журналов до и после ошибки без секретов и персональных данных.
  • Перечень последних изменений и уже выполненных проверок.
  • Безопасный доступ к тестовой среде либо способ воспроизвести сбой без влияния на клиентов.

Частые вопросы

Можно ли просто отключить повторные попытки?

Это остановит часть циклов, но приведет к потере временно ошибочных событий. Сначала исправьте самопорождение и идемпотентность, затем настройте ограниченный retry.

Как понять, что цикл окончательно устранен?

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

Когда нужна помощь специалиста

Если serverless-функция зациклилась и быстро расходует лимиты, я могу безопасно остановить trigger, найти рекурсивную связь, внедрить идемпотентность и восстановить очередь без повторных эффектов.