Асинхронные задачи позволяют развязать импорт, синхронизацию, уведомления, генерацию документов и интеграции. Однако то, что обработчик обработал сообщение, не обязательно доказывает корректность бизнес-результата. Успех мог быть зафиксирован до подтверждения внешнего эффекта, между двумя шагами мог произойти сбой либо одна и та же операция могла быть выполнена более одного раза.
Сверка асинхронных задач в PHP покрывает этот разрыв: она сопоставляет ожидаемый системой результат с доказательствами того, что произошло, выявляет отсутствие или расхождения и запускает контролируемое исправление. Она не заменяет очередь, повторные попытки или идемпотентность; она дополняет их независимой проверкой.
Техническое выполнение не равно бизнес-результату

Задача может завершиться без исключения и всё же оставить процесс незавершённым. Например, приложение создаёт запрос на синхронизацию, обработчик вызывает внешний API и получает неокончательный ответ из-за обрыва сети. Если он повторит попытку без идемпотентного ключа, он может создать дубликат. Если он предположит успех, запись может остаться несинхронизированной.
Также не следует считать заданной конкретную семантику доставки инфраструктуры обмена сообщениями. Возможность повторной доставки и дублирующихся выполнений зависит от брокера, его настроек персистентности, подтверждений, поведения обработчика и возникающих сбоев. При проектировании необходимо проверять эти свойства в выбранной технологии и, когда возможны дубликаты или изменение порядка, явно учитывать и корректно обрабатывать их.
Операционный вопрос состоит не только в том, «было ли сообщение обработано?», но и в том, «могу ли я доказать, что ожидаемый эффект существует, один раз там, где это требуется, и с правильными данными?». Такое доказательство требует источника свидетельств: доступного для запроса ответа внешней системы, сохранённого удалённого идентификатора, сохранённого документа или подтверждённого изменения состояния.
Повторные попытки, идемпотентность и сверка: разные обязанности
Повторные попытки обрабатывают временные ошибки: временную недоступность, лимиты использования, кратковременные блокировки или сетевые проблемы. Следует определить лимит попыток, возрастающую задержку, классификацию ошибок и место назначения для сообщений, требующих внимания. Бесконечные повторные попытки могут скрыть ошибку данных или усугубить внешний инцидент.
Идемпотентность делает повторное выполнение операции безопасным. Она может быть достигнута с помощью стабильного идентификатора операции, отправляемого внешнему провайдеру, уникального ограничения в базе данных или транзакционной проверки перед эффектом. Это не означает, что эффект произошёл: это означает, что повтор не должен приводить к дополнительным или дублирующим эффектам.
Сверка ищет ожидающие, незавершённые или противоречивые операции и решает, что делать с каждой из них. Она особенно необходима при наличии внешних эффектов, пакетных процессов, обновлений нескольких систем или коммуникаций, получение которых нельзя доказать только со стороны отправляющего приложения.
- Используйте повторные попытки при сбоях, классифицированных как временные.
- Используйте идемпотентность, чтобы повторные попытки или повторная доставка не дублировали эффекты.
- Используйте сверку, чтобы проверить конечное состояние и исправить выявленные различия.
Моделирование операции и хранение проверяемых свидетельств
Поддерживаемый дизайн разделяет три понятия. Запрошенная задача представляет намерение, например «синхронизировать заказ 452». Ожидаемый эффект определяет наблюдаемый результат: «внешняя система содержит заказ с версией 7». Подтверждение хранит свидетельство существования этого результата: удалённый идентификатор, версию, временную метку, валидированный ответ или результат последующего запроса.
До публикации сообщения создайте запись выполнения в долговечной базе данных. Если приложение изменяет собственные данные и публикует сообщение, рассмотрите паттерн outbox: сохраните бизнес-изменение и ожидающее событие в одной транзакции, а публикацию поручите последующему процессу. Это снижает риск зафиксировать локальное изменение и потерять сообщение либо опубликовать сообщение для изменения, которое было отменено.
Запись должна включать как минимум:
- Неизменяемый и уникальный operation_id, используемый для корреляции сообщений, логов и внешних вызовов.
- Тип операции, затронутую сущность и версию или отпечаток ожидаемого содержимого.
- Текущее состояние, число попыток, время следующей разрешённой попытки и временные метки.
- Идемпотентный ключ и, если он существует, идентификатор удалённого ресурса.
- Краткое свидетельство и безопасные ссылки на ответы или ошибки, без записи секретов или ненужных персональных данных.
- Причину закрытия, компенсации, отбрасывания или эскалации на ручную проверку.
Определите явные переходы, например: pending, processing, awaiting_confirmation, confirmed, retry_scheduled, manual_review, compensated и not_applicable. Для каждого перехода должны быть определены свой ответственный и проверяемое условие. Условное обновление, например переход в processing только если предыдущее состояние — pending, уменьшает гонки между обработчиками.
Построение процесса сверки
Сверка может выполняться запланированной PHP-командой, выделенным рабочим процессом или операционным процессом. Она должна работать с временными окнами: не проверяйте операции, созданные несколько секунд назад, если внешней интеграции обычно требуется несколько минут. Определяйте окно на основе реальных данных о задержке и пересматривайте его при изменении лимитов или провайдеров.
Для каждой подходящей операции сопоставляйте заранее определённые источники истины. Локальная база может быть авторитетным источником для намерения и версии данных; внешняя система — для того, получила ли она или создала ли ресурс. Когда надёжный запрос к внешней целевой системе отсутствует, свидетельством может быть подписанное подтверждение, идентификатор провайдера или отложенная проверка по файлу результатов.
- Выберите неподтверждённые операции, превысившие ожидаемый срок.
- Проверьте существование эффекта, используя
operation_id, идемпотентный ключ или однозначный бизнес-ключ. - Сравните релевантные поля и версии, а не только существование ресурса.
- Классифицируйте случай как отсутствующий, корректный, расходящийся, неоднозначный или неприменимый.
- Выполните разрешённое действие и сохраните решение вместе с его свидетельствами.
Неоднозначный результат не должен автоматически превращаться в повторную постановку в очередь. Если вызов мог создать ресурс, но нет способа надёжно запросить его, повторная попытка может продублировать списание, уведомление или документ. В таких случаях заблокируйте автоматическое действие и направьте случай в панель исключений с достаточным контекстом для принятия решения.
Исправление без внесения нового ущерба
Действие зависит от расхождения и стоимости ошибки. Повторно поставить в очередь уместно, когда эффект отсутствует и операция идемпотентна. Компенсировать может отменить некорректный эффект посредством явной бизнес-операции, а не неизбирательным техническим удалением. Пометить для проверки предпочтительно при неоднозначности, конфликте версий или финансовых последствиях. Закрыть как неприменимое подходит, когда сущность была отменена или заменена в соответствии с документированными правилами.
Ручные исправления также должны оставлять след: кто принял решение, какие свидетельства он изучил, какое действие применил и каким был результат. Ограничьте права и избегайте кнопок, выполняющих операцию без показа сущности, версии, внешней целевой системы и риска дублирования.
Наблюдаемость и тесты, подтверждающие дизайн
Логи, коррелированные по operation_id, упрощают отслеживание операции между веб-интерфейсом, рабочими процессами и внешними сервисами. Полезные метрики не ограничиваются исключениями: измеряйте возраст ожидающих операций, количество операций на ручной проверке, частоту расхождений, повторные попытки по причинам и время до подтверждения. Оповещения должны срабатывать из-за накопления, возраста или нарушения срока, а не из-за каждой отдельной ошибки.
Проверяйте характерные сбои: сбой после внешнего эффекта и до сохранения подтверждения; дублирующее выполнение; сообщение вне порядка; перезапуск рабочего процесса; тайм-аут с неопределённым удалённым результатом; длительную недоступность; и изменения версии, пока операция остаётся ожидающей. Тест должен проверять как конечное состояние, так и отсутствие дубликатов и качество сохранённых свидетельств.
Пример: синхронизация записи с внешней системой
Предположим, PHP-приложение синхронизирует запись клиента. При её изменении оно создаёт операцию sync_customer со стабильным идентификатором и ожидаемой локальной версией. Рабочий процесс отправляет эти значения во внешнюю целевую систему как идемпотентный ключ. Если он получает валидное подтверждение, он сохраняет удалённый идентификатор и меняет состояние на confirmed.
Если тайм-аут происходит после отправки запроса, рабочий процесс оставляет операцию в awaiting_confirmation. Компонент сверки запрашивает внешнюю целевую систему по идемпотентному ключу. Если он находит ту же версию, он подтверждает. Если не находит, он планирует новую отправку. Если он находит другую версию, он помечает случай для проверки вместо перезаписи данных, которые могли быть правомерно изменены в другой системе.
Чек-лист для внедрения сверки без переписывания процесса

- Проведите инвентаризацию существующих задач и отдайте приоритет тем, что создают внешние эффекты, затрагивают деньги, регуляторные данные или процессы, которые сложно повторить.
- Для каждого типа задокументируйте ожидаемый эффект, источник истины и свидетельство, которое позволит его подтвердить.
- Проверьте в конкретных брокерах и обработчиках, что происходит при сбоях, поздних подтверждениях, персистентности, повторной доставке и порядке сообщений.
- Добавьте стабильный
operation_idи передавайте его в сообщении, логах, внешних вызовах и записях состояния. - Добавьте таблицу операций с состояниями, попытками, сроками, идемпотентным ключом и свидетельствами; при необходимости начните в режиме наблюдения.
- Определите условные переходы и письменную политику для повторной попытки, подтверждения, компенсации, эскалации или закрытия как неприменимого.
- Реализуйте компонент сверки, ограниченный временным окном и одним пилотным типом операции.
- До автоматизации исправлений проверьте сценарии с дубликатом, неоднозначным тайм-аутом, сбоем между шагами, изменением порядка и перезапуском.
- Создайте панель или запрос исключений с возрастом, сущностью, свидетельствами и рекомендуемым действием.
- Периодически пересматривайте метрики, зависшие операции и ручные решения, чтобы корректировать сроки, правила и контроли.
Постепенное внедрение позволяет повысить надёжность без замены всей архитектуры: сначала сделайте неопределённые операции видимыми, затем подтверждайте результаты и, наконец, автоматизируйте только те исправления, безопасность которых можете доказать.



