Перейти к содержанию

Patched и совместимое изменение Workflow

Длительное согласование может пережить несколько выпусков приложения. Новый код должен обслуживать не только новые заявки, но и уже записанные истории. Добавим validateSubmission перед бронью так, чтобы старый процесс не получил неожиданную команду при replay. Результат урока — подготовить совместимую ветку и определить, когда её допустимо убирать.

Полный файл 11-versioned.ts продолжает именно 09-compensation.ts. Между этими состояниями сохранены имена Activities, Signal, Query и логика cleanup. Автор не развёртывал Worker и не выполнял replay, поэтому совместимость описана по модели API и исходникам, а не выдана за полученный результат.

Какая перемена нарушает старое ожидание

В старом коде первая внешняя команда — reserveSlot. Если просто вставить await validateSubmission(input) перед ней, старая история при replay встретит другую последовательность. Даже если validate кажется безвредной, это новая Activity и новое значимое действие. Содержательная похожесть функций не делает команды одинаковыми.

ValidateSubmission в Activities проверяет идентификатор, целую revision и длину title. Она не меняет редакционные данные. Тем не менее её результат и ошибка становятся частью исполнения. Для новых заявок это полезная ранняя проверка, а для старых нужно сохранить путь, соответствующий уже записанной истории.

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

Версия кода и revision материала — разные признаки. Revision относится к содержимому заявки. Patch ID относится к изменению Workflow. Новая редакция может обрабатываться старым совместимым алгоритмом, а новая версия алгоритма — старой редакцией. Подмена одного другим делает протокол длительного процесса неясным.

Добавляем ветку с постоянным именем

В новом Workflow импортируется patched и перед основным try появляется:

if (patched('validate-submission-v1')) {
  await validateSubmission(input);
}

Для новых исполнений новая ветка становится частью истории через patch marker. При replay старой истории без этого изменения код сохраняет прежний путь. Идентификатор patch должен оставаться постоянным для этой перемены; он не строится из даты компьютера, текущего deployment или случайного значения.

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

Validate находится до try, потому что до неё не выполнялась бронь. Non-retryable ошибка InvalidSubmission завершает новый процесс без обязанности освобождать внешний слот. Это осознанная предметная граница. Если проверка сама начнёт создавать ресурс, её ответственность изменится и такой placement перестанет быть достаточным.

Основные способы изменения описаны в руководстве versioning. Patching подходит для локального совместимого перехода; Worker Versioning является отдельной моделью развёртывания. Старые экспериментальные примеры versioning не следует переносить без проверки актуального server/SDK-контракта.

Почему пример не гарантирует все ранние стадии

Серия постепенно меняла процесс: минимальная публикация, таймер, Signal, retry, cancellation и compensation. Эти файлы предназначены для независимых учебных заявок. У них одинаковое имя approvePublication ради понятного CLI, но произвольная история стадии 02 не становится автоматически совместимой с финальной стадией.

Наш patch решает конкретную перемену от 09 к 11. Поэтому будущая самостоятельная демонстрация начинается с заявки стадии 09, которая дошла до waiting. После смены Worker на 11 она должна продолжить старую ветку. Новая заявка стадии 11 должна выполнить validation перед reserveSlot. Это два разных ожидаемых наблюдения.

Для проверки читатель сохраняет старую history и использует replay.ts. Здесь replay не выполнен; отсутствие ошибки в редакционном чтении не заменяет исполнение. В production проект должен иметь проверяемый набор историй и регламент выпуска, соответствующий используемому способу versioning.

Если старый Worker стадии 09 продолжит получать новые histories с patch, смешивание несовместимых исполнителей на одной очереди создаёт дополнительный риск. Поэтому учебное переключение не моделирует полноценный rolling deployment нескольких версий. Сначала нужен определённый способ маршрутизации задач и сохранения совместимых bundles.

Жизненный цикл patch

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

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

Не переиспользуйте validate-submission-v1 для следующего несвязанного изменения, например отзыва публикации. Постоянное имя связывает маркер с конкретным смыслом. Для нового перехода нужна отдельная идентичность и объяснение, какие истории он затрагивает. Иначе один marker начинает означать несколько разных наборов команд.

Предметные ошибки validate требуют собственной совместимости данных. Если сделать минимальную длину title намного больше, новые заявки могут начать отказываться. Даже когда replay старых histories безопасен, продукт всё равно должен понять изменение правил. Техническая совместимость процесса и желательное бизнес-поведение — отдельные критерии.

Выбор другой стратегии

Иногда удобнее зарегистрировать новый Workflow type, например approvePublicationV2, и направлять новые заявки туда, оставив старый код для уже начатых. Это явный cutover, а не изменение текущих исполнений. Он требует сопровождать две реализации и продумать пользовательскую связь между ними.

Worker Versioning позволяет связывать исполнения с версиями deployment и контролировать rollout средствами Temporal. Детали конфигурации зависят от актуального server/SDK, поэтому наш локальный курс не подставляет старый build-id пример как современную универсальную инструкцию. Мы используем проверенный patched API и обозначаем предел демонстрации.

После урока новый шаг validation добавлен с определённой совместимой веткой, а старое состояние не считается автоматически переписанным. Осталось собрать передачу, наблюдение и правила завершения проекта, включая хранение данных и границу применения Continue-As-New.

Оглавление серии