Tratamento de erros em funções
Nesta página
Esta página explica como trabalhar com erros em Atlas Functions.
Observação
Tratamento de erros personalizado para Atlas Triggers de reconhecimento de data center usando o Amazon Web Services Eventbridge
Você pode criar um manipulador de erros personalizado especificamente para acionadores de banco de dados de dados usando AWS Eventbridge. Para obter mais informações, consulte Tratamento personalizado de erros.
Você pode gerenciar erros de função usando técnicas padrão do JavaScript, como declarações try...catch .
function willThrowAndHandleError() { try { throw new Error("This will always happen"); } catch (err) { console.error("An error occurred. Error message:" + err.message); } } exports = willThrowAndHandleError;
Exibir registros
Você pode visualizar registros de todas as execuções de Função invocadas por um Atlas trigger na página Logs. Isso inclui funções que não foram executadas com sucesso devido a um erro.
Para obter mais informações, consulte Registros de trigger.
Repetir funções
Atlas Functions não têm um comportamento integrado de novas tentativas.
Você pode adicionar um comportamento de novas tentativas personalizado. Por exemplo, talvez você queira adicionar um comportamento de novas tentativas se o serviço de terceiros que sua função chama tiver conectividade intermitente e você deseja que a função seja executada novamente mesmo que o serviço de terceiros esteja temporariamente inativo.
As seções a seguir descrevem as estratégias disponíveis para adicionar um comportamento de novas tentativas às suas funções:
Chame funções recursivamente em blocos de gerenciamento de erros
Usar gatilhos de banco de dados para tentar as funções novamente
Função de chamada recursiva em blocos de tratamento de erros
Você pode gerenciar operações que podem falhar ao chamar uma função recursivamente.
Em um alto nível, este processo inclui os seguintes componentes:
Execute as operações que deseja tentar novamente em uma declaração
try
e faça com que a função chame a si mesma em uma declaraçãocatch
.Para evitar a execução indefinida, defina um número máximo de tentativas. Toda vez que a função falhar e inserir a declaração
catch
, incremente uma contagem do número atual de tentativas. Pare a execução recursiva quando o número atual de novas tentativas da função atingir o número máximo de tentativas.Você também pode limitar as novas tentativas para reduzir o número total de execuções em um período.
A tabela a seguir descreve algumas vantagens e desvantagens de lidar com novas tentativas de função com a estratégia de chamada recursiva.
Vantagens | Desvantagens |
---|---|
|
|
O exemplo de código a seguir demonstra uma implementação de uma nova tentativa de uma função usando recursão em blocos de tratamento de erros:
// Utility function to suspend execution of current process async function sleep(milliseconds) { await new Promise((resolve) => setTimeout(resolve, milliseconds)); } // Set variables to be used by all calls to `mightFail` // Tip: You could also store `MAX_RETRIES` and `THROTTLE_TIME_MS` // in App Services Values const MAX_RETRIES = 5; const THROTTLE_TIME_MS = 5000; let currentRetries = 0; let errorMessage = ""; async function mightFail(...inputVars) { if (currentRetries === MAX_RETRIES) { console.error( `Reached maximum number of retries (${MAX_RETRIES}) without successful execution.` ); console.error("Error Message:", errorMessage); return; } let res; try { // operation that might fail res = await callFlakyExternalService(...inputVars); } catch (err) { errorMessage = err.message; // throttle retries await sleep(THROTTLE_TIME_MS); currentRetries++; res = await mightFail(...inputVars); } return res; } exports = mightFail;
Use gatilhos de banco de dados (trigger) para tentar novamente
Você também pode tentar as funções novamente usando um gatilho de banco de dados para executar novas tentativas e uma collection MongoDB para acompanhar execuções com falha anterior.
Em um alto nível, este processo inclui os seguintes componentes:
Função principal que executa a lógica que você deseja tentar novamente, envolta na função do manipulador.
Coleção MongoDB do rastreador de execução com falha que rastreia execuções com falha da função principal.
Função de manipulador que invoca a função principal e registra quando a função falha na collection do rastreador de execução com falha.
Função de acionamento do banco de dados que executa novamente a função de manipulador sempre que a função de manipulador adiciona um erro à coleção de rastreadores de execução com falha.
Você pode suportar várias funções principais com um conjunto de uma função de manipulador, collection do rastreador de execução e função de gatilho de banco de dados (trigger).
Vantagens | Desvantagens |
---|---|
|
|
Você pode criar um mecanismo de repetição para funções na UI do Atlas ou usando a App Services CLI. O procedimento a seguir mostra como criar um manipulador, uma nova função de gatilho de banco de dados (trigger) e uma função principal.
Criar a função de manipulador
Primeiro, crie a função handleRetry
do manipulador que invoca a função principal.
handleRetry
aceita os seguintes parâmetros:
Parâmetro | Tipo | Descrição |
---|---|---|
functionToRetry | função de JavaScript | Função para tentar novamente. |
functionName | String | Nome da função que deseja tentar novamente. |
operationId | ObjectId | Identificador único para a execução da função principal, incluindo novas tentativas. |
previousRetries | Número | Quantas vezes a função principal já foi tentada anteriormente. |
...args | Parâmetros de repouso | Número indefinido de argumentos passados para a função principal. |
handleRetry
realiza as seguintes operações:
Tenta executar
functionToRetry
em uma declaraçãotry
. Se a execução for bem-sucedida,handleRetry
retorna o valor retornado porfunctionToRetry
.Se a execução de
functionToRetry
na etapa anterior gerar um erro, a declaraçãocatch
gerencia o erro da seguinte maneira:Verifica se o número de tentativas anteriores é igual ao número máximo permitido de tentativas. Se os dois números forem iguais, a função gerará um erro porque o máximo de tentativas foi atingido. A função não tenta novamente.
Crie um objeto de entrada de registro de execução de função para inserir no banco de dados.
Obtenha uma referência à collection de rastreadores de execução com falha.
Insira a entrada do registro de execução do registro de função na coleção rastreadora de execução que falhou. Essa operação de inserção faz com que a função de gatilho de banco de dados (trigger), que você fará na próxima etapa, seja acionada.
A função principal é passada como argumento functionToRetry
. handleRetry
tenta executar a função principal. Se a execução falhar, esta função tentará novamente a função principal.
Navegue até Functions. Clique no botão Create New Function.
No campo Name, adicione
handleRetry
.No Function Editor, adicione o seguinte código e, em seguida, salve a Função:
handleRetry.js// Tip: You could also put this in an App Services Value const MAX_FUNC_RETRIES = 5; async function handleRetry( functionToRetry, functionName, operationId, previousRetries, ...args ) { try { // Try to execute the main function const response = await functionToRetry(...args); return response; } catch (err) { // Evaluates if should retry function again. // If no retry, throws error and stops retrying. if (previousRetries === MAX_FUNC_RETRIES) { throw new Error( `Maximum number of attempts reached (${MAX_FUNC_RETRIES}) for function '${functionName}': ${err.message}` ); } // Build function execution log entry for insertion into database. const logEntry = { operationId, errorMessage: err.message, timestamp: new Date(), retries: previousRetries + 1, args, functionName, }; // Get reference to database collection const executionLog = context.services .get("mongodb-atlas") .db("logs") .collection("failed_execution_logs"); // Add execution log entry to database await executionLog.insertOne(logEntry); return; } } exports = handleRetry;
Adicione o seguinte a
functions/config.json
:functions/config.json[ { "name": "handleRetry", "private": true, "run_as_system": true } // ...other configuration ] Crie o arquivo para a Função
functions/handleRetry.js
:functions/handleRetry.js// Tip: You could also put this in an App Services Value const MAX_FUNC_RETRIES = 5; async function handleRetry( functionToRetry, functionName, operationId, previousRetries, ...args ) { try { // Try to execute the main function const response = await functionToRetry(...args); return response; } catch (err) { // Evaluates if should retry function again. // If no retry, throws error and stops retrying. if (previousRetries === MAX_FUNC_RETRIES) { throw new Error( `Maximum number of attempts reached (${MAX_FUNC_RETRIES}) for function '${functionName}': ${err.message}` ); } // Build function execution log entry for insertion into database. const logEntry = { operationId, errorMessage: err.message, timestamp: new Date(), retries: previousRetries + 1, args, functionName, }; // Get reference to database collection const executionLog = context.services .get("mongodb-atlas") .db("logs") .collection("failed_execution_logs"); // Add execution log entry to database await executionLog.insertOne(logEntry); return; } } exports = handleRetry; Distribua suas alterações:
Execute o seguinte comando para implementar suas alterações:
appservices push
Criar o triggerde banco de dados de repetição
Navegue até a página Triggers :
Se ainda não tiver sido exibido, selecione a organização que contém seu projeto no menu Organizations na barra de navegação.
Se ainda não estiver exibido, selecione seu projeto no menu Projects na barra de navegação.
Na barra lateral, clique em Triggers sob o título Services.
A página Acionadores é exibida.
Clique em Add a Trigger.
Crie o Trigger com a seguinte configuração:
CampoValorNomeNome da sua escolha (por exemplo:retryOperation
)HabilitadoSimIgnorar eventos ao reabilitarSimClassificação de eventosSimNome do clusterNome da sua escolha (por exemplo:mongodb-atlas
)Nome do banco de dadosNome da sua escolha (por exemplo:logs
)Nome da collectionNome da sua escolha (por exemplo:failed_execution_logs
)Tipo de operaçãoInsertDocumento completoSimPré-imagem do documentoNoSelecione um tipo de eventoFunçãoFunçãoClique em + New Function. Consulte as seguintes informações sobre o conteúdo da função.Configuração avançadaN/A - Nenhuma configuração avançada necessária.Agora adicione o código à função que o trigger invoca.
A função
retryOperation
usa como parâmetrologEntry
o documento que o manipulador de novas tentativas publicou na collection do rastreador de execução que falhou. Em seguida, oretryOperation
utiliza context.functions.execute() para invocar a função principal com informações delogEntry
.No campo Function Name, adicione
retryOperationDbTrigger
.Para o campo Function, adicione o seguinte código e salve o trigger:
functions/retryOperationDbTrigger.jsasync function retryOperation({ fullDocument: logEntry }) { // parse values from log entry posted to database const { args, retries, functionName, operationId } = logEntry; // Re-execute the main function await context.functions.execute(functionName, ...args, operationId, retries); } exports = retryOperation;
Autentique um usuário do MongoDB Atlas :
Use sua chave de API de administração do MongoDB Atlas para fazer login na App Services CLI:
appservices login --api-key="<API KEY>" --private-api-key="<PRIVATE KEY>" Extraia os arquivos de configuração mais recentes do seu aplicativo:
Execute o seguinte comando para obter uma cópia local dos seus arquivos de configuração:
appservices pull --remote=<App ID> Por padrão, o comando extrai arquivos para o diretório de trabalho atual. Você pode especificar um caminho de diretório com a bandeira
--local
opcional.Adicione configuração para o gatilho de banco de dados (trigger). Para obter mais informações, consulte a referência de configuração do trigger.
triggers/retryOperation.json{ "name": "retry", "type": "DATABASE", "config": { "operation_types": ["INSERT"], "database": "logs", "collection": "failed_execution_logs", "service_name": "mongodb-atlas", "project": {}, "full_document": true, "full_document_before_change": false, "unordered": false, "skip_catchup_events": false }, "disabled": false, "event_processors": { "FUNCTION": { "config": { "function_name": "retryOperationDbTrigger" } } } } Agora adicione o código à função
functions/config.json
que o trigger invoca.A função
retryOperation
usa como parâmetrologEntry
o documento que o manipulador de novas tentativas publicou na collection do rastreador de execução que falhou. Em seguida, oretryOperation
utiliza context.functions.execute() para invocar a função principal com informações delogEntry
.functions/config.json[ // ...other configuration { "name": "retryOperationDbTrigger", "private": true } ] Adicione o seguinte código ao arquivo
functions/retryOperationDbTrigger.js
:retryOperationDbTrigger.jsasync function retryOperation({ fullDocument: logEntry }) { // parse values from log entry posted to database const { args, retries, functionName, operationId } = logEntry; // Re-execute the main function await context.functions.execute(functionName, ...args, operationId, retries); } exports = retryOperation; Distribua suas alterações:
Execute o seguinte comando para implementar suas alterações:
appservices push
Criar a função principal
Agora que você tem o gerenciador de função e a Função de gatilho de banco de dados (trigger) de repetição, você pode escrever a função principal.
No exemplo a seguir, a função lança aleatoriamente um erro ao executar a adição. As funções JavaScript que executam essa lógica são as seguintes:
getRandomOneTwoThree()
: Função auxiliar para gerar erros para o exemplo.additionOrFailure()
: Função com a lógica principal.
A invocação de additionOrFailure()
envolta pelo manipulador de novas tentativas ocorre na função exportada additionWithRetryHandler()
. Todas as funções que usam a função de manipulador de repetição devem se assemelhar a essa função.
Você deve incluir os parâmetros corretos para que essa função funcione com o restante da lógica de repetição. Esses parâmetros são:
Parâmetro | Tipo | Descrição |
---|---|---|
...args | Parâmetros de repouso | Zero ou mais parâmetros devem passar para a função com lógica principal. No caso deste exemplo, os dois números adicionados em additionOrFailure() , num1 e num2 . |
operationId | Identificador exclusivo para a chamada de função
e novas tentativas. Defina o valor padrão como new BSON.ObjectId() . | |
retries | Número | Defina o valor padrão como 0. |
O corpo de additionWithRetryHandler
é o manipulador de repetição handleRetry
invocado por context.functions.execute()
, que por sua vez invoca additionOrFailure
. Os argumentos que você passa para context.functions.execute()
são os seguintes:
Argument | Tipo | Descrição |
---|---|---|
"handleRetry" | String | Nome da Função que você definiu para chamar a função principal
e publicar nos registros de repetição, se a função principal não for executada corretamente. |
additionOrFailure | função de JavaScript | A principal função que o handleRetry() invoca. |
operationId | BSON.ObjectId | Passado como argumento do parâmetro operationId de additionWithRetryHandler() . |
retries | Número | Passado como argumento do parâmetro retries de additionWithRetryHandler() . |
...args | Espalhe argumentos | Zero ou mais argumentos devem passar para a função com lógica principal. Passado como argumento do parâmetro ...args de additionWithRetryHandler() |
No campo Function Name, adicione
additionWithRetryHandler
.No campo Function, adicione o seguinte código:
additionWithRetryHandler.js// randomly generates 1, 2, or 3 function getRandomOneTwoThree() { return Math.floor(Math.random() * 3) + 1; } function additionOrFailure(num1, num2) { // Throw error if getRandomOneTwoThree returns 1 const rand = getRandomOneTwoThree(); if (rand === 1) throw new Error("Uh oh!!"); const sum = num1 + num2; console.log(`Successful addition of ${num1} + ${num2}. Result: ${sum}`); // Otherwise return the sum return sum; } async function additionWithRetryHandler( inputVar1, inputVar2, // create a new `operation_id` if one not provided operationId = new BSON.ObjectId(), // count number of attempts retries = 0 ) { const res = await context.functions.execute( "handleRetry", additionOrFailure, "additionWithRetryHandler", // MUST BE NAME OF FUNCTION operationId, retries, inputVar1, inputVar2 ); return res; } exports = additionWithRetryHandler; Clique em Save.
Adicione os metadados de Função a
functions/config.json
:functions/config.json[ // ...other configuration { "name": "additionWithRetryHandler", "private": false } ] Adicione o seguinte código ao arquivo
functions/additionWithRetryHandler.js
:functions/additionWithRetryHandler.js// randomly generates 1, 2, or 3 function getRandomOneTwoThree() { return Math.floor(Math.random() * 3) + 1; } function additionOrFailure(num1, num2) { // Throw error if getRandomOneTwoThree returns 1 const rand = getRandomOneTwoThree(); if (rand === 1) throw new Error("Uh oh!!"); const sum = num1 + num2; console.log(`Successful addition of ${num1} + ${num2}. Result: ${sum}`); // Otherwise return the sum return sum; } async function additionWithRetryHandler( inputVar1, inputVar2, // create a new `operation_id` if one not provided operationId = new BSON.ObjectId(), // count number of attempts retries = 0 ) { const res = await context.functions.execute( "handleRetry", additionOrFailure, "additionWithRetryHandler", // MUST BE NAME OF FUNCTION operationId, retries, inputVar1, inputVar2 ); return res; } exports = additionWithRetryHandler; Distribua suas alterações:
Execute o seguinte comando para implementar suas alterações:
appservices push
Agora, quando você invocar additionWithRetryHandler
, a função tentará novamente se falhar.