Docs Menu
Docs Home
/ /
Funciones

Manejar errores en funciones

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 disparadores de base de datos mediante AWS EventBridge. Para obtener más información, consulte "Control 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;

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.

Dependiendo de cómo se invoque una función, esta se mostrará de forma diferente en los registros. Por ejemplo, losregistros de las funciones llamadas por disparadores de Atlas se muestran como "Disparadores", mientras que los registros de las funciones llamadas desde un SDK de cliente de Realm se muestran como "Funciones". Para obtener más información, consulte la documentación sobre los tipos de entrada de registro.

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.

En esta sección se describen las siguientes estrategias para agregar comportamiento de reintento a sus funciones:

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 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 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
  • Toda la lógica de reintento ocurre dentro de una 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 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;

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

  • 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
  • Cada reintento es su propia ejecución de función, con su propio tiempo máximo de ejecución y recursos.

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

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

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

1

Primero, cree la función del controlador handleRetry que invoca 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 desea 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 probado previamente la función principal?

...args

Parámetros de descanso

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 arroja un error, la declaración catch maneja el error de la siguiente manera:

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

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

    3. Obtenga una referencia a la colección de rastreadores de ejecución fallida.

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

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.

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;

Envíe sus cambios a App Services:

appservices push
2
  1. Navegue hasta Triggers en la interfaz de usuario de su aplicación.

  2. Haga clic en el botón 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

    Ordenamiento 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

    Haga clic en + New Function. Consulte 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 del disparador de base de datos. Para obtener más información, consulte la referencia de configuración del disparador.

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 agregue el código para la función que invoca el disparador.

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:

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;

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

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

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

Envíe sus cambios a App Services:

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

...args

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 additionOrFailure(), num1 y num2.

operationId

Identificador único para la llamada de función y los reintentos. Establezca el valor predeterminado en new BSON.ObjectId().

retries

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

"handleRetry"

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.

additionOrFailure

Función de JavaScript

La función principal que invoca handleRetry().

operationId

BSON.ObjectId

Se pasa como argumento del parámetro operationId de additionWithRetryHandler().

retries

Número

Se pasa como argumento del parámetro retries de additionWithRetryHandler().

...args

Difundir argumentos

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

En el campo Function Name, agregue additionWithRetryHandler.

Para el campo Function, agregue el siguiente código y guarde la función:

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;

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;

Envíe sus cambios a App Services:

appservices push

Ahora, cuando invocas additionWithRetryHandler, la función volverá a intentarlo si falla.

Volver

Funciones de prueba

En esta página