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 disparadores de base de datos mediante AWS EventBridge. Para obtener más información, consulte "Control de errores personalizado".
Basic 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;
Ver registros
Puede ver registros de todas las ejecuciones de función, incluida aquella en la que un error impidió la ejecución exitosa en Registros del servicio de aplicaciones.
Depending on how a Function is invoked, it shows up differently in the logs. For example, logs for Functions called by Atlas Triggers show up in the logs as "Triggers" while logs for Functions called from a Realm client SDK show up in the logs as "Functions". For more information, refer to the Log entry type documentation.
Funciones de reintentos
Las funciones de Atlas no tienen un comportamiento de reintento integrado. Puede agregar un comportamiento de reintento personalizado. Por ejemplo, podría querer agregar un comportamiento de reintento si el servicio de terceros al que llama su función tiene conectividad intermitente y desea que la función se vuelva a ejecutar incluso si el servicio de terceros está temporalmente inactivo.
Esta sección describe las siguientes estrategias para agregar un comportamiento de reintento a sus funciones:
Llamar funciones recursivamente en bloques de manejo de errores
Usa activadores de base de datos para volver a intentar las funciones
Llamar recursivamente a una función en bloques de manejo de errores
Puedes gestionar operaciones que podrían fallar al llamar a una función recursivamente.
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 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 |
|---|---|
|
|
El siguiente ejemplo de código demuestra una implementación de reintento de una función utilizando recursividad 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;
Usar 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 (ver el punto a continuación).
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 |
|---|---|
|
|
Crear una función para gestionar el reintento de ejecución
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 |
|---|---|---|
| Función de JavaScript | Función para reintentar. |
| String | Nombre de la función que deseas 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 intentado previamente la función principal. |
| Parámetros rest | 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 genera un error, la instruccióncatchmaneja el error de la siguiente manera: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.
Cree un objeto de entrada de registro de ejecución de función para insertarlo en la base de datos.
Obtén una referencia a la colección del rastreador de ejecuciones fallidas.
Inserte la entrada del registro de ejecución de la función en la colección del rastreador de ejecuciones fallidas. Esta operación de 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.
Navegar a Functions. Haga clic en el botón Create New Function.
En el campo Name, agregue handleRetry.
En el Function Editor agrega el siguiente código y luego guarda la función:
// 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;
Tip
Instalar y configurar la CLI de App Services
Si está utilizando la CLI para actualizar su aplicación de App Services, primero debe instalar y configurar la CLI de App Services.
Agrega lo siguiente a functions/config.json:
[ { "name": "handleRetry", "private": true, "run_as_system": true } // ...other configuration ]
Crea el archivo para la función 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;
Push your changes to App Services:
appservices push
Create retry database trigger
Navegue hasta Triggers en la interfaz de usuario de su aplicación.
Haga clic en el botón 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í
Ordenación 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
Haz clic en + New Function. Consulta la siguiente información sobre el contenido de la función.
Configuración avanzada
No es necesaria ninguna configuración avanzada.
Agregue la configuración para el Activador de base de datos. Para obtener más información, consulte la referencia de configuración de activador.
{ "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" } } } }
Now add the code for the Function that the Trigger invokes.
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.
En el campo Function Name, agregue retryOperationDbTrigger.
Para el campo Function, agregue el siguiente código y luego guarde el disparador:
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;
Agregue los metadatos de la función a functions/config.json:
[ // ...other configuration { "name": "retryOperationDbTrigger", "private": true } ]
Agrega el siguiente código al archivo 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;
Push your changes to App Services:
appservices push
Escribir una función para reintentar
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() 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.
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 |
|---|---|---|
| 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 |
| Identificador único para la llamada y reintentos de la función. Establecer valor por defecto por | |
| 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 pasas a context.functions.execute() son los siguientes:
Argument | Tipo | Descripción |
|---|---|---|
| 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. |
| Función de JavaScript | La función principal que invoca |
| BSON.ObjectId | Se pasó como argumento desde el parámetro |
| Número | Se pasó como argumento desde el parámetro |
| Extend arguments | 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 y guarde la función:
// 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;
Agregue los metadatos de la función a functions/config.json:
[ // ...other configuration { "name": "additionWithRetryHandler", "private": false } ]
Agrega el siguiente código al archivo 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;
Push your changes to App Services:
appservices push
Ahora, cuando invoques additionWithRetryHandler, la función realizará reintentos si falla.