Partilhar via


Controlo de versões no Durable Functions (Funções do Azure)

É inevitável que as funções sejam adicionadas, removidas e alteradas ao longo da duração de uma aplicação. Durable Functions permite o encadeamento de funções em conjunto de formas que não eram anteriormente possíveis e este encadeamento afeta a forma como pode lidar com o controlo de versões.

Como lidar com alterações interruptivas

Existem vários exemplos de alterações interruptivas a ter em conta. Este artigo aborda os mais comuns. O tema principal por trás de todos eles é que as orquestrações de funções novas e existentes são afetadas por alterações ao código de função.

Alterar assinaturas de atividade ou de função de entidade

Uma alteração de assinatura refere-se a uma alteração no nome, entrada ou saída de uma função. Se este tipo de alteração for efetuada a uma atividade ou função de entidade, pode quebrar qualquer função de orquestrador que dependa da mesma. Isto aplica-se especialmente a idiomas seguros de tipos. Se atualizar a função orchestrator para acomodar esta alteração, poderá interromper as instâncias existentes em voo.

Por exemplo, suponha que temos a seguinte função de orquestrador.

[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    bool result = await context.CallActivityAsync<bool>("Foo");
    await context.CallActivityAsync("Bar", result);
}

Esta função simplista utiliza os resultados do Foo e transmite-os para a Barra. Vamos supor que precisamos de alterar o valor devolvido de Foo de um Booleano para uma Cadeia para suportar uma maior variedade de valores de resultados. O resultado tem o seguinte aspeto:

[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    string result = await context.CallActivityAsync<string>("Foo");
    await context.CallActivityAsync("Bar", result);
}

Esta alteração funciona bem para todas as novas instâncias da função do orquestrador, mas pode interromper quaisquer instâncias em voo. Por exemplo, considere o caso em que uma instância de orquestração chama uma função chamada Foo, obtém um valor booleano e, em seguida, os pontos de verificação. Se a alteração da assinatura for implementada neste momento, a instância com ponto de verificação falhará imediatamente quando retomar e reproduzir a chamada para Foo. Esta falha ocorre porque o resultado na tabela do histórico é um valor Booleano, mas o novo código tenta anular a serialização para um valor de Cadeia, resultando num comportamento inesperado ou até mesmo numa exceção de runtime para idiomas seguros para tipos.

Este exemplo é apenas uma das muitas formas diferentes de uma alteração de assinatura de função poder interromper as instâncias existentes. Em geral, se um orquestrador precisar de mudar a forma como chama uma função, é provável que a alteração seja problemática.

Alterar a lógica do orquestrador

A outra classe de problemas de controlo de versões vem da alteração do código da função do orquestrador de uma forma que altera o caminho de execução para instâncias em voo.

Considere a seguinte função de orquestrador:

[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    bool result = await context.CallActivityAsync<bool>("Foo");
    await context.CallActivityAsync("Bar", result);
}

Agora vamos supor que pretende fazer uma alteração para adicionar uma nova chamada de função entre as duas chamadas de função existentes.

[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    bool result = await context.CallActivityAsync<bool>("Foo");
    if (result)
    {
        await context.CallActivityAsync("SendNotification");
    }

    await context.CallActivityAsync("Bar", result);
}

Esta alteração adiciona uma nova chamada de função a SendNotification entre Foo e Bar. Não existem alterações de assinatura. O problema surge quando uma instância existente é retomada a partir da chamada para a Barra. Durante a repetição, se a chamada original para Foo tiver devolvido true, a repetição do orquestrador chamará SendNotification, que não está no histórico de execução. O runtime deteta esta inconsistência e gera um erro de orquestração não determinista porque encontrou uma chamada para SendNotification quando esperava ver uma chamada para a Barra. O mesmo tipo de problema pode ocorrer ao adicionar chamadas à API a outras operações duráveis, como criar temporizadores duráveis, aguardar eventos externos, chamar sub-orquestrações, etc.

Estratégias de mitigação

Seguem-se algumas das estratégias para lidar com os desafios de controlo de versões:

  • Não fazer nada (não recomendado)
  • Parar todas as instâncias em voo
  • Implementações lado a lado

Não fazer nada

A abordagem ingénua do controlo de versões é não fazer nada e deixar que as instâncias de orquestração a bordo falhem. Dependendo do tipo de alteração, podem ocorrer os seguintes tipos de falhas.

  • As orquestrações podem falhar com um erro de orquestração não determinista .
  • As orquestrações podem ficar bloqueadas indefinidamente, comunicando um Running estado.
  • Se uma função for removida, qualquer função que tente chamá-la poderá falhar com um erro.
  • Se uma função for removida após a execução agendada, a aplicação poderá deparar-se com falhas de runtime de baixo nível no motor do Durable Task Framework, o que pode resultar numa degradação grave do desempenho.

Devido a estas potenciais falhas, a estratégia "não fazer nada" não é recomendada.

Parar todas as instâncias em voo

Outra opção é parar todas as instâncias em voo. Se estiver a utilizar o fornecedor de Armazenamento do Azure predefinido para Durable Functions, pode parar todas as instâncias ao limpar os conteúdos das filas de controlo interno e workitem-queue. Em alternativa, pode parar a aplicação de funções, eliminar estas filas e reiniciar a aplicação novamente. As filas serão recriadas automaticamente assim que a aplicação for reiniciada. As instâncias de orquestração anteriores podem permanecer indefinidamente no estado "Em execução", mas não irão desorganização dos registos com mensagens de falha ou causar quaisquer danos à sua aplicação. Esta abordagem é ideal no rápido desenvolvimento de protótipos, incluindo o desenvolvimento local.

Nota

Esta abordagem requer acesso direto aos recursos de armazenamento subjacentes e não sou adequado para todos os fornecedores de armazenamento suportados pelo Durable Functions.

Implementações lado a lado

A forma mais à prova de falha para garantir que as alterações interruptivas são implementadas em segurança é ao implementá-las lado a lado com as suas versões mais antigas. Isto pode ser feito com qualquer uma das seguintes técnicas:

  • Implemente todas as atualizações como funções totalmente novas, deixando as funções existentes tal como estão. Geralmente, isto não é recomendado devido à complexidade envolvida na atualização recursiva dos chamadores das novas versões de função.
  • Implemente todas as atualizações como uma nova aplicação de funções com uma conta de armazenamento diferente.
  • Implemente uma nova cópia da aplicação de funções com a mesma conta de armazenamento, mas com um nome de hub de tarefas atualizado. Isto resulta na criação de novos artefactos de armazenamento que podem ser utilizados pela nova versão da sua aplicação. A versão antiga da sua aplicação continuará a ser executada com o conjunto anterior de artefactos de armazenamento.

A implementação lado a lado é a técnica recomendada para implementar novas versões das suas aplicações de funções.

Nota

Esta orientação para a estratégia de implementação lado a lado utiliza termos específicos do Armazenamento do Azure, mas aplica-se geralmente a todos os fornecedores de armazenamento Durable Functions suportados.

Blocos de implementação

Ao efetuar implementações lado a lado no Funções do Azure ou Serviço de Aplicações do Azure, recomendamos que implemente a nova versão da aplicação de funções num novo Bloco de implementação. Os blocos de implementação permitem-lhe executar várias cópias da sua aplicação de funções lado a lado com apenas uma delas como o bloco de produção ativo. Quando estiver pronto para expor a nova lógica de orquestração à sua infraestrutura existente, pode ser tão simples como trocar a nova versão para o bloco de produção.

Nota

Esta estratégia funciona melhor quando utiliza acionadores HTTP e webhook para funções de orquestrador. Para acionadores não HTTP, como filas ou Hubs de Eventos, a definição do acionador deve derivar de uma definição de aplicação que é atualizada como parte da operação de troca.

Passos seguintes