Перейти к содержимому
DedicatedPHP Контакт

Когда процесс PHP прерывается на полпути: проектирование компенсирующих действий

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

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

Бизнес-процесс может выполнить часть работы и завершиться с ошибкой до того, как будет закончен. Например, PHP-приложение создаёт учётную запись в своей базе данных, но не может подготовить для неё ресурсы во внешнем сервисе. В такой ситуации бездумный повтор процесса может привести к дублированию эффектов, а признание его неудачным — к несогласованности систем. Компенсации в распределённых процессах на PHP позволяют определить, как реагировать на такие частичные результаты, не предполагая, что несколько систем участвуют в одной атомарной транзакции.

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

Локальные транзакции, повторные попытки и компенсации решают разные задачи

Локальные транзакции, повторные попытки и компенсации решают разные задачи — guía visual de DedicatedPHP

Транзакция базы данных защищает изменения в пределах этой базы данных. Если процесс сохраняет несколько строк, а одна из операций завершается с ошибкой до COMMIT, он может выполнить ROLLBACK, чтобы отменить эти изменения. Однако такой откат не отменит отправленное письмо, автоматически не отменит запрос, уже принятый API, и не отменит подтверждённые изменения в другой базе данных.

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

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

Моделируйте каждую операцию как последовательность наблюдаемых шагов

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

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

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

Не держите транзакцию базы данных открытой во время сетевого вызова. Внешние вызовы могут иметь высокую задержку или зависнуть, а длительная транзакция удерживает ресурсы и не делает две системы атомарными. Фиксируйте локальную работу в соответствии с явно заданной последовательностью и сохраняйте всё необходимое для продолжения процесса или выполнения компенсации.

Проектируйте компенсации как явные, ограниченные и идемпотентные действия

Для каждого шага, создающего эффект, решите, существует ли для него компенсация и когда её следует выполнять. Компенсация не обязательно должна быть буквальной обратной операцией. Если пользователь уже получил уведомление, невозможно удалить факт прочтения сообщения; может потребоваться зарегистрировать исправление или сообщить об отмене. Если действие необратимо либо имеет юридические или финансовые последствия, правильным решением может быть остановка процесса и эскалация.

  • Идемпотентность: повторное выполнение одной и той же компенсации не должно создавать дополнительные эффекты. Используйте стабильный идентификатор операции или ключ идемпотентности, если система это поддерживает, и сохраняйте результат.
  • Порядок: определите, какие шаги компенсируются и в какой последовательности. Обычно компенсацию пытаются выполнять в порядке, обратном порядку выполнения, но зависимости и бизнес-правила могут требовать иного порядка.
  • Ограничения: определите, при каких ошибках допустимы повторные попытки, сколько попыток выполнять и когда остановиться. Делайте паузы между попытками и не допускайте бесконечных повторов.
  • Неопределённость: если истёк тайм-аут API, проверьте состояние или используйте ключ идемпотентности, прежде чем повторять создание объекта или выполнять потенциально неправильную компенсацию.

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

Гипотетический пример: создание учётной записи в двух системах

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

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

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

Если компенсация тоже не удалась: видимость и проверка

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

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

Проверяйте частичные сбои и определяйте необходимость компенсации

Проверяйте частичные сбои и определяйте необходимость компенсации — guía visual de DedicatedPHP

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

  • Сохраняются ли идентификатор операции и результаты каждого шага?
  • Приводит ли повторное сообщение к тому же результату без дублирования эффектов?
  • Проверяется ли состояние во внешней системе, если ответ на запрос неопределённый?
  • Выполняется ли компенсация в предусмотренном порядке и безопасно ли её повторять?
  • Остаётся ли операция видимой при сбое компенсации и направляется ли она на проверку?
  • Можно ли позднее сверить локальное состояние с внешней системой?
  • Проверяют ли тесты бизнес-инварианты, а не только коды ответов?

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

Хотите применить эти идеи в своем проекте?Давайте обсудим вашу PHP-платформу.
Посмотреть связанные услуги