对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
Docs 菜单

处理函数中的错误

本页介绍了如何处理 Atlas Function 中的错误。

注意

使用 AWS EventBridge 进行数据库触发器的自定义错误处理

您可以使用 AWS EventBridge 创建专门为数据库触发器创建的自定义错误处理程序。 有关详细信息,请参阅自定义错误处理。

您可以使用标准JavaScript错误处理技术(例如 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;

您可以在 Logs 页面查看Atlas trigger调用的所有函数执行记录。 这包括由于错误而未能成功执行的函数。

有关更多信息,请参阅trigger日志。

Atlas Functions 没有内置的重试行为。

您可以添加自定义重试行为。例如,如果函数调用的第三方服务具有间歇性连接,并且您希望即使第三方服务器暂时关闭,函数也能重新执行,则您可能希望添加重试行为。

以下部分描述了向函数添加重试行为的可用策略:

您可以递归调用“函数”来处理可能失败的操作。

总体而言,此进程包括以下组成部分:

  • 在 try 语句中执行要重试的操作,并在 catch 语句中让函数调用本身。

  • 要防止无限期执行,请设置最大重试次数。每次函数失败并进入 catch 语句时,增加当前重试次数的计数。在函数的当前重试次数达到最大重试次数时,停止递归执行。

  • 您可能还希望限制重试次数以减少某个时间范围内的执行总数。

下表描述了使用递归调用策略处理函数重试的一些优点和缺点。

优点
缺点
  • 所有重试逻辑都发生在一个函数内。

  • 函数可以在重试后返回一个值。

  • 最少的额外代码。

  • 所有重试必须在单个函数的最大执行时间内进行。

以下代码示例演示了通过在错误处理块中使用递归来重试函数的实施:

递归调用策略示例
// 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;

您还可以使用数据库触发器执行重试,并使用 MongoDB 集合跟踪以前失败的执行以重试函数。

总体而言,此进程包括以下组成部分:

  • 主函数,用于执行要重试的逻辑,包装在处理程序函数中。

  • 失败执行跟踪器 MongoDB 集合;用于跟踪主函数的失败执行。

  • 处理程序函数;用于调用主函数,并在函数失败时将其记录到失败执行跟踪器集合中。

  • 数据库触发器函数;每当处理程序函数将错误添加到失败执行跟踪器集合时,就会重新运行处理程序函数。

您可以使用一组处理程序函数、执行跟踪器集合和数据库触发器函数来支持多个主函数。

优点
缺点
  • 每次重试都是其自己的函数执行,具有自己的最大执行时间和资源。
  • 如果重试函数,则无法返回值。

  • 每个函数调用都需要调用函数两次,一次用于函数本身,一次用于重试处理程序。

  • 更复杂的逻辑,可能更难编写、调试和监控。

您可以在Atlas用户界面中或使用App Services CLI为函数创建重试机制。 以下过程将引导您创建处理程序、重试数据库trigger函数和主函数。

1

首先,创建调用主函数的处理程序函数 handleRetry。

handleRetry 接受以下参数:

Parameter
类型
说明

functionToRetry

JavaScript 函数

要重试的函数。

functionName

字符串

您要重试的函数名称。

operationId

ObjectId

主函数执行的唯一标识符,包括重试。

previousRetries

数值

以前尝试执行主函数的次数。

...args

其余参数

传递给主函数的不确定数量的参数。

handleRetry 执行以下操作:

  1. 尝试在 try 语句中执行 functionToRetry。如果执行成功,则 handleRetry 将返回由以下内容返回的值 functionToRetry。

  2. 如果在上一步中执行 functionToRetry 而引发错误,则catch 语句按如下方式处理该错误:

    1. 检查以前的重试次数是否等于允许的最大重试次数。如果两个数字相同,则函数会发生错误,因为已达到最大重试次数。该函数不再尝试重试。

    2. 创建一个函数执行日志条目对象以插入到数据库中。

    3. 获取对失败执行跟踪器集合的引用。

    4. 将函数日志执行日志条目插入失败执行跟踪器集合。 此插入操作会导致您将在下一步中创建的数据库trigger函数触发。

主函数是作为 functionToRetry 参数传递的。handleRetry 尝试执行主函数。如果执行失败,该函数尝试重试主函数。

  1. 导航至 Functions。单击按钮 Create New Function 。

  2. 在字段 Name 中,添加 handleRetry。

  3. 在 Function Editor 中添加以下代码,然后保存该函数:

    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;
2
  1. 在 Atlas 中,前往 Triggers 页面。

    1. 如果尚未显示,请从导航栏上的 Organizations 菜单中选择包含项目的组织。

    2. 如果尚未显示,请从导航栏的 Projects 菜单中选择您的项目。

    3. 在侧边栏中,单击 Streaming Data 标题下的 Triggers。

    会显示触发器页面。

  2. 单击 Add a Trigger(连接)。

  3. 使用以下配置创建触发器:

    字段
    值

    名称

    您选择的名称(例如:retryOperation)

    已启用

    是

    Skip Events on Re-Enable

    是

    事件排序

    是

    集群名称

    您选择的名称(例如:mongodb-atlas)

    Database Name

    您选择的名称(例如:logs)

    集合名称

    您选择的名称(例如:failed_execution_logs)

    操作类型

    Insert

    完整文档

    是

    文档原像

    No

    Select an Event Type(选择事件类型)

    function

    function

    单击 + New Function。有关函数内容,请参阅以下信息。

    高级配置

    不适用 — 无需高级配置。

  4. 现在将代码添加到trigger调用的函数中。

    函数retryOperation将重试处理程序发布到失败执行跟踪器集合的文档作为参数logEntry 。 然后, retryOperation使用context.functions.execute()使用来自logEntry的信息调用主函数。

    1. 在字段 Function Name 中,添加 retryOperationDbTrigger。

    2. 对于字段Function,添加以下代码,然后保存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;
3

现在您有了函数处理程序和重试数据库触发器函数,您可以编写主函数。

在以下示例中,函数在执行加法时随机出现错误。执行此逻辑的 JavaScript 函数如下:

  • getRandomOneTwoThree():用于为示例生成错误的辅助函数。

  • additionOrFailure():使用主逻辑的函数。

调用重试处理程序包装的 additionOrFailure() 是在导出的函数 additionWithRetryHandler() 中进行的。所有使用重试处理函数的函数应类似于该函数。

必须包含正确的参数,才能使此函数与其余重试逻辑一起工作。这些参数包括:

Parameter
类型
说明

...args

其余参数

使用主逻辑传递给函数的零个或多个参数。在本例中,additionOrFailure() 对 num1 和 num2 这两个数字进行相加。

operationId

函数调用和重试的唯一标识符。将默认值设置为 new BSON.ObjectId()。

retries

数值

将默认值设置为 0。

additionWithRetryHandler的主体是context.functions.execute() handleRetry ,而该重试处理程序又会调用additionOrFailure 。 您传递给context.functions.execute()的参数如下:

Argument
类型
说明

"handleRetry"

字符串

您定义的函数名称,用于调用主函数,并在主函数未正确执行时发布到重试日志。

additionOrFailure

JavaScript 函数

handleRetry() 调用的主函数。

operationId

BSON.ObjectId

作为参数从 additionWithRetryHandler() 的 operationId 参数中传入。

retries

数值

作为参数从 additionWithRetryHandler() 的 retries 参数中传入。

...args

展开参数

使用主逻辑传递给函数的零个或更多参数。作为参数从 additionWithRetryHandler() 的 ...args 参数中传入

  1. 在字段 Function Name 中,添加 additionWithRetryHandler。

  2. 对于字段Function ,添加以下代码:

    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. 单击 Save(连接)。

现在,在您调用 additionWithRetryHandler 时,如果失败,函数将重试。