Cross-chain сообщения: идемпотентная обработка
Источник сообщения установлен: локальный транспорт разрешён, исходная сеть и отправляющий экземпляр соответствуют досье библиотеки. Однако одна подлинная полезная нагрузка может быть наблюдена повторно. В уроке добавим однократное применение объявления и отделим идентичность доставки от идентичности редакционного решения.
Результат — модель журнала приёмника курса 17. Это самостоятельное расширение MirrorLedger, а не новый метод базового CourseCatalog. Приведённый фрагмент объясняет изменение состояния; реальный транспортный адаптер и проверки не запускались.
Две разновидности повтора
Первый повтор — та же транспортная идентичность пришла ещё раз. Причиной может быть повторное наблюдение, ручная сверка или предусмотренная процедура транспорта. Второй повтор — другое сообщение переносит то же прикладное объявление. Наличие нового messageId не делает старую редакцию новым содержанием. Поэтому одного множества messageId может быть недостаточно для всех бизнес-правил.
Наша модель хранит обработанную транспортную идентичность и текущую редакцию каждого курса. Первое препятствует повторному действию того же сообщения. Второе определяет порядок и допустимость редакционного обновления. Если нужен исторический журнал всех редакций, его вводят отдельно; текущая запись не обязана исполнять эту задачу автоматически.
Область транспортного ключа
Ключ повтора имеет определённый контекст. Для универсальной модели можно включить источник, отправляющий экземпляр, целевой экземпляр и транспортную идентичность согласно его гарантиям. Не следует считать произвольную строку messageId глобально уникальной во всех будущих механизмах. Если конкретный транспорт даёт более сильное правило, адаптер использует его явно.
При построении собственного хеша применяют однозначное кодирование полей. Склейка произвольных строк без границ способна смешать разные входы. Такой служебный хеш относится к идентичности доставки, а не к SHA-256 манифеста. Досье называет оба предмета, чтобы не подставить один digest вместо другого.
транспортный контекст + messageId → ключ обработанного сообщения
courseId + edition → идентичность объявления
manifest bytes → SHA-256 → обязательство содержания
Три строки описывают независимые задачи. Они могут использовать разные структуры и алгоритмы, не создавая противоречия.
Порядок изменения состояния
После аутентификации и разбора сообщения приёмник проверяет, обработана ли его идентичность. Затем оценивает допустимость редакции. При успешном решении фиксирует применение и новую текущую запись. Если далее есть внешние действия, их взаимодействие требует отдельного анализа, включая повторный вход и атомарность. Наш первый фрагмент внешних вызовов не делает.
// Фрагмент внутри самостоятельного приёмника после проверки источника.
if (processed[deliveryKey]) return;
if (edition <= currentEdition[courseId]) revert OlderEdition();
processed[deliveryKey] = true;
currentEdition[courseId] = edition;
currentDigest[courseId] = manifestDigest;
Имена mappings и ошибки являются частью описанной модели MirrorLedger. Фрагмент не является полным контрактом и не удостоверяет источник самостоятельно. Значение manifestDigest получает смысл SHA-256 только по соглашению библиотеки, а не благодаря имени переменной.
Отказ не является применением
Если схема неправильная или редакция недопустима, политика должна определить результат без ложного статуса готовности. В показанном EVM-фрагменте revert отменяет изменения данной операции. Поэтому ранняя запись processed не переживает последующий revert в той же транзакции. Это отличается от нескольких независимых серверных записей без общей транзакции.
Для серверного журнала требуется выбранный механизм атомарного решения: уникальное ограничение, транзакция и связь с последующим действием. Иначе процесс способен сохранить «обработано», завершиться до выдачи и потерять возможность восстановить незавершённый результат. Учебная модель не обещает одинаковую атомарность во всех средах.
Повтор после отказа относится к исправленному состоянию или предусмотренной повторной попытке. Нельзя бесконечно повторять заведомо недопустимый источник. Диагностика сохраняет, какая стадия отвергла сообщение, а интерфейс не называет любой вызов приёмника успешным обновлением.
Опоздавшая первая редакция
Зеркало уже применило редакцию 2, затем получило отдельное подлинное сообщение редакции 1. Проверка источника проходит, transportId новый, но правило текущей версии отвергает уменьшение номера. Материал первой редакции может остаться в архиве; он не должен стать текущим из-за задержки доставки.
Понятие номера редакции здесь является собственным договором издателя. Оно должно иметь определённый тип и порядок. Строковое сравнение "10" и "2" не заменяет числовую семантику. Если проект выбирает другую идентичность версий, его правило обновления формулируется отдельно, а не угадывается по заголовку выпуска.
Повтор той же редакции с другим обязательством требует особого решения. В нашем договоре объявленная редакция не переписывается незаметно. Конфликт сохраняется как ошибка процесса; нельзя выбрать последнюю доставку и скрыть различие. Для исправленного содержания издатель создаёт новую редакцию.
Повторное наблюдение события
Индексатор может прочитать событие несколько раз после перезапуска или перекрытия диапазонов. Это не новый сетевой вызов, но серверная проекция тоже должна быть идемпотентной. Она сохраняет идентичность события с сетевым контекстом и пересматривает принадлежность истории по своей модели. Транспортная защита контракта не освобождает индексатор от этого правила.
Если сервер выдаёт доступ читателю по зеркалу, его решение связывается с конкретным применённым объявлением. Повторный сигнал не создаёт два права. Однако получение открытого текста остаётся отдельной стадией: техническая однократность записи не означает невозможность копировать материалы.
Удаление старых отметок processed тоже меняет гарантию. Если транспортный идентификатор снова станет допустимым после уборки, приёмник может повторить эффект. Для ограниченного хранения нужна обоснованная область: например, подтверждённый закрытый диапазон или другая проверяемая политика протокола. Нельзя просто удалять отметку через месяц, считая, что старое сообщение больше никому не известно. Публичная история способна сохраняться дольше локального журнала.
Одновременные попытки также требуют последовательного принятия. В EVM разные транзакции исполняются в определённом порядке; в серверной проекции несколько работников могут читать один прежний статус параллельно. Уникальное ограничение и атомарное решение предотвращают два независимых эффекта. Проверка флага в памяти одного процесса не создаёт такую гарантию для всей системы. Поэтому транспорт, контрактный приёмник и серверное применение имеют собственные механизмы однократности.
Досье повторов
Запишем правила области ключа, текущей редакции, конфликта обязательств, атомарного применения и повторного наблюдения. Для каждого случая зададим ожидаемый эффект: первый допустимый вход меняет запись, дубль не повторяет действие, более старая версия не откатывает текущую. Это исходные критерии, не результаты запуска.
Конкретные транспортные состояния представлены в CCIP Message Lifecycle, прикладные границы — в рекомендациях CCIP EVM. Далее адаптер свяжет нашу независимую модель с выбранным транспортом, сохранив семантику библиотеки.