Ative as novas tentativas de funções acionadas por eventos (1.ª geração)
Este documento descreve como ativar a repetição para funções orientadas por eventos. As novas tentativas automáticas não estão disponíveis para funções HTTP.
Semântica da nova tentativa
As funções do Cloud Run oferecem uma execução, pelo menos, uma vez de uma função orientada por eventos para cada evento emitido por uma origem de eventos. Por predefinição, se uma invocação de função terminar com um erro, a função não é invocada novamente e o evento é ignorado. Quando ativa as repetições numa função baseada em eventos, o Cloud Run Functions repete uma invocação de função com falha até ser concluída com êxito ou o período de repetição expirar.
Esta janela de nova tentativa expira após 7 dias. As funções do Cloud Run repetem as funções acionadas por eventos recém-criadas através de uma estratégia de recuo exponencial, com um recuo crescente entre 10 e 600 segundos. Esta política é aplicada a novas funções na primeira vez que as implementa. Não é aplicada retroativamente às funções existentes que foram implementadas pela primeira vez antes de as alterações descritas nesta nota de lançamento entrarem em vigor, mesmo que reimplemente as funções.
Quando as novas tentativas não estão ativadas para uma função, que é a predefinição, a função
comunica sempre que foi executada com êxito e os códigos de resposta 200 OK
podem
aparecer nos respetivos registos. Isto ocorre mesmo que a função tenha encontrado um erro. Para deixar claro quando a sua função encontra um erro, certifique-se de que comunica os erros de forma adequada.
Por que motivo as funções acionadas por eventos não são concluídas
Em raras ocasiões, uma função pode terminar prematuramente devido a um erro interno e, por predefinição, a função pode ou não ser repetida automaticamente.
Normalmente, uma função acionada por 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 isto pode acontecer incluem:
- A função contém um erro e o tempo de execução gera uma exceção.
- A função não consegue alcançar um ponto final de serviço ou excede o tempo limite ao tentar fazê-lo.
- A função gera intencionalmente uma exceção (por exemplo, quando um parâmetro falha a validação).
- Uma função Node.js devolve uma promessa rejeitada ou passa um valor não-
null
para um retorno.
Em qualquer um destes casos, a função deixa de ser executada por predefinição e o evento é rejeitado. Para repetir a função quando ocorre um erro, pode alterar a política de repetição predefinida definindo a propriedade "retry on failure". Isto faz com que o evento seja repetido várias vezes até que a função seja concluída com êxito ou o tempo limite de repetição expire.
Ative ou desative as novas tentativas
Para ativar ou desativar as repetições, pode usar a gcloud
ferramenta de linha de comandos
ou a Google Cloud consola. Por predefinição, as novas tentativas estão desativadas.
Configure as repetições a partir da ferramenta de linha de comandos gcloud
Para ativar as novas tentativas através da ferramenta de linha de comandos gcloud
, inclua a flag --retry
ao implementar a sua função:
gcloud functions deploy FUNCTION_NAME --retry FLAGS...
Para desativar as novas tentativas, volte a implementar a função sem a flag --retry
:
gcloud functions deploy FUNCTION_NAME FLAGS...
Configure novas tentativas a partir da consola
Se estiver a criar uma nova função:
- No ecrã Criar função, em Acionador, escolha o tipo de evento que vai atuar como acionador para a sua função.
- Selecione a caixa de verificação Tentar novamente em caso de falha para ativar as novas tentativas.
Se estiver a atualizar uma função existente:
- Na página Vista geral das funções do Cloud Run, clique no nome da função que está a atualizar para abrir o ecrã Detalhes da função e, de seguida, escolha Editar na barra de menu para apresentar o painel Acionador.
- Selecione ou desmarque a caixa de verificação Repetir em caso de falha para ativar ou desativar as repetições.
Práticas recomendadas
Esta secção descreve as práticas recomendadas para usar novas tentativas.
Use a repetição para processar erros momentâneos
Uma vez que a sua função é repetida continuamente até à execução bem-sucedida, os erros permanentes, como erros de programação, devem ser eliminados do seu código através de testes antes de ativar as repetições. As novas tentativas são mais adequadas para lidar com falhas intermitentes ou transitórias que têm uma elevada probabilidade de resolução após uma nova tentativa, como um ponto final de serviço instável ou um limite de tempo.
Defina uma condição de fim para evitar ciclos de repetição infinitos
É uma prática recomendada proteger a sua função contra ciclos contínuos quando usa novas tentativas. Pode fazê-lo incluindo uma condição de fim bem definida antes de a função começar o processamento. Tenha em atenção que esta técnica só funciona se a função for iniciada com êxito e conseguir avaliar a condição final.
Uma abordagem simples, mas eficaz, consiste em rejeitar eventos com data/hora anteriores a um determinado período. Isto ajuda a evitar execuções excessivas quando as falhas são persistentes ou duram mais tempo do que o esperado.
Por exemplo, este fragmento do código rejeita todos os eventos com mais de 10 segundos:
Node.js
Python
Go
Java
C#
using CloudNative.CloudEvents; using Google.Cloud.Functions.Framework; using Google.Events.Protobuf.Cloud.PubSub.V1; using Microsoft.Extensions.Logging; using System; using System.Threading; using System.Threading.Tasks; namespace TimeBoundedRetries; public class Function : ICloudEventFunction<MessagePublishedData> { private static readonly TimeSpan MaxEventAge = TimeSpan.FromSeconds(10); private readonly ILogger _logger; // Note: for additional testability, use an injectable clock abstraction. public Function(ILogger<Function> logger) => _logger = logger; public Task HandleAsync(CloudEvent cloudEvent, MessagePublishedData data, CancellationToken cancellationToken) { string textData = data.Message.TextData; DateTimeOffset utcNow = DateTimeOffset.UtcNow; // Every PubSub CloudEvent will contain a timestamp. DateTimeOffset timestamp = cloudEvent.Time.Value; DateTimeOffset expiry = timestamp + MaxEventAge; // Ignore events that are too old. if (utcNow > expiry) { _logger.LogInformation("Dropping PubSub message '{text}'", textData); return Task.CompletedTask; } // Process events that are recent enough. // If this processing throws an exception, the message will be retried until either // processing succeeds or the event becomes too old and is dropped by the code above. _logger.LogInformation("Processing PubSub message '{text}'", textData); return Task.CompletedTask; } }
Ruby
PHP
/** * This function shows an example method for avoiding infinite retries in * Google Cloud Functions. By default, functions configured to automatically * retry execution on failure will be retried indefinitely - causing an * infinite loop. To avoid this, we stop retrying executions (by not throwing * exceptions) for any events that are older than a predefined threshold. */ use Google\CloudFunctions\CloudEvent; function avoidInfiniteRetries(CloudEvent $event): void { $log = fopen(getenv('LOGGER_OUTPUT') ?: 'php://stderr', 'wb'); $eventId = $event->getId(); // The maximum age of events to process. $maxAge = 10; // 10 seconds // The age of the event being processed. $eventAge = time() - strtotime($event->getTime()); // Ignore events that are too old if ($eventAge > $maxAge) { fwrite($log, 'Dropping event ' . $eventId . ' with age ' . $eventAge . ' seconds' . PHP_EOL); return; } // Do what the function is supposed to do fwrite($log, 'Processing event: ' . $eventId . ' with age ' . $eventAge . ' seconds' . PHP_EOL); // infinite_retries failed function executions $failed = true; if ($failed) { throw new Exception('Event ' . $eventId . ' failed; retrying...'); } }
Distinguir entre funções que podem ser repetidas e erros fatais
Se a sua função tiver repetições ativadas, qualquer erro não processado aciona uma repetição. Certifique-se de que o seu código capta todos os erros que não devem resultar numa nova tentativa.
Node.js
Python
Go
Java
C#
using CloudNative.CloudEvents; using Google.Cloud.Functions.Framework; using Google.Events.Protobuf.Cloud.PubSub.V1; using Microsoft.Extensions.Logging; using System; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace Retry; public class Function : ICloudEventFunction<MessagePublishedData> { private readonly ILogger _logger; public Function(ILogger<Function> logger) => _logger = logger; public Task HandleAsync(CloudEvent cloudEvent, MessagePublishedData data, CancellationToken cancellationToken) { bool retry = false; string text = data.Message?.TextData; // Get the value of the "retry" JSON parameter, if one exists. if (!string.IsNullOrEmpty(text)) { JsonElement element = JsonSerializer.Deserialize<JsonElement>(data.Message.TextData); retry = element.TryGetProperty("retry", out var property) && property.ValueKind == JsonValueKind.True; } // Throwing an exception causes the execution to be retried. if (retry) { throw new InvalidOperationException("Retrying..."); } else { _logger.LogInformation("Not retrying..."); } return Task.CompletedTask; } }
Ruby
PHP
use Google\CloudFunctions\CloudEvent; function tipsRetry(CloudEvent $event): void { $cloudEventData = $event->getData(); $pubSubData = $cloudEventData['message']['data']; $json = json_decode(base64_decode($pubSubData), true); // Determine whether to retry the invocation based on a parameter $tryAgain = $json['some_parameter']; if ($tryAgain) { /** * Functions with automatic retries enabled should throw exceptions to * indicate intermittent failures that a retry might fix. In this * case, a thrown exception will cause the original function * invocation to be re-sent. */ throw new Exception('Intermittent failure occurred; retrying...'); } /** * If a function with retries enabled encounters a non-retriable * failure, it should return *without* throwing an exception. */ $log = fopen(getenv('LOGGER_OUTPUT') ?: 'php://stderr', 'wb'); fwrite($log, 'Not retrying' . PHP_EOL); }
Torne as funções acionadas por eventos repetíveis idempotentes
As funções acionadas por eventos que podem ser repetidas têm de ser idempotentes. Seguem-se algumas diretrizes gerais para tornar uma função deste tipo idempotente:
- Muitas APIs externas (como a Stripe) permitem fornecer uma chave de idempotência como um parâmetro. Se estiver a usar uma API deste tipo, deve usar o ID do evento como chave de idempotência.
- A idempotência funciona bem com o fornecimento pelo menos uma vez, porque torna a repetição segura. Assim, uma prática recomendada geral para escrever código fiável é combinar a idempotência com as novas tentativas.
- Certifique-se de que o seu código é idempotente internamente. Por exemplo:
- Certifique-se de que as mutações podem ocorrer mais do que uma vez sem alterar o resultado.
- Consultar o estado da base de dados numa transação antes de alterar o estado.
- Certifique-se de que todos os efeitos secundários são idempotentes.
- Impor uma verificação transacional fora da função, independentemente do código. Por exemplo, manter o estado num local que registe que um determinado ID do evento já foi processado.
- Lidar com chamadas de funções duplicadas fora da banda. Por exemplo, ter um processo de limpeza separado que limpe após chamadas de funções duplicadas.
Configure a política de repetição
Consoante as necessidades da sua função, pode querer configurar a política de repetição diretamente. Isto permite-lhe configurar qualquer combinação do seguinte:
- Reduzir o período de nova tentativa de 7 dias para apenas 10 minutos.
- Altere o tempo de recuo mínimo e máximo para a estratégia de repetição de recuo exponencial.
- Altere a estratégia de repetição para repetir 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 repetição:
- Escrever uma função HTTP.
- Use a API Pub/Sub para criar uma subscrição do Pub/Sub, especificando o URL da função como o destino.
Consulte a documentação do Pub/Sub sobre como processar falhas para obter mais informações sobre a configuração direta do Pub/Sub.
Passos seguintes
- Implementar funções do Cloud Run.
- Chamar funções de acionador do Pub/Sub.
- Chamar funções de acionamento do Cloud Storage.
- Funções do Cloud Run com tutorial do Pub/Sub.
- Funções do Cloud Run com o tutorial do Cloud Storage.