Esta página explica cómo trabajar con errores en las funciones de Atlas.
Nota
Manejo de errores personalizado para activadores de bases de datos mediante 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 Manejo de errores personalizado.
Puede manejar errores de función utilizando técnicas de manejo de errores de JavaScript estándar como intente...capturar declaraciones.
function willThrowAndHandleError() { try { throw new Error("This will always happen"); } catch (err) { console.error("An error occurred. Error message:" + err.message); } } exports = willThrowAndHandleError;
Ver registros
Puede ver los registros de todas las ejecuciones de funciones invocadas por un disparador Atlas en el Logs página. Esto incluye funciones que no se pudieron ejecutar correctamente debido a un error.
Para obtener más información, consulte Registros de activación.
Funciones de reintento
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:
Llamar recursivamente a funciones en bloques de manejo de errores
Utilice activadores de base de datos para reintentar funciones
Llamar recursivamente a una función en bloques de manejo de errores
Puedes manejar operaciones que podrían fallar llamando a una función de forma recursiva.
A alto nivel, este proceso incluye los siguientes componentes:
Ejecute las operaciones que desee volver a intentar en un
trydeclaración y hacer que la función se llame a sí misma en una declaracióncatch.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 es posible que desees limitar los reintentos para reducir la cantidad total de ejecuciones en un período de tiempo.
La siguiente tabla describe algunas ventajas y desventajas de manejar reintentos de función con la estrategia de llamada recursiva.
Ventajas | Desventajas |
|---|---|
|
|
El siguiente ejemplo de código demuestra una implementación de reintento de una función mediante el uso de recursión en bloques de manejo de errores:
// 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;
Utilice activadores de base de datos para reintentar
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 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.
Rastreador de ejecuciones fallidas Colección MongoDB que rastrea ejecuciones fallidas de la función principal.
Función del controlador que invoca la función principal y registra cuando la función falla en la colección de seguimiento de ejecución fallida.
Función de activación de base de datos que vuelve a ejecutar la función del controlador cada vez que esta agrega un error a la colección de rastreadores de ejecución fallida.
Puede admitir varias funciones principales con un conjunto de funciones de controlador, una colección de seguimiento de ejecución y una función de activación de base de datos.
Ventajas | Desventajas |
|---|---|
|
|
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.
Crear la función del controlador
Primero, cree la función del controlador handleRetry que invoca la función principal.
handleRetry acepta los siguientes parámetros:
Parameter | Tipo | Descripción |
|---|---|---|
| Función de JavaScript | Función para reintentar. |
| String | Nombre de la función que desea volver a intentar. |
| ObjectId | Identificador único para la ejecución de la función principal, incluidos los reintentos. |
| Número | ¿Cuántas veces se ha probado previamente la función principal? |
| Parámetros de descanso | Número indefinido de argumentos pasados a la función principal. |
handleRetry realiza las siguientes operaciones:
Intenta ejecutar
functionToRetryen una instruccióntry. Si la ejecución es exitosa,handleRetrydevuelve el valor devuelto porfunctionToRetry.Si la ejecución de
functionToRetryen el paso anterior arroja un error, la declaracióncatchmaneja el error de la siguiente manera:Comprueba si el número de reintentos previos es igual al número máximo permitido. Si ambos números son iguales, la función genera un error porque se ha alcanzado el número máximo de reintentos. La función ya no intenta reintentar.
Construya un objeto de entrada de registro de ejecución de función para insertarlo en la base de datos.
Obtenga una referencia a la colección de rastreadores de ejecución fallida.
Inserte la entrada del registro de ejecución de la función en la colección del rastreador de ejecuciones fallidas. Esta inserción activa la función de desencadenador de base de datos, que creará 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.
Vaya a Functions. Haga clic en el botón Create New Function.
En el campo Name, agregue
handleRetry.En el Function Editor agregue el siguiente código y luego guarde 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;
Añade lo siguiente a
functions/config.json:functions/config.json[ { "name": "handleRetry", "private": true, "run_as_system": true } // ...other configuration ] 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; Implemente sus cambios:
Ejecute el siguiente comando para implementar sus cambios:
appservices push
Crear el disparador de base de datos de reintento
En Atlas, diríjase a la página Triggers.
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.
Si aún no se muestra, seleccione su proyecto en el menú Projects de la barra de navegación.
En la barra lateral, haz clic en Triggers en la sección Streaming Data.
Haga clic en Add a Trigger.
Cree el disparador con la siguiente configuración:
CampoValorNombre
Nombre de su elección (ej:
retryOperation)Activado
Sí
Omitir eventos al volver a habilitar
Sí
Ordenamiento de eventos
Sí
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
Sí
Preimagen del documento
No
Seleccione un tipo de evento
Función
Función
Haga clic en + New Function. Consulte la siguiente información sobre el contenido de la función.
Configuración avanzada
N/A - No es necesaria ninguna configuración avanzada.
Ahora agregue el código a la función que invoca el disparador.
La función
retryOperationtoma como parámetrologEntryel documento que el controlador de reintentos envió a la colección de seguimiento de ejecuciones fallidas. Luego,retryOperationusa context.functions.execute() para invocar la función principal con la informaciónlogEntryde.En el campo Function Name, agregue
retryOperationDbTrigger.Para el campo Function, agregue el siguiente código y luego guarde el disparador:
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;
Autenticar un usuario de 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>" Extraiga los últimos archivos de configuración de su 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.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" } } } } Ahora agrega el código a la función
functions/config.jsonque Invoca el activador.La función
retryOperationtoma como parámetrologEntryel documento que el controlador de reintentos envió a la colección de seguimiento de ejecuciones fallidas. Luego,retryOperationusa context.functions.execute() para invocar la función principal con la informaciónlogEntryde.functions/config.json[ // ...other configuration { "name": "retryOperationDbTrigger", "private": true } ] Agregue el siguiente código al archivo
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; Implemente sus cambios:
Ejecute el siguiente comando para implementar sus cambios:
appservices push
Crear la función principal
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 genera un error aleatorio al realizar una suma. Las funciones de JavaScript que ejecutan esta lógica son las siguientes:
getRandomOneTwoThree():Función auxiliar para generar errores para el ejemplo.additionOrFailure():Función con la lógica principal.
La invocación de,additionOrFailure() encapsulada por el controlador de additionWithRetryHandler() reintentos, se produce enla función exportada. Todas las funciones que utilizan el controlador de reintentos deben ser similares a esta función.
Debe 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 |
|---|---|---|
| Parámetros de descanso | Se pasan cero o más parámetros a la función con lógica principal. En este ejemplo, se suman los dos números |
| Identificador único para la llamada de función y los reintentos. Establezca el valor predeterminado en | |
| Número | Establezca el valor predeterminado en 0. |
El cuerpo de additionWithRetryHandler es el controlador de reintentos handleRetry, invocado por context.functions.execute(), que a su vez invoca a additionOrFailure. Los argumentos que se pasan a context.functions.execute() son los siguientes:
Argument | Tipo | Descripción |
|---|---|---|
| String | Nombre de la función que definió para invocar la función principal y publicar en los registros de reintentos si la función principal no se ejecuta correctamente. |
| Función de JavaScript | La función principal que invoca |
| BSON.ObjectId | Se pasa como argumento del parámetro |
| Número | Se pasa como argumento del parámetro |
| Difundir argumentos | Cero o más argumentos para pasar a la función con lógica principal. Pasados como argumento del parámetro |
En el campo Function Name, agregue
additionWithRetryHandler.Para el campo Function, agregue el siguiente código:
adiciónWithRetryHandler.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; Haga clic en Save.
Agregue los metadatos de la función a
functions/config.json:functions/config.json[ // ...other configuration { "name": "additionWithRetryHandler", "private": false } ] Agregue 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; Implemente sus cambios:
Ejecute el siguiente comando para implementar sus cambios:
appservices push
Ahora, cuando invocas additionWithRetryHandler, la función volverá a intentarlo si falla.