Menu Docs
Página inicial do Docs
/
MongoDB Atlas
/ / /

Tratamento de erros em funções

Nesta página

  • 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 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;

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.

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:

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çã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 uma nova tentativa de uma função usando recursão em blocos de tratamento de erros:

Exemplo de estratégia de chamada recursiva
// 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 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
  • 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.

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.

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

  1. Navegue até Functions. Clique no botão Create New Function.

  2. No campo Name, adicione handleRetry.

  3. 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;
  1. Adicione o seguinte a functions/config.json:

    functions/config.json
    [
    {
    "name": "handleRetry",
    "private": true,
    "run_as_system": true
    }
    // ...other configuration
    ]
  2. 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;
  3. Distribua suas alterações:

    Execute o seguinte comando para implementar suas alterações:

    appservices push
2
  1. Navegue até a página Triggers :

    1. Se ainda não tiver sido exibido, selecione a organização que contém seu projeto no menu Organizations na barra de navegação.

    2. Se ainda não estiver exibido, selecione seu projeto no menu Projects na barra de navegação.

    3. Na barra lateral, clique em Triggers sob o título Services.

      A página Acionadores é exibida.

  2. Clique em 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
    N/A - Nenhuma configuração avançada necessária.
  4. Agora adicione o código à 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 collection do rastreador de execução que falhou. Em seguida, o retryOperation utiliza context.functions.execute() para invocar a função principal com informações de logEntry.

    1. No campo Function Name, adicione retryOperationDbTrigger.

    2. 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;
  1. 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>"
  2. 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.

  3. 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"
    }
    }
    }
    }
  4. Agora adicione o código à função functions/config.json que o trigger invoca.

    A função retryOperation usa como parâmetro logEntry o documento que o manipulador de novas tentativas publicou na collection do rastreador de execução que falhou. Em seguida, o retryOperation utiliza context.functions.execute() para invocar a função principal com informações de logEntry.

    functions/config.json
    [
    // ...other configuration
    {
    "name": "retryOperationDbTrigger",
    "private": true
    }
    ]
  5. 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;
  6. Distribua suas alterações:

    Execute o seguinte comando para implementar suas alterações:

    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 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()
  1. No campo Function Name, adicione additionWithRetryHandler.

  2. 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;
  3. Clique em Save.

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

    functions/config.json
    [
    // ...other configuration
    {
    "name": "additionWithRetryHandler",
    "private": false
    }
    ]
  2. 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;
  3. 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.

Voltar

Dependências externas