Join us at MongoDB.local London on 7 May to unlock new possibilities for your data. Use WEB50 to save 50%.
Register now >
Docs Menu
Docs Home
/ /

Gestionar errores en las funciones

Esta página explica cómo trabajar con errores en Atlas Functions.

Nota

Gestión de errores personalizados para activadores de base de datos utilizando AWS EventBridge

Puede crear un controlador de errores personalizado específicamente para los activadores de base de datos mediante AWS EventBridge. Para obtener más información, consulte Custom Error Handling.

Puede gestionar los errores de funciones usando técnicas estándar de gestión de errores de JavaScript como instrucciones 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;

Puedes ver los registros de todas las ejecuciones de funciones invocadas por un Atlas activador en el Logs página. Esto incluye funciones que no se pudieron ejecutar correctamente debido a un error.

Para más información, consulta los Registros de activadores.

Las funciones Atlas no tienen un comportamiento de reintento incorporado.

Puedes añadir un comportamiento de reintento personalizado. Por ejemplo, podrías añadir un comportamiento de reintento si el servicio de terceros al que llama tu función tiene conectividad intermitente y quieres que la función se vuelva a ejecutar incluso si el servicio de terceros está temporalmente inactivo.

Las siguientes secciones describen las estrategias disponibles para agregar comportamiento de reintento a sus Funciones:

Puedes gestionar operaciones que podrían fallar al llamar a una función recursivamente.

A un alto nivel, este proceso incluye los siguientes componentes:

  • Ejecute las operaciones que desee volver a intentar en un try declaración y hacer que la función se llame a sí misma en una declaración catch.

  • Para evitar la ejecución indefinida, establezca un número máximo de reintentos. Cada vez que la función falle y acceda a la instrucción catch, incremente el número actual de reintentos. Detenga la ejecución recursiva cuando el número actual de reintentos de la función alcance el número máximo.

  • También puedes limitar los reintentos para reducir el número total de ejecuciones en un periodo de tiempo.

La siguiente tabla describe algunas ventajas y desventajas de gestionar los reintentos de funciones con la estrategia de llamada recursiva.

Ventajas
Desventajas
  • Toda la lógica de reintentos tiene lugar dentro de una sola función.

  • La función puede devolver un valor después de un reintento.

  • Código adicional mínimo.

  • Todos los reintentos deben ocurrir dentro del tiempo máximo de ejecución de una sola función.

El siguiente ejemplo de código demuestra una implementación de reintento de una Function utilizando la recursividad en bloques de manejo de errores:

Ejemplo de estrategia de llamada 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;

También puede reintentar funciones utilizando un disparador de base de datos para ejecutar reintentos y una colección MongoDB para rastrear ejecuciones fallidas anteriormente.

A un alto nivel, este proceso incluye los siguientes componentes:

  • Función principal que ejecuta la lógica que desea volver a intentar, envuelta en la función del controlador.

  • Colección de MongoDB de seguimiento de ejecución fallida que rastrea las ejecuciones fallidas de la función principal.

  • Función Handler/Controladora que invoca la función principal y registra cuando la función falla en la colección failed execution tracker.

  • Activador de base de datos que vuelve a ejecutar la función de gestor cada vez que ésta añade un error a la colección de seguimiento de ejecuciones fallidas.

Puede soportar múltiples funciones principales con un solo conjunto de una función manejadora, una colección de seguimiento de ejecución y un activador de base de datos.

Ventajas
Desventajas
  • Cada reintento es su propia ejecución de función, con su propio tiempo máximo de ejecución y recursos.

  • Si la función se vuelve a intentar, no puede devolver un valor.

  • Cada llamada de la función requiere dos invocaciones de función, una para la función en sí y otra para el gestor de reintentos.

  • Lógica más compleja, que puede ser más difícil de escribir, depurar y supervisar.

Se puede crear un mecanismo de reintento para Functions en la Interfaz de usuario de Atlas o utilizando la App Services CLI. El siguiente procedimiento le guía a través de la creación de un manejador, una Function de activador de base de datos de reintentos y una Function principal.

1

En primer lugar, cree la función ‚controller‘ handleRetry que invoque la función principal.

handleRetry acepta los siguientes parámetros:

Parameter
Tipo
Descripción

functionToRetry

Función de JavaScript

Función para reintentar.

functionName

String

Nombre de la función que deseas volver a intentar.

operationId

ObjectId

Identificador único para la ejecución de la función principal, incluidos los reintentos.

previousRetries

Número

Cuántas veces se ha intentado previamente la función principal.

...args

Parámetros rest

Número indefinido de argumentos pasados a la función principal.

handleRetry realiza las siguientes operaciones:

  1. Intenta ejecutar functionToRetry en una instrucción try. Si la ejecución es exitosa, handleRetry devuelve el valor devuelto por functionToRetry.

  2. Si la ejecución de functionToRetry en el paso anterior genera un error, la instrucción catch maneja el error de la siguiente manera:

    1. Verifica si el número de reintentos anteriores es igual al número máximo permitido de reintentos. Si los dos números son iguales, la función lanza un error porque se ha alcanzado el máximo de reintentos. La función ya no intenta reintentar.

    2. Cree un objeto de entrada de registro de ejecución de función para insertarlo en la base de datos.

    3. Obtén una referencia a la colección del rastreador de ejecuciones fallidas.

    4. Inserta el registro de ejecución de la función en la colección de seguimiento de ejecuciones fallidas. Esta operación de inserción hace que se active la función activador de base de datos, que crearás en el siguiente paso.

La función principal se pasa como argumento functionToRetry. handleRetry intenta ejecutar la función principal. Si la ejecución falla, esta función intenta reintentar la función principal.

  1. Navegue a Functions. Haga clic en el botón Create New Function.

  2. En el campo Name, agregue handleRetry.

  3. En el Function Editor agrega el siguiente código y luego guarda la función:

    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. Agrega lo siguiente a functions/config.json:

    functions/config.json
    [
    {
    "name": "handleRetry",
    "private": true,
    "run_as_system": true
    }
    // ...other configuration
    ]
  2. Crea el archivo para la función 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. Implementa tus cambios:

    Ejecute el siguiente comando para implementar sus cambios:

    appservices push
2
  1. En Atlas, diríjase a la página Triggers.

    1. Si aún no aparece, se debe seleccionar la organización que contiene el proyecto en el menú Organizations de la barra de navegación.

    2. Si aún no se muestra, seleccione su proyecto en el menú Projects de la barra de navegación.

    3. En la barra lateral, haz clic en Triggers en la sección Streaming Data.

    Se muestra la página Desencadenantes.

  2. Haga clic en Add a Trigger.

  3. Cree el disparador con la siguiente configuración:

    Campo
    Valor

    Nombre

    Nombre de su elección (ej: retryOperation)

    Activado

    Omitir eventos al volver a habilitar

    Ordenación de eventos

    Nombre del clúster

    Nombre de su elección (ej: mongodb-atlas)

    Nombre de la base de datos

    Nombre de su elección (ej: logs)

    Nombre de colección

    Nombre de su elección (ej: failed_execution_logs)

    Tipo de operación

    Insert

    Documento completo

    Preimagen del documento

    No

    Seleccione un tipo de evento

    Función

    Función

    Haz clic en + New Function. Consulta la siguiente información sobre el contenido de la función.

    Configuración avanzada

    N/A - No es necesaria una configuración avanzada.

  4. Ahora añade el código a la función que invoca el activador.

    La función retryOperation toma como parámetro logEntry el documento que el controlador de reintentos envió a la colección de seguimiento de ejecuciones fallidas. Luego, retryOperation usa context.functions.execute() para invocar la función principal con la información logEntry de.

    1. En el campo Function Name, agregue retryOperationDbTrigger.

    2. Para el campo Function, añade el siguiente código y luego guarda el activador:

      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. Autenticar a un usuario MongoDB Atlas:

    Utilice su clave API de administración de MongoDB Atlas para iniciar sesión en la CLI de App Services:

    appservices login --api-key="<API KEY>" --private-api-key="<PRIVATE KEY>"
  2. Solicita los archivos de configuración más recientes de tu aplicación:

    Ejecute el siguiente comando para obtener una copia local de sus archivos de configuración:

    appservices pull --remote=<App ID>

    De forma predeterminada, el comando extrae los archivos al directorio de trabajo actual. Puede especificar una ruta de directorio con el indicador opcional --local.

  3. Agrega la configuración para el activador de base de datos. Para obtener más información, consulta la referencia de configuración de activadores.

    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. Ahora agrega el código a la función functions/config.json que Invoca el activador.

    La función retryOperation toma como parámetro logEntry el documento que el controlador de reintentos envió a la colección de seguimiento de ejecuciones fallidas. Luego, retryOperation usa context.functions.execute() para invocar la función principal con la información logEntry de.

    functions/config.json
    [
    // ...other configuration
    {
    "name": "retryOperationDbTrigger",
    "private": true
    }
    ]
  5. Agrega el siguiente código al archivo 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. Implementa tus cambios:

    Ejecute el siguiente comando para implementar sus cambios:

    appservices push
3

Ahora que tiene el controlador de funciones y la función disparadora de base de datos de reintentos, puede escribir la función principal.

En el siguiente ejemplo, la función arroja aleatoriamente un error al realizar una suma. Las funciones de JavaScript que ejecutan esta lógica son las siguientes:

  • getRandomOneTwoThree(): Función asistente para generar errores en el ejemplo.

  • additionOrFailure():Función con la lógica principal.

La invocación de additionOrFailure() envuelta por el gestor de reintentos ocurre en la función exportada additionWithRetryHandler(). Todas las funciones que utilizan la función de manejo de reintentos deben parecerse a esta función.

Debes incluir los parámetros correctos para que esta función funcione con el resto de la lógica de reintento. Estos parámetros son:

Parameter
Tipo
Descripción

...args

Parámetros rest

Cero o más parámetros para pasar a la función con lógica principal. En el caso de este ejemplo, los dos números añadidos en additionOrFailure(), num1 y num2.

operationId

Identificador único para la llamada y reintentos de la función. Establecer valor por defecto por new BSON.ObjectId().

retries

Número

Establezca el valor predeterminado en 0.

El cuerpo de additionWithRetryHandler es el gestor de reintentos handleRetry invocado por context.functions.execute(), que a su vez invoca additionOrFailure. Los argumentos que se pasan a context.functions.execute() son los siguientes:

Argument
Tipo
Descripción

"handleRetry"

String

Nombre de la función definida para invocar la función principal y publicar en los registros de reintentos si la función principal no se ejecuta correctamente.

additionOrFailure

Función de JavaScript

La función principal que invoca handleRetry().

operationId

BSON.ObjectId

Se pasó como argumento desde el parámetro operationId de additionWithRetryHandler().

retries

Número

Se pasó como argumento desde el parámetro retries de additionWithRetryHandler().

...args

Extend arguments

Cero o más argumentos para pasar a la función con lógica principal. Pasados ​​como argumento del parámetro ...args de additionWithRetryHandler().

  1. En el campo Function Name, agregue additionWithRetryHandler.

  2. Para el campo Function, se debe agregar el siguiente 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. Haga clic en Save.

  1. Agregue los metadatos de la función a functions/config.json:

    functions/config.json
    [
    // ...other configuration
    {
    "name": "additionWithRetryHandler",
    "private": false
    }
    ]
  2. Agrega el siguiente código al archivo 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. Implementa tus cambios:

    Ejecute el siguiente comando para implementar sus cambios:

    appservices push

Ahora, cuando invoques additionWithRetryHandler, la función realizará reintentos si falla.

Volver

Dependencias externas

En esta página