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 Database Tools usando o Amazon Web Services Eventbridge. Para obter mais informações, consulte Tratamento personalizado de erros.
Tratamento básico 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 exibir registros de todas as execuções de função, incluindo as que um erro impediu a execução bem-sucedida em Registros do App Services.
Dependendo de como uma função é invocada, ela aparece de forma diferente nos registros. Por exemplo, logs para Funções chamados por Atlas Triggers aparecem nos logs como "Triggers" enquanto logs para Funções chamados de um SDK do cliente Realm aparecem nos logs como "Funções". Para obter mais informações, consulte a documentação do tipo de entrada de log.
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.
Esta seção descreve as seguintes estratégias 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 nível elevado, 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 novas tentativas em 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 nível elevado, 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 (veja o marcador abaixo).
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 |
---|---|
|
|
Criar uma função para lidar com a nova tentativa de execução
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ções na collection do rastreador de execução que falhou. Esta 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:
// 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;
Dica
Instalar e configurar o App Services CLI
Se você estiver usando a CLI para atualizar seu App Services App, deverá primeiro instalar e configurar a App Services CLI.
Adicione o seguinte a functions/config.json
:
[ { "name": "handleRetry", "private": true, "run_as_system": true } // ...other configuration ]
Crie o arquivo para a Função 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;
Envie suas alterações para o App Services:
appservices push
Criar gatilho de banco de dados de nova tentativa
Navegue até Triggers na IU do seu aplicativo.
Clique no botão 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çadaNenhuma configuração avançada necessária.
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.
{ "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 da função que o trigger invoca.
A função retryOperation
usa como parâmetro logEntry
o documento que o manipulador de novas tentativas publicou na coleta do rastreador de execução que falhou. Em seguida, o retryOperation
utiliza context.functions.execute() para invocar a função principal com informações do logEntry
.
No campo Function Name, adicione retryOperationDbTrigger
.
Para o campo Function, adicione o seguinte código e salve o Trigger:
async 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;
Adicione os metadados de Função a functions/config.json
:
[ // ...other configuration { "name": "retryOperationDbTrigger", "private": true } ]
Adicione o seguinte código ao arquivo functions/retryOperationDbTrigger.js
:
async 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;
Envie suas alterações para o App Services:
appservices push
Grave a função para tentar novamente
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 novas tentativas 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 e salve a função:
// 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;
Adicione os metadados de Função a functions/config.json
:
[ // ...other configuration { "name": "additionWithRetryHandler", "private": false } ]
Adicione o seguinte código ao arquivo 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;
Envie suas alterações para o App Services:
appservices push
Agora, quando você invocar additionWithRetryHandler
, a função tentará novamente se falhar.