Partilhar via


Versionamento de orquestração (pré-visualização)

Atualizar e desvalorizar orquestrações é uma consideração fundamental ao trabalhar com sistemas de orquestração duráveis. Se uma orquestração for interrompida e posteriormente retomada (por exemplo, durante uma atualização do host), o Durable Task Scheduler repete os eventos da orquestração, garantindo que todos os passos anteriores foram executados com sucesso antes de avançar para o passo seguinte. Esta ação assegura fiabilidade, uma das promessas centrais do paradigma de execução duradoura.

Se uma orquestração sofrer alterações entre implementações, os passos que são executados podem deixar de ser os mesmos. Neste caso, o sistema lança um NonDeterministicError, em vez de permitir que a orquestração continue.

A versionação de orquestração previne problemas relacionados com o não-determinismo, permitindo-lhe trabalhar de forma fluida com orquestrações novas (ou antigas). O Durable Task Scheduler tem dois estilos diferentes de versionamento, que pode usar separadamente ou em conjunto:

Importante

Atualmente, os SDKs de Tarefa Durável não estão disponíveis para JavaScript e PowerShell.

Importante

Atualmente, os SDKs de Tarefa Durável não estão disponíveis para JavaScript e PowerShell.

Controle de versão condicional baseado em cliente/contexto

Para que uma orquestração tenha uma versão, deve primeiro defini-la no cliente.

O SDK .NET utiliza as extensões padrão do construtor de hosts.

Observação

Disponível no .NET SDK (Microsoft.DurableTask.Client.AzureManaged) desde a v1.9.0.

builder.Services.AddDurableTaskClient(builder =>
{
    builder.UseDurableTaskScheduler(connectionString);
    builder.UseDefaultVersion("1.0.0");
});

Observação

Disponível no Java SDK (com.microsoft:durabletask-client) desde a versão 1.6.0.

public DurableTaskClient durableTaskClient(DurableTaskProperties properties) {
    // Create client using Azure-managed extensions
    return DurableTaskSchedulerClientExtensions.createClientBuilder(properties.getConnectionString())
        .defaultVersion("1.0")
        .build();
}
 c = DurableTaskSchedulerClient(host_address=endpoint, secure_channel=secure_channel,
                                   taskhub=taskhub_name, token_credential=credential,
                                   default_version="1.0.0")

Depois de adicionar a versão ao cliente, qualquer orquestração iniciada por este host utiliza a versão 1.0.0. A versão é uma cadeia simples e aceita qualquer valor. No entanto, o SDK tenta convertê-lo em . NET's System.Version.

  • Se puder ser convertida, essa biblioteca é usada para comparação.
  • Se não, é usada uma simples comparação de cadeias.

Fornecer a versão no cliente também a torna disponível no TaskOrchestrationContext, o que significa que pode usar a versão em instruções condicionais. Desde que as versões mais recentes de orquestração tenham o controle de versão apropriado, tanto as versões antigas como as novas de orquestração podem correr em conjunto no mesmo host.

Example:

[DurableTask]
class HelloCities : TaskOrchestrator<string, List<string>>
{
    private readonly string[] Cities = ["Seattle", "Amsterdam", "Hyderabad", "Kuala Lumpur", "Shanghai", "Tokyo"];

    public override async Task<List<string>> RunAsync(TaskOrchestrationContext context, string input)
    {
        List<string> results = [];
        foreach (var city in Cities)
        {
            results.Add(await context.CallSayHelloAsync($"{city} v{context.Version}"));
            if (context.CompareVersionTo("2.0.0") >= 0)
            {
                results.Add(await context.CallSayGoodbyeAsync($"{city} v{context.Version}"));
            }
        }

        Console.WriteLine("HelloCities orchestration completed.");
        return results;
    }
}

Depois de adicionar a versão ao cliente, qualquer orquestração iniciada por este cliente utiliza a versão 1.0.0. A versão é uma cadeia simples e aceita qualquer valor.

Fornecer a versão no cliente também a disponibiliza em TaskOrchestration, o que significa que pode usar a versão em instruções condicionais. Desde que as versões mais recentes de orquestração tenham o bloqueio de versões apropriado, tanto a antiga como a nova podem correr em conjunto no mesmo cliente.

Example:

public TaskOrchestration create() {
    return ctx -> {
        List<String> results = new ArrayList<>();
        for (String city : new String[]{ "Seattle", "Amsterdam", "Hyderabad", "Kuala Lumpur", "Shanghai", "Tokyo" }) {
            results.add(ctx.callActivity("SayHello", city, String.class).await());
            if (VersionUtils.compareVersions(ctx.getVersion(), "2.0.0") >= 0) {
                // Simulate a delay for newer versions
                results.add(ctx.callActivity("SayGoodbye", city, String.class).await());
            }
        }
        ctx.complete(results);
    };
}

Depois de adicionar a versão ao cliente, qualquer orquestração iniciada por este cliente utiliza a versão 1.0.0. A versão é uma string simples analisada usando packaging.version, que suporta a comparação semântica de versionamento e aceita qualquer valor.

Fornecer a versão no cliente também a torna disponível no task.OrchestrationContext, o que significa que pode usar a versão em instruções condicionais. Desde que as versões mais recentes de orquestração tenham o bloqueio de versões apropriado, tanto a antiga como a nova podem correr em conjunto no mesmo cliente.

Example:

def orchestrator(ctx: task.OrchestrationContext, _):
    if ctx.version == "1.0.0":
        # For version 1.0.0, we use the original logic
        result: int = yield ctx.call_activity(activity_v1, input="input for v1")
    elif ctx.version == "2.0.0":
        # For version 2.0.0, we use the updated logic
        result: int = yield ctx.call_activity(activity_v2, input="input for v2")
    else:
        raise ValueError(f"Unsupported version: {ctx.version}")
    return {
        'result': result,
    }

Neste exemplo, adicionámos uma SayGoodbye atividade à HelloCities orquestração. Esta atividade é chamada apenas para versões de orquestração 2.0.0 e posteriores. Com as simples instruções condicionais, qualquer orquestração com uma versão inferior 2.0.0 continua a funcionar e qualquer nova orquestração inclui a nova atividade.

Quando usar o controle de versão do cliente

Embora a versionação por cliente forneça o mecanismo mais simples para a orquestração de versões, interagir com a versão pode ser intensivo em programação. Utilize a versão do cliente se:

  • Quer uma versão padrão em todas as versões, ou
  • Precisas de lógica personalizada em torno de versões específicas.

Controle de versão baseado no trabalhador

Embora as orquestrações ainda precisem de uma versão cliente para definir a versão, o método de versionamento baseado em processos ajuda a evitar o uso de condicionais nas suas orquestrações. O trabalhador escolhe como agir em diferentes versões das orquestrações antes de estas começarem a ser executadas.

O controle de versão do trabalhador requer que os seguintes campos sejam definidos:

  1. A versão do trabalhador.

  2. A versão padrão aplicava-se às suborquestrações iniciadas pelo trabalhador.

  3. A estratégia que o trabalhador usa para corresponder à versão da orquestração.

    Nome Descrição
    Nenhum A versão não é considerada quando o trabalho está sendo processado
    Rigoroso A versão na orquestração e o trabalhador devem corresponder exatamente
    AtualOuMaisAntigo A versão na orquestração deve ser igual ou menor que a versão no trabalhador
  4. A estratégia que o operador adota se a versão não cumprir a estratégia de correspondência definida.

    Nome Descrição
    Rejeitar A orquestração é rejeitada pelo trabalhador, mas permanece na fila de tarefas para ser tentada novamente mais tarde.
    Falha A orquestração falhou e foi removida da fila de trabalho

Semelhante à versão do cliente, pode definir estes campos através do padrão host builder.

Observação

Disponível no SDK do .NET (Microsoft.DurableTask.Worker.AzureManaged) desde a v1.9.0.

builder.Services.AddDurableTaskWorker(builder =>
{
    builder.AddTasks(r => r.AddAllGeneratedTasks());
    builder.UseDurableTaskScheduler(connectionString);
    builder.UseVersioning(new DurableTaskWorkerOptions.VersioningOptions
    {
        Version = "1.0.0",
        DefaultVersion = "1.0.0",
        MatchStrategy = DurableTaskWorkerOptions.VersionMatchStrategy.Strict,
        FailureStrategy = DurableTaskWorkerOptions.VersionFailureStrategy.Reject,
    });
});

Observação

Disponível no Java SDK (com.microsoft:durabletask-client) desde v1.6.0.

private static DurableTaskGrpcWorker createTaskHubServer() {
    DurableTaskGrpcWorkerBuilder builder = new DurableTaskGrpcWorkerBuilder();
    builder.useVersioning(new DurableTaskGrpcWorkerVersioningOptions(
            "1.0",
            "1.0",
            DurableTaskGrpcWorkerVersioningOptions.VersionMatchStrategy.CURRENTOROLDER,
            DurableTaskGrpcWorkerVersioningOptions.VersionFailureStrategy.REJECT));

    // Orchestrations can be defined inline as anonymous classes or as concrete classes
    builder.addOrchestration(new TaskOrchestrationFactory() {
        @Override
        public String getName() { return "HelloCities"; }

        @Override
        public TaskOrchestration create() {
            return ctx -> {
                List<String> results = new ArrayList<>();
                for (String city : new String[]{ "Seattle", "Amsterdam", "Hyderabad", "Kuala Lumpur", "Shanghai", "Tokyo" }) {
                    results.add(ctx.callActivity("SayHello", city, String.class).await());
                }
                ctx.complete(results);
            };
        }
    });

    // Activities can be defined inline as anonymous classes or as concrete classes
    builder.addActivity(new TaskActivityFactory() {
        @Override
        public String getName() { return "SayHello"; }

        @Override
        public TaskActivity create() {
            return ctx -> {
                String input = ctx.getInput(String.class);
                return "Hello, " + input + "!";
            };
        }
    });

    return builder.build();
}
with DurableTaskSchedulerWorker(host_address=endpoint, secure_channel=secure_channel,
                                taskhub=taskhub_name, token_credential=credential) as w:
    # This worker is versioned for v2, as the orchestrator code has already been updated
    # CURRENT_OR_OLDER allows this worker to process orchestrations versioned below 2.0.0 - e.g. 1.0.0
    w.use_versioning(worker.VersioningOptions(
        version="2.0.0",
        default_version="2.0.0",
        match_strategy=worker.VersionMatchStrategy.CURRENT_OR_OLDER,
        failure_strategy=worker.VersionFailureStrategy.FAIL
    ))
    w.add_orchestrator(orchestrator)
    w.add_activity(activity_v1)
    w.add_activity(activity_v2)
    w.start()

Estratégias para lidar com falhas

Rejeitar

Use a Reject estratégia de falha quando o comportamento desejado for que a orquestração tente novamente mais tarde ou com outro trabalhador. Durante a Reject falha:

  1. Uma orquestração é rejeitada e devolvida à fila de trabalho.
  2. Uma orquestração é desalinhada.
  3. A orquestração dequeueada pode cair num trabalhador diferente ou no mesmo novamente.

O processo repete-se até que um trabalhador capaz de tratar da orquestração esteja disponível. Esta estratégia lida de forma fluida com implementações em que uma orquestração é atualizada. À medida que a implementação avança, os trabalhadores que não conseguem lidar com a orquestração rejeitam-na, enquanto os que conseguem processá-la.

A possibilidade de ter versões mistas de trabalhadores e orquestração permite cenários como implantações azul-verde.

Fail

Use a estratégia de falha Fail quando não são esperadas outras versões. Neste caso, a nova versão é uma anomalia e nenhum trabalhador deve sequer tentar trabalhar nela. O Durable Task Scheduler falha a orquestração, colocando-a num estado terminal.

Quando usar o controle de versão do trabalhador

Use a versão de trabalhadores em cenários onde versões de orquestração desconhecidas ou não suportadas não devem ser executadas. Em vez de colocar o código de manipulação de versão no trabalhador, o controle de versão do trabalhador impede que a orquestração seja executada. Este método permite um código de orquestração mais simples. Sem quaisquer alterações de código, vários cenários de implementação podem ser geridos, como implantações azul-verdes.

Próximos passos