【问题标题】:NodeJS Memory Issues in Array.map()Array.map() 中的 NodeJS 内存问题
【发布时间】:2021-08-24 13:39:47
【问题描述】:

概述

我在 AWS lambda 上使用 NodeJs 中的异步映射函数时遇到了一些奇怪的行为,我很想知道为什么会发生这种情况。

我在下面的代码块中重新创建了一个玩具示例,其中给出了错误的代码。

topLevel() 函数有一个对象数组。该函数在数组上调用 Array.map(),并将异步辅助函数传递给 map 函数。辅助函数对对象执行一些转换,其中一种转换是加密银行帐户嵌套对象。这是通过另一个异步辅助函数 (encryptBank) 完成的。

虫子

在topLevel函数的返回值中,id="abc"用户的银行账户有来自id="def"用户的加密银行账户对象。 id="def" 的用户拥有正确的加密银行账户对象。

什么可能导致这种情况发生?从异步映射函数调用异步函数有问题吗?在 Array.map() 中使用异步函数是个坏主意吗?任何提示都将不胜感激,因为这个错误一直让我发疯。

环境信息

使用 NodeJS(版本 12)运行时在 AWS Lambda 上运行。

代码

// This is the lambda handler
module.exports.topLevel = async () {
  const users = [
      {
        id: 'abc',
        name: 'George P. Burdell',
        job: 'Jack of all trades',
        married: true,
        bankInformation: {
            institution: 'Bank of America',
            routingNumber: 987654321,
            accountNumber: 665471235774
        }
      },
      {
        id: 'def',
        name: 'Jay-Z',
        job: 'Artist',
        married: true,
        bankInformation: {
            institution: 'Chase',
            routingNumber: 123456789,
            accountNumber: 97822651348 
        }
      }
  ]

  const transformedUsers = await Promise.all(users.map(helper))
  return transformedUsers

}

async function helper(o) {
    Object.Keys(o).forEach((k) => {
        if (k === 'bankInformation') {
            o[k] = await encryptBank(o[k])
            await s3.putObject({Body: JSON.stringify(o[k]),Bucket: bankBucketName, Key: `${o.id.toLowerCase()}.json`}).promise()
        } else if (k === 'married') {
            o[k] = !o[k]
        }
        else {
            o[k] = k.toUpperCase()
        }
    })
    return o
}

async function encryptBank(o) {
    Object.Keys(o).forEach((k) => {
        o[k] = await encrypt(o[k])
    })
    return o
}

【问题讨论】:

    标签: node.js asynchronous memory


    【解决方案1】:

    我无法真正遵循您的示例以及它与错误的关系,但是,这闻起来是错误生成的 AWS Lambda 函数(要排除这种情况,请在您的代码上运行本地测试)

    有时来自一个请求的状态可能会渗入 AWS Lambda 中另一个请求的状态。

    这样做的原因是 AWS Lambda 在启动后可能会重复使用 lambda。这意味着您在处理程序代码之外定义的任何变量等都将从一次调用持续到下一次调用。

    为了避免这个问题,不要在你的处理函数之外创建变量,并确保任何导入/需要的模块不保存状态。如果是这样,请将它们导入您的处理程序函数中,而不是在脚本的顶部。注意:这可能会增加 lambda 调用时间并因此增加成本。

    【讨论】:

    • 示例代码显示了不同的函数调用是如何在 users.map() 堆栈跟踪中发生的。 lambda 配置正确。在生产中,“用户”数组是来自 dynamoDb 表的流事件数组。当只有一个事件时,该功能按预期工作。当有多个事件时,有时它会按预期工作,有时我们会得到原始帖子中描述的错误。所需模块等已正确导入,并且此函数不引用处理程序或辅助函数范围之外的任何变量。
    • 如果上面编写的代码在本地测试中运行良好,则需要监控输入到 lambda 的发电机流。您可能会错误地更新上游的发电机,这会为您的 lambda 提供不正确的流数据。基本上,如果您的代码通过了单元测试,则它是您需要修复的上游问题
    • 我已经监控了发送到 lambda 的流。来自流的数据是正确的,我查看了 cloudwatch 日志并验证了来自流事件的新/旧图像。这个问题几乎可以肯定是围绕节点的内存管理。我远不是异步和地图交互方面的专家,我真的认为这是一个问题。
    • @AlexKetchum 测试测试测试。这就是我所能建议的。如果它是节点的问题,它应该会出现在本地单元测试中
    • @AlexKetchum 另一个可能有助于找到错误的建议。取消优化您的代码,即删除 map 函数并使用基本的 for 循环。我发现有时这可以帮助解决问题。如果你可以让它在没有地图的情况下工作,它至少在正确的方向上取得了一些进展 - “让它工作,让它正确,让它快速” - Kent Beck
    猜你喜欢
    • 2023-04-03
    • 2013-05-03
    • 2014-08-23
    • 1970-01-01
    • 2022-08-16
    • 2023-03-18
    • 1970-01-01
    • 2012-07-06
    • 2020-07-18
    相关资源
    最近更新 更多