Поделиться через


Управление версиями в устойчивых функциях (Функции Azure) — общедоступная предварительная версия

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

Note

Для пользователей планировщика устойчивых задач, если вы используете пакеты SDK для устойчивых задач вместо устойчивых функций, обратитесь к статье по версиям устойчивых пакетов SDK.

Terminology

В этой статье используются два связанных, но отдельных термина:

  • Функция Orchestrator (или просто orchestrator): ссылается на код функции, определяющий логику рабочего процесса — шаблон или схему выполнения рабочего процесса.
  • Экземпляр оркестрации (или просто "оркестрация"): обозначает конкретное выполнение функции оркестратора с собственным состоянием, ID экземпляра и входными данными. Несколько экземпляров оркестрации могут выполняться одновременно из одной функции оркестратора.

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

Принцип работы

Функция управления версиями оркестрации работает по этим основным принципам:

  • Связь версий: при создании экземпляра оркестрации она получает версию, которая постоянно связана с ней.

  • Выполнение с учетом версии: код функции Orchestrator может изучить значение версии, связанное с текущим экземпляром оркестрации, и разветвлять выполнение соответствующим образом.

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

  • Защита пересылки: среда выполнения автоматически запрещает рабочим, работающим с более ранними версиями оркестратора, выполнять оркестрации, запущенные более новыми версиями оркестратора.

Important

Версионирование оркестрации в настоящее время находится в общедоступной предварительной версии.

Предпосылки

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

Если вы используете язык non-.NET (JavaScript, Python, PowerShell или Java) с пакетами расширений, приложение-функция должна ссылаться на пакет расширений версии 4.26.0 или более поздней версии. Настройте диапазон extensionBundle в host.json, чтобы минимальная версия была не менее 4.26.0, например:

{
    "version": "2.0",
    "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[4.26.0, 5.0.0)"
    }
}

Дополнительные сведения о выборе и обновлении версий пакета см. в документации по конфигурации пакета расширений .

Используйте Microsoft.Azure.Functions.Worker.Extensions.DurableTask версию 1.5.0 или более позднюю.

Базовое использование

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

Note

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

Шаг 1: Конфигурация параметра defaultVersion

Чтобы настроить версию по умолчанию для оркестрации, необходимо добавить или обновить параметр defaultVersion в файле host.json в проекте Azure Functions.

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>"
    }
  }
}

Строка версии может соответствовать любому формату, который соответствует стратегии управления версиями:

  • Многочастное версионирование: "1.0.0", "2.1.0"
  • Простое нумерирование: "1", "2"
  • На основе даты: "2025-01-01"
  • Пользовательский формат: "v1.0-release"

После настройки defaultVersion все новые экземпляры оркестрации будут постоянно связаны с этой версией.

Правила сравнения версий

При выборе стратегии Strict или CurrentOrOlder (см. Сопоставление версий), среда выполнения сравнивает версию экземпляра оркестрации со значением defaultVersion рабочей роли, используя следующие правила:

  • Пустые или нулевые версии рассматриваются как равные.
  • Пустая или нулевая версия считается более ранней, чем любая определенная версия.
  • Если оба варианта могут быть интерпретированы как System.Version, используется метод CompareTo.
  • В противном случае выполняется сравнение строк без учета регистра.

Шаг 2: Логика функции «Orchestrator»

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

Important

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

[Function("MyOrchestrator")]
public static async Task<string> RunOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    if (context.Version == "1.0")
    {
        // Original logic for version 1.0
        ...
    }
    else if (context.Version == "2.0")
    {
        // New logic for version 2.0
        ...
    }
    ...
}

Note

Свойство context.Version доступно только для чтения и отражает версию, которая была постоянно связана с экземпляром оркестрации при его создании. Это значение нельзя изменить во время выполнения оркестрации. Если вы хотите указать версию с помощью других средств host.json, это можно сделать при запуске экземпляра оркестрации с помощью клиентских API оркестрации (см. статью Запуск новых оркестраций и вложенных оркестраций с "конкретными версиями").

Tip

Если вы только начинаете использовать версионирование оркестраций и у вас уже есть текущие оркестрации, созданные до того, как вы указали defaultVersion, вы всё равно можете добавить настройку defaultVersion в ваш host.json прямо сейчас. Для всех ранее созданных оркестраций функция context.Version возвращает значение null (или эквивалентное значение, зависящее от языка), поэтому логику оркестратора можно структурировать таким образом, чтобы соответствующим образом обрабатывать как устаревшие оркестрации (с пустой версией), так и новые оркестрации с версиями. Ниже приведены значения, зависящие от языка, для проверки случая наследия.

  • C#: context.Version == null или context.Version is null
  • JavaScript: context.df.version == null
  • Питон: context.version is None
  • PowerShell: $null -eq $Context.Version
  • Java: context.getVersion() == null Также обратите внимание, что указание "defaultVersion": null в host.json эквивалентно тому, чтобы не указывать его вовсе.

Tip

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

Что происходит после развертывания

Вот что ожидать после развертывания обновленной функции оркестратора с новой логикой версии:

  • Сосуществование рабочих: рабочие, содержащие новый код функции оркестратора, начнут работу, в то время как некоторые рабочие со старым кодом все еще могут быть активными.

  • Назначение версии для новых экземпляров: всем новым оркестрациям и вложенным оркестрациям, созданным новыми работниками, будет назначена версия из defaultVersion.

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

  • Старые ограничения рабочей роли: старые работники смогут обрабатывать только оркестрации с версией , равной или ниже версии, указанной в собственном defaultVersionhost.jsonкоде, так как они не должны иметь код оркестратора, совместимый с более новыми версиями. Это ограничение предотвращает ошибки выполнения и непредвиденное поведение.

Note

Управление версиями оркестрации не влияет на жизненный цикл рабочей роли. Платформа Azure Functions управляет предоставлением и выводом рабочих в соответствии с обычными правилами, в зависимости от параметров размещения.

Пример. Замена действия в последовательности

В этом примере показано, как заменить одно действие другим в середине последовательности, используя оркестрацию версий.

Версия 1.0

Конфигурация host.json:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "1.0"
    }
  }
}

Функция Оркестратор:

[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var orderId = context.GetInput<string>();
    
    await context.CallActivityAsync("ValidateOrder", orderId);
    await context.CallActivityAsync("ProcessPayment", orderId);
    await context.CallActivityAsync("ShipOrder", orderId);
    
    return "Order processed successfully";
}

Версия 2.0 с обработкой скидки

Конфигурация host.json:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "2.0"
    }
  }
}

Функция Оркестратор:

using DurableTask.Core.Settings;

[Function("ProcessOrderOrchestrator")]
public static async Task<string> ProcessOrder(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var orderId = context.GetInput<string>();

    await context.CallActivityAsync("ValidateOrder", orderId);

    if (VersioningSettings.CompareVersions(context.Version, "1.0") <= 0)
    {
        // Preserve original logic for existing instances
        await context.CallActivityAsync("ProcessPayment", orderId);
    }
    else // a higher version (including 2.0)
    {
        // New logic with discount processing (replaces payment processing)
        await context.CallActivityAsync("ApplyDiscount", orderId);
        await context.CallActivityAsync("ProcessPaymentWithDiscount", orderId);
    }
    
    await context.CallActivityAsync("ShipOrder", orderId);

    return "Order processed successfully";
}

Расширенное использование

Для более сложных сценариев управления версиями можно настроить другие параметры для управления соответствием и несоответствиям версии среды выполнения.

Tip

Используйте конфигурацию по умолчанию (CurrentOrOlder с Reject) для большинства сценариев, чтобы обеспечить безопасное развертывание при сохранении состояния оркестрации во время переходов версий. Рекомендуется продолжить работу с расширенной конфигурацией только в том случае, если у вас есть определенные требования, которые не могут быть выполнены с поведением по умолчанию.

Сопоставление версий

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

Configuration

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>",
      "versionMatchStrategy": "CurrentOrOlder"
    }
  }
}

Доступные стратегии

  • None (не рекомендуется): полностью игнорировать версию оркестрации. Все полученные работы обрабатываются независимо от версии. Эта стратегия эффективно отключает проверку версий и позволяет любому рабочему сотруднику обрабатывать любой экземпляр оркестрации.

  • Strict: обрабатываются только задачи из оркестрации с точно такой же версией, как указано в defaultVersion в рабочем процессе host.json. Эта стратегия обеспечивает высокий уровень изоляции версий, но требует тщательной координации развертывания, чтобы избежать оставленных без управления оркестраций. Последствия несоответствия версий описаны в разделе обработки несоответствия версий .

  • CurrentOrOlder (по умолчанию): обработка задач из оркестрации, чья версия меньше или равна версии, указанной defaultVersion в рабочем host.json. Эта стратегия позволяет обеспечить обратную совместимость, позволяя новым работникам обрабатывать оркестрации, запущенные более старыми версиями оркестратора, не позволяя старым работникам обрабатывать более новые оркестрации. Последствия несоответствия версий описаны в разделе обработки несоответствия версий .

Обработка несоответствия версий

Параметр versionFailureStrategy определяет, что происходит, если версия экземпляра оркестрации не соответствует текущей defaultVersion.

Configuration:

{
  "extensions": {
    "durableTask": {
      "defaultVersion": "<version>",
      "versionFailureStrategy": "Reject"
    }
  }
}

Доступные стратегии:

  • Reject (по умолчанию): не нужно обрабатывать оркестрацию. Экземпляр оркестрации остается в текущем состоянии и может быть повторно запущен позже, когда совместимый рабочий станет доступным. Эта стратегия является самым безопасным вариантом, так как она сохраняет состояние оркестрации.

  • Fail: сбой оркестрации. Эта стратегия немедленно завершает экземпляр оркестрации с состоянием сбоя, которое может быть уместно в сценариях, когда несоответствия версий указывают на серьезные проблемы с развертыванием.

Запуск новых оркестраций и вложенных оркестраций с определенными версиями

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

Когда следует использовать определенные версии:

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

Вы можете переопределить версию по умолчанию, предоставив определенное значение версии при создании новых экземпляров оркестрации с помощью API клиента оркестрации. Это позволяет детально управлять тем, какую версию использует каждый новый экземпляр оркестрации.

[Function("HttpStart")]
public static async Task<HttpResponseData> HttpStart(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
    [DurableClient] DurableTaskClient client,
    FunctionContext executionContext)
{
    var options = new StartOrchestrationOptions
    {
        Version = "1.0"
    };
    
    string instanceId = await client.ScheduleNewOrchestrationInstanceAsync("ProcessOrderOrchestrator", orderId, options);

    // ...
}

Вы также можете запускать подоркестрации с использованием определённых версий через функцию оркестратора.

[Function("MainOrchestrator")]
public static async Task<string> RunMainOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context)
{
    var subOptions = new SubOrchestratorOptions
    {
        Version = "1.0"
    };
    
    var result = await context.CallSubOrchestratorAsync<string>("ProcessPaymentOrchestrator", orderId, subOptions);
    
    // ...
}

Удаление устаревших путей кода

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

Когда безопасно удалять устаревший код:

  • Все экземпляры оркестрации с использованием старой версии завершены (выполнено успешно, не удалось или было завершено)
  • Новые экземпляры оркестрации не будут созданы по старой версии
  • Вы проверили с помощью мониторинга или запроса, что ни один экземпляр не работает на устаревшей версии.
  • Достаточное время прошло с момента последнего развертывания старой версии (учитывая требования к непрерывности бизнес-процессов).

Рекомендации по удалению:

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

Warning

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

Лучшие практики

Управление версиями

  • Используйте многокомпонентное управление версиями: применяйте согласованную схему управления версиями, например major.minor.patch.
  • Критические изменения в документе: четко документируйте, какие изменения требуют новой версии.
  • Управление жизненным циклом версии: определите время удаления устаревших участков кода.

Организация кода

  • Отдельная логика версий: используйте четкое разветвление или разделенные методы для разных версий.
  • Сохранение детерминизма: не изменяйте существующую логику версии после развертывания. Если изменения абсолютно необходимы (например, критически важные исправления ошибок), убедитесь, что они поддерживают детерминированное поведение и не изменяют последовательность операций, иначе можно ожидать, что более новые версии оркестратора могут завершиться ошибкой при обработке старых оркестраций.
  • Тщательно протестируйте все пути версий, особенно во время переходов.

Мониторинг и наблюдаемость

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

Troubleshooting

Общие проблемы

  • Проблема: Экземпляры оркестрации, созданные с версией 1.0, вызывают сбой после развертывания версии 2.0

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

    • Решение. Это ожидаемое поведение. Среда выполнения намеренно запрещает старым работникам выполнять оркестрации с более новыми версиями для обеспечения безопасности. Убедитесь, что все работники обновляются до последней версии оркестратора и defaultVersion их параметры host.json обновляются соответствующим образом. Это поведение можно изменить при необходимости с помощью расширенных параметров конфигурации (дополнительные сведения см. в разделе "Дополнительное использование ").
  • Проблема: сведения о версии недоступны в оркестраторе (context.Version или context.getVersion() имеет значение NULL, независимо от defaultVersion параметра)

    • Решение: Проверьте раздел Предварительные требования, чтобы убедиться, что среда соответствует всем требованиям для версионирования оркестрации.
  • Проблема. Оркестрации более новой версии делают очень медленный прогресс или полностью застряли

    • Решение. Проблема может иметь различные первопричины:
      1. Недостаточно новых работников: убедитесь, что достаточное количество работников с равной или более высокой версией defaultVersion развертываются и активны для обработки новых оркестраций.
      2. Вмешательство старых работников в маршрутизацию оркестрации: старые работники могут мешать работе механизма маршрутизации оркестрации, что затрудняет новым работникам получение оркестраций для обработки. Это может быть особенно заметно при использовании определенных поставщиков хранилища (службы хранилища Azure или MSSQL). Как правило, платформа Функций Azure гарантирует, что старые работники удаляются вскоре после развертывания, поэтому любая задержка, как правило, не является значительной. Однако если вы используете конфигурацию, которая позволяет управлять жизненным циклом старых процессов, убедитесь, что старые процессы в конечном итоге отключаются. Кроме того, рекомендуется использовать планировщик устойчивых задач, так как он предоставляет улучшенный механизм маршрутизации, который менее подвержен этой проблеме.

Дальнейшие шаги