Repetir funções assíncronas

Este documento descreve como você pode solicitar funções de segundo plano assíncronas (não HTTPS) para tentar novamente em caso de falha.

Semântica de nova tentativa

O Cloud Functions garante a execução pelo menos uma vez de uma função orientada a eventos para cada evento emitido por uma origem de eventos. Por padrão, se uma invocação de função terminar com um erro, a função não será invocada novamente e o evento será descartado. Quando você habilita novas tentativas em uma função orientada a eventos, o Cloud Functions tenta novamente uma invocação de função com falha até que ela seja concluída com êxito ou a janela de novas tentativas expire.

Para funções de 2ª geração, esta janela de nova tentativa expira após 24 horas. Para funções de 1ª geração, expira após 7 dias. O Cloud Functions tenta novamente funções orientadas a eventos recém-criadas usando uma estratégia de espera exponencial, com uma espera crescente entre 10 e 600 segundos. Esta política é aplicada a novas funções na primeira vez que você as implanta. Ele não é aplicado retroativamente a funções existentes que foram implantadas pela primeira vez antes que as alterações descritas nesta nota de versão entrassem em vigor, mesmo se você reimplantar as funções.

Quando as novas tentativas não estão habilitadas para uma função, que é o padrão, a função sempre informa que foi executada com êxito e 200 OK podem aparecer em seus logs. Isso ocorre mesmo se a função encontrar um erro. Para deixar claro quando sua função encontra um erro, certifique-se de relatar os erros de forma adequada.

Por que as funções orientadas a eventos não são concluídas

Em raras ocasiões, uma função pode ser encerrada prematuramente devido a um erro interno e, por padrão, a função pode ou não ser repetida automaticamente.

Mais normalmente, uma função orientada a eventos pode não ser concluída com êxito devido a erros gerados no próprio código da função. Os motivos pelos quais isso pode acontecer incluem:

  • A função contém um bug e o tempo de execução lança uma exceção.
  • A função não pode alcançar um terminal de serviço ou expira ao tentar fazer isso.
  • A função lança intencionalmente uma exceção (por exemplo, quando um parâmetro falha na validação).
  • Uma função Node.js retorna uma promessa rejeitada ou passa um valor não null para um retorno de chamada.

Em qualquer um dos casos acima, a função para de ser executada por padrão e o evento é descartado. Para tentar novamente a função quando ocorrer um erro, você pode alterar a política de repetição padrão definindo a propriedade "repetir em caso de falha" . Isso faz com que o evento seja repetido repetidamente até que a função seja concluída com êxito ou o tempo limite de nova tentativa expire.

Habilitar ou desabilitar novas tentativas

Configurar novas tentativas no Console do GCP

Se você estiver criando uma nova função:

  1. Na tela Criar função , selecione Adicionar gatilho e escolha o tipo de evento que atuará como gatilho para sua função.
  2. No painel do acionador do Eventarc , marque a caixa de seleção Tentar novamente em caso de falha para ativar novas tentativas.

Se você estiver atualizando uma função existente:

  1. Na página Visão geral do Cloud Functions , clique no nome da função que você está atualizando para abrir a tela Detalhes da função e escolha Editar na barra de menus para exibir os painéis de gatilho HTTPS e Eventarc .
  2. No painel do gatilho do Eventarc , clique no ícone para editar as configurações do seu gatilho.
  3. No painel do acionador do Eventarc , marque ou desmarque a caixa de seleção Tentar novamente em caso de falha para ativar ou desativar novas tentativas.

Configure novas tentativas a partir do seu código de função

Com o Cloud Functions para Firebase, você pode ativar novas tentativas no código de uma função. Para fazer isso para uma função em segundo plano, como functions.foo.onBar(myHandler); , use runWith e configure uma política de falha:

functions.runWith({failurePolicy: true}).foo.onBar(myHandler);

Definir true conforme mostrado configura uma função para tentar novamente em caso de falha.

Melhores Práticas

Esta seção descreve as práticas recomendadas para usar novas tentativas.

Use nova tentativa para lidar com erros transitórios

Como sua função é repetida continuamente até a execução bem-sucedida, erros permanentes, como bugs, devem ser eliminados do seu código por meio de testes antes de ativar novas tentativas. As novas tentativas são melhor usadas para lidar com falhas intermitentes/transitórias que têm uma alta probabilidade de resolução após novas tentativas, como um ponto de extremidade de serviço instável ou tempo limite.

Defina uma condição final para evitar loops de repetição infinitos

É uma prática recomendada proteger sua função contra loop contínuo ao usar novas tentativas. Você pode fazer isso incluindo uma condição final bem definida, antes que a função comece a ser processada. Observe que esta técnica só funciona se sua função for iniciada com sucesso e for capaz de avaliar a condição final.

Uma abordagem simples, mas eficaz, é descartar eventos com carimbos de data/hora anteriores a um determinado horário. Isso ajuda a evitar execuções excessivas quando as falhas são persistentes ou duram mais do que o esperado.

Por exemplo, este snippet de código descarta todos os eventos com mais de 10 segundos:

const eventAgeMs = Date.now() - Date.parse(event.timestamp);
const eventMaxAgeMs = 10000;
if (eventAgeMs > eventMaxAgeMs) {
  console.log(`Dropping event ${event} with age[ms]: ${eventAgeMs}`);
  callback();
  return;
}

Use catch com promessas

Se sua função tiver novas tentativas habilitadas, qualquer erro não tratado acionará uma nova tentativa. Certifique-se de que seu código capture quaisquer erros que não devam resultar em uma nova tentativa.

Aqui está um exemplo do que você deve fazer:

return doFooAsync().catch((err) => {
    if (isFatal(err)) {
        console.error(`Fatal error ${err}`);
    }
    return Promise.reject(err);
});

Tornar funções acionadas por eventos repetíveis idempotentes

As funções orientadas a eventos que podem ser repetidas devem ser idempotentes. Aqui estão algumas diretrizes gerais para tornar tal função idempotente:

  • Muitas APIs externas (como Stripe) permitem fornecer uma chave de idempotência como parâmetro. Se você estiver usando essa API, deverá usar o ID do evento como chave de idempotência.
  • A idempotência funciona bem com entrega pelo menos uma vez, porque torna seguro tentar novamente. Portanto, uma prática recomendada geral para escrever código confiável é combinar idempotência com novas tentativas.
  • Certifique-se de que seu código seja internamente idempotente. Por exemplo:
    • Certifique-se de que as mutações possam acontecer mais de uma vez sem alterar o resultado.
    • Consulte o estado do banco de dados em uma transação antes de alterar o estado.
    • Certifique-se de que todos os efeitos colaterais sejam idempotentes.
  • Imponha uma verificação transacional fora da função, independente do código. Por exemplo, persista o estado em algum lugar registrando que um determinado ID de evento já foi processado.
  • Lide com chamadas de função duplicadas fora da banda. Por exemplo, tenha um processo de limpeza separado que limpe após chamadas de função duplicadas.

Configurar a política de novas tentativas

Dependendo das necessidades do seu Cloud Function, talvez você queira configurar a política de nova tentativa diretamente. Isso permitiria que você configurasse qualquer combinação do seguinte:

  • Reduza a janela de novas tentativas de 7 dias para apenas 10 minutos.
  • Altere o tempo de espera mínimo e máximo para a estratégia de nova tentativa de espera exponencial.
  • Altere a estratégia de nova tentativa para tentar novamente imediatamente.
  • Configure um tópico de mensagens não entregues .
  • Defina um número máximo e mínimo de tentativas de entrega.

Para configurar a política de nova tentativa:

  1. Escreva uma função HTTP.
  2. Use a API Pub/Sub para criar uma assinatura do Pub/Sub, especificando o URL da função como destino.

Consulte a documentação do Pub/Sub sobre como lidar com falhas para obter mais informações sobre como configurar o Pub/Sub diretamente.