Menu Docs
Página inicial do Docs
/ /
Serviços Atlas App
/

Tratamento de erros em funções

Nesta página

  • Tratamento básico de erros
  • Exibir registros
  • Repetir funções
  • Função de chamada recursiva em blocos de tratamento de erros
  • Use gatilhos de banco de dados (trigger) para tentar novamente

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.

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;

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.

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:

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ção catch.

  • 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
  • Toda lógica de repetição ocorre dentro de uma função.

  • A função pode retornar um valor após uma nova tentativa.

  • Código adicional mínimo.

  • Todas as tentativas devem ocorrer dentro do tempo de execução máximo de uma única função.

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;

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
  • Cada nova tentativa é sua própria execução da função, com tempo máximo de execução e recursos próprios.

  • Se a Função for repetida, ela não poderá retornar um valor.

  • Cada chamada de função requer duas invocações de função, uma para a função em si e outra para o manipulador de novas tentativas.

  • Lógica mais complexa, que pode ser mais difícil de escrever, depurar e monitorar.

1

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:

  1. Tenta executar functionToRetry em uma declaração try. Se a execução for bem-sucedida, handleRetry retorna o valor retornado por functionToRetry.

  2. Se a execução de functionToRetry na etapa anterior gerar um erro, a declaração catch gerencia o erro da seguinte maneira:

    1. 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.

    2. Crie um objeto de entrada de registro de execução de função para inserir no banco de dados.

    3. Obtenha uma referência à collection de rastreadores de execução com falha.

    4. 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:

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;

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:

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;

Envie suas alterações para o App Services:

appservices push
2
  1. Navegue até Triggers na IU do seu aplicativo.

  2. Clique no botão Add a Trigger.

  3. Crie o Trigger com a seguinte configuração:

    Campo
    Valor
    Nome
    Nome da sua escolha (por exemplo: retryOperation)
    Habilitado
    Sim
    Ignorar eventos ao reabilitar
    Sim
    Classificação de eventos
    Sim
    Nome do cluster
    Nome da sua escolha (por exemplo: mongodb-atlas)
    Nome do banco de dados
    Nome da sua escolha (por exemplo: logs)
    Nome da collection
    Nome da sua escolha (por exemplo: failed_execution_logs)
    Tipo de operação
    Insert
    Documento completo
    Sim
    Pré-imagem do documento
    No
    Selecione um tipo de evento
    Função
    Função
    Clique em + New Function. Consulte as seguintes informações sobre o conteúdo da função.
    Configuração avançada
    Nenhuma 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.

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 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:

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;

Adicione os metadados de Função a functions/config.json:

functions/config.json
[
// ...other configuration
{
"name": "retryOperationDbTrigger",
"private": true
}
]

Adicione o seguinte código ao arquivo functions/retryOperationDbTrigger.js:

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
3

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:

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;

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;

Envie suas alterações para o App Services:

appservices push

Agora, quando você invocar additionWithRetryHandler, a função tentará novamente se falhar.

Voltar

Funções de teste