Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Repetir funções assíncronas

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

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

Semântica de repetição

O Cloud Functions garante a execução pelo menos uma vez de uma função orientada a eventos para cada evento emitido por uma fonte de eventos. No entanto, por padrão, se uma chamada de função terminar com um erro, a função não será invocada novamente e o evento será descartado. Quando você ativa 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 repetição expire (por padrão, após 7 dias).

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

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 lançados no próprio código da função. Algumas das razões pelas quais isso pode acontecer são as seguintes:

  • 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 ponto de extremidade de serviço ou expira ao tentar alcançar o ponto de extremidade.
  • A função lança intencionalmente uma exceção (por exemplo, quando um parâmetro falha na validação).
  • Quando funções escritas em Node.js retornam uma promessa rejeitada ou passam um valor não null para um retorno de chamada.

Em qualquer um dos casos acima, a função para de executar por padrão e o evento é descartado. Se você quiser repetir a função quando ocorrer um erro, poderá alterar a política de repetição padrão definindo a propriedade "retry on failure" . Isso faz com que o evento seja repetido repetidamente por vários dias até que a função seja concluída com êxito.

Habilitando e desabilitando novas tentativas

Como usar o console do GCP

Você pode ativar ou desativar novas tentativas no Console do GCP da seguinte maneira:

  1. Acesse a página Visão geral do Cloud Functions no Console do Cloud Platform.

  2. Clique em Criar função . Como alternativa, clique em uma função existente para acessar sua página de detalhes e clique em Editar .

  3. Preencha os campos obrigatórios para sua função.

  4. Verifique se o campo Trigger está definido para um tipo de gatilho baseado em evento, como Cloud Pub/Sub ou Cloud Storage.

  5. Expanda as configurações avançadas clicando em Mais .

  6. Marque ou desmarque a caixa Repetir em caso de falha .

No código de função

Com o Cloud Functions para Firebase, você pode ativar novas tentativas no código para 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 melhores práticas para usar novas tentativas.

Use a repetição 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 de seu código por meio de testes antes de habilitar 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 ao tentar novamente, 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 loops contínuos 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 essa técnica só funciona se sua função for iniciada com êxito 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 tempo. 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 idempotentes funções orientadas a eventos que podem ser repetidas

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 que você forneça uma chave de idempotência como parâmetro. Se você estiver usando essa API, deverá usar o ID do evento como a 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 idempotente internamente. Por exemplo:
    • Certifique-se de que as mutações possam ocorrer 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 são 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.
  • Lidar com chamadas de função duplicadas fora de banda. Por exemplo, tenha um processo de limpeza separado que limpe após chamadas de função duplicadas.