Автоматизация Web3: расписание, повторы и гонки
Время запуска и время действия
В предыдущем уроке мы повторно проверяли условия при приёме отчёта. Теперь организуем расписание. Библиотека проверяет выпуск каждый час, но выражение «каждый час» требует точного смысла. Когда начался часовой слот? Что делать с пропуском? Можно ли принять два отчёта за один период? От этих решений зависит история проверки, поэтому сначала зададим правила, а затем будем выбирать конкретный триггер CRE.
Рассматриваем отдельный LibraryScheduler поверх реестра проверок. Прежний Chainlink Automation больше не является маршрутом новой регистрации: официальная документация сообщает о его прекращении и переходе к CRE. Современный workflow может получать cron-триггер, но расписание запуска не устраняет гонки и повторы в нашем приложении.
Считаем слот
Для учебной модели выберем epoch 0 и period 3600 секунд. Слот определяется целой частью (now − epoch) / period. При now 7200 начинается слот 2; при now 10799 он ещё продолжается; при now 10800 начинается слот 3. Эти числа условные и не являются текущим временем сети.
function slotOf(now, epoch, period) {
if (period <= 0n || now < epoch) throw new Error('invalid schedule');
return (now - epoch) / period;
}
Функция показывает арифметику, без обращения к RPC или запуска workflow. На уровне цепи время исполнения берётся из согласованного контекста транзакции. Время, присланное браузером, не должно решать, какой слот доступен для записи. При этом время внешнего наблюдения сохраняем отдельно: оно объясняет свежесть сведений о файлах.
У слота есть область: курс 17, выпуск 2, revision 5 и тип проверки manifest-check. Слот 2 для другого курса не та же бизнес-операция. Смена выпуска создаёт новую последовательность наблюдений, но не должна переименовывать старые результаты задним числом.
Два исполнителя увидели одну работу
Представим, что два независимых запуска одновременно прочитали «слот 2 свободен». Оба подготовили отчёты. Если состояние проверки и отметка выполнения находятся в разных несогласованных операциях, оба могут создать эффект. Предварительная блокировка интерфейса эту гонку не решает.
Для записи на одной цепи используем атомарный переход. Потребитель проверяет полный ключ слота, актуальность выпуска и revision, затем помечает слот обработанным и сохраняет результат в одной транзакции. Второй подходящий отчёт увидит занятую запись. Если вся транзакция откатилась, отметка также откатится: нельзя оставлять «выполнено» без результата.
Это не означает, что блокчейн обеспечивает атомарность между HTTP-источником и своей памятью. Внешние наблюдения могли состояться дважды. Наше правило ограничивает принятие результата, а бюджет внешних запросов контролируется отдельно, как в уроке о расходах workflow.
JobId, слот и revision
Один запуск может иметь несколько попыток доставки. Их идентификаторы нужны для диагностики. Но если защитить только jobId, новый запуск с другим номером обойдёт запрет второго эффекта. Поэтому журнал содержит две связи: транспортная попытка относится к jobId, а jobId относится к ключу бизнес-слота.
Для курса 17 составим условные ключи S2 и S3. Это обозначения, не вычисленные криптографические значения. Отчёты R1 и R2 относятся к S2. После принятия R1 отчёт R2 не обновляет результат автоматически. Отчёт R3 относится к S3 и может быть принят, если текущая политика допускает его время и выпуск.
Ревизия защищает смысл расписания. Если период изменили с часа на пятнадцать минут, нельзя просто подставить новую арифметику в старый журнал. Номер слота 2 теперь обозначает иной интервал. Заводим revision 6 и определяем точку перехода; старые записи сохраняют прежний период и epoch.
Пропуски и опоздания
Допустим, workflow не работал два часа. При восстановлении есть несколько возможных политик: выполнять только текущую проверку, обрабатывать ограниченное число пропущенных слотов или сохранять пропуски без догоняющих запросов. Для проверки доступности файлов выбираем текущий слот. Проверка сейчас не сообщает, были ли файлы доступны час назад.
Поэтому не заполняем пропущенный слот сегодняшним положительным ответом с прошлым временем. Такая история выглядела бы непрерывной, но содержала ложное наблюдение. Журнал отмечает «нет результата», а новый отчёт относится к действительному времени проверки.
Опоздавший отчёт тоже оценивается явно. В нашей модели допустимо окно доставки до конца следующего слота, но запись хранит исходный slot и observedAt. Это проектное правило, не параметр CRE по умолчанию. Можно выбрать более строгую политику, если информация используется для решения о платеже; область её применения должна быть объяснена заранее.
Кто может закрыть слот
Правило «первый результат занимает слот» опасно при открытом входе. Посторонний может отправить фиктивный отчёт и заблокировать настоящий. Поэтому источник и схема проверяются до изменения журнала. Только допустимый отчёт может претендовать на запись. Ошибка внешней проверки также должна иметь определённый статус: она не маскируется под успешную доступность.
В примере отрицательный достоверный отчёт закрывает слот со статусом unavailable. Это означает, что проверка выполнена, а результат отрицательный. Оператор может инициировать внеплановую проверку с отдельным типом и журналом причины. Он не удаляет неприятный результат и не превращает её в повтор той же часовой операции.
Если часы внешнего источника заметно расходятся с временем принятия, сохраняем оба значения и отмечаем несогласованность. Присланный observedAt из будущего не используем для продления окна. Это тот же принцип проверки свежести, который применяли к ценовому ответу.
Разбираем четыре случая
Первый случай: R1 и R2 пришли для S2 с правильным источником. Ожидаем один принятый результат. Второй: R3 пришёл для новой ревизии, но получил старую метку периода. Потребитель отклоняет несовместимую область. Третий: два пропущенных слота; журнал сохраняет пробел, текущая проверка не переписывает прошлое. Четвёртый: достоверный отрицательный результат; слот отмечен завершённым с отрицательным состоянием.
Это исходные сценарии будущих проверок, а не выполненные тесты. В учебном интерфейсе показываем расписание, фактическое наблюдение, принятие и пропуски раздельно. Тогда слово «автоматически» не обещает идеальную периодичность. Далее перейдём к доказательствам: там также требуется точно определить, какое действие разрешается и в какой области одно право считается использованным.