Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
É inevitável que as funções sejam adicionadas, removidas e alteradas ao longo da vida útil de um aplicativo. As funções duráveis permitem encadear funções de maneiras que não eram possíveis anteriormente, e esse encadeamento afeta como você pode lidar com o controle de versão.
Tipos de alterações disruptivas
Há vários exemplos de mudanças de quebra a ter em conta. Este artigo discute os mais comuns. O tema principal por trás de todos eles é que as orquestrações das funções novas e existentes são afetadas por alterações no código das funções.
Mudança de assinaturas de atividades ou funções de entidades
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 feito numa atividade ou função de uma entidade, poderá interromper qualquer função do orquestrador que dependa dela. Isto é especialmente verdadeiro para linguagens de tipo seguro. Se você atualizar a função orchestrator para acomodar essa alteração, poderá interromper instâncias existentes em voo.
Como 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 pega nos resultados de Foo e passa-os para Bar. Vamos supor que precisamos alterar o valor de retorno de Foo de um Boolean para um String para suportar uma variedade maior de valores de resultado. O resultado é assim:
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
string result = await context.CallActivityAsync<string>("Foo");
await context.CallActivityAsync("Bar", result);
}
Essa alteração funciona bem para todas as novas instâncias da função orchestrator, 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 de volta um valor booleano e, em seguida, pontos de verificação. Se a alteração de assinatura for implantada neste ponto, a instância de ponto de verificação falhará imediatamente quando for retomada e reproduzir a chamada para Foo. Essa falha acontece porque o resultado na tabela de histórico é um valor booleano, mas o novo código tenta desserializá-lo em um valor String, resultando em comportamento inesperado ou até mesmo exceção de tempo de execução para linguagens de tipo seguro.
Este exemplo é apenas uma das muitas maneiras diferentes pelas quais uma alteração de assinatura de função pode quebrar instâncias existentes. Em geral, se um orquestrador precisa mudar a maneira como chama uma função, então a mudança provavelmente será problemática.
Alterando a lógica do orquestrador
A outra classe de problemas de controle de versão vem da alteração do código de 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 você queira 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);
}
Essa alteração adiciona uma nova chamada de função para SendNotification entre Foo e Bar. Não há alterações de assinatura. O problema surge quando uma instância existente é retomada da chamada para Bar. Durante o replay, se a chamada original para Foo retornou true, então o replay do orquestrador chamará SendNotification, que não está em seu histórico de execução. O tempo de execução deteta essa inconsistência e gera um erro de orquestração não determinística porque encontrou uma chamada para SendNotification quando esperava ver uma chamada para Bar. O mesmo tipo de problema pode ocorrer ao adicionar chamadas de API a outras operações duráveis, como criar temporizadores duráveis, esperar por eventos externos, chamar suborquestrações, etc.
Estratégias de mitigação
Aqui estão algumas das estratégias para lidar com desafios de controle de versão:
- Não fazer nada (não recomendado)
- Versionamento de orquestração (recomendado na maioria dos casos)
- Pare todas as instâncias em voo
- Implantações lado a lado
Não fazer nada
A abordagem ingênua do versionamento é não fazer nada e deixar que as instâncias de orquestração em voo falhem. Dependendo do tipo de alteração, os seguintes tipos de falhas podem ocorrer.
- As orquestrações podem falhar com um erro de orquestração não determinístico .
- As orquestrações podem ficar presas indefinidamente, reportando um estado
Running. - Se uma função for removida, qualquer função que tente chamá-la pode falhar com um erro.
- Se uma função for removida depois de agendada para ser executada, o aplicativo poderá enfrentar falhas de tempo de execução de baixo nível no mecanismo do Durable Task Framework, resultando potencialmente em grave degradação do desempenho.
Devido a essas falhas potenciais, a estratégia "não fazer nada" não é recomendada.
Versão de orquestração
Observação
A versão de orquestração está atualmente em versão preliminar pública.
O recurso de versionamento de orquestração permite que diferentes versões de orquestrações coexistam e sejam executadas simultaneamente sem conflitos e problemas de não-determinismo, tornando possível implantar atualizações enquanto permite que instâncias de orquestração em voo sejam concluídas sem intervenção manual.
Com versionamento de orquestração:
- Cada instância de orquestração recebe uma versão permanentemente associada a ela quando criada
- As funções do Orquestrador podem examinar a sua versão e executar ramificações em conformidade.
- Os trabalhadores que executam versões mais recentes da função do orquestrador podem continuar executando instâncias de orquestração criadas por versões mais antigas
- O tempo de execução impede que os trabalhadores que executam versões mais antigas da função orchestrator executem orquestrações de versões mais recentes
Essa estratégia é recomendada para aplicativos que precisam oferecer suporte a alterações significativas enquanto mantêm implantações sem tempo de inatividade.
Para obter orientações detalhadas de configuração e implementação, consulte Versionamento de orquestração em funções duráveis.
Pare todas as instâncias em voo
Outra opção é parar todas as instâncias em voo. Se estiver a usar o provedor de Armazenamento Azure padrão para Funções Duráveis, pode interromper todas as instâncias limpando o conteúdo das filas internas de Fila de Controle e Fila de Itens de Trabalho. Como alternativa, você pode parar o aplicativo de função, excluir essas filas e reiniciar o aplicativo novamente. As filas serão recriadas automaticamente assim que o aplicativo for reiniciado. As instâncias de orquestração anteriores podem permanecer no estado "Em execução" indefinidamente, mas não sobrecarregarão seus logs com mensagens de falha ou causarão qualquer dano ao seu aplicativo. Esta abordagem é ideal no desenvolvimento rápido de protótipos, incluindo o desenvolvimento local.
Observação
Essa abordagem requer acesso direto aos recursos de armazenamento subjacentes e pode não ser apropriada para todos os provedores de armazenamento suportados pelo Durable Functions.
Implantações lado a lado
A maneira mais à prova de falhas para garantir que as alterações significativas sejam implantadas com segurança é implantando-as lado a lado com suas versões mais antigas. Isso pode ser feito usando qualquer uma das seguintes técnicas:
- Implante todas as atualizações como funções totalmente novas, deixando as funções existentes as-is. Isso geralmente não é recomendado devido à complexidade envolvida na atualização recursiva dos chamadores das novas versões de funções.
- Implante todas as atualizações como um novo aplicativo de função com uma conta de armazenamento diferente.
- Implante uma nova cópia do aplicativo de função com a mesma conta de armazenamento, mas com um nome de hub de tarefas atualizado. Isso resulta na criação de novos artefatos de armazenamento que podem ser usados pela nova versão do seu aplicativo. A versão antiga do seu aplicativo continuará a ser executada usando o conjunto anterior de artefatos de armazenamento.
A implantação lado a lado é a técnica recomendada para implantar novas versões de seus aplicativos de função.
Observação
Esta orientação para a estratégia de implantação lado a lado usa termos específicos do Armazenamento do Azure, mas se aplica geralmente a todos os provedores de armazenamento de Funções Duráveis com suporte.
Blocos de implementação
Ao fazer implantações lado a lado no Azure Functions ou no Serviço de Aplicativo do Azure, recomendamos que você implante a nova versão do aplicativo de função em um novo slot de Implantação. Os slots de implantação permitem executar várias cópias da sua aplicação de funções lado a lado, com apenas uma delas como slot de produção ativo. Quando você estiver pronto para expor a nova lógica de orquestração à sua infraestrutura existente, pode ser tão simples quanto trocar a nova versão para o slot de produção.
Observação
Essa estratégia funciona melhor quando você usa gatilhos HTTP e webhook para funções de orquestrador. Para gatilhos não-HTTP, como filas ou Hubs de Eventos, a definição de gatilho deve derivar de uma configuração de aplicativo que é atualizada como parte da operação de permuta.