欢迎光临
我们一直在努力

记录客户 nodejs 探针异常问题

起步

记录是因为异常的原因是 node 自身的问题。

问题说明

客户报错,报错日志:

2025-03-08 19:40:27.770: RangeError: Value undefined out of range for undefined options property undefined
    at Map.set (<anonymous>)
    at AsyncHook.init (/opt/bonree/apm/agent/nodejs/7.1.0/Bonree/lib/models/async_trace.js:18:18)
    at PromiseWrap.emitInitNative (internal/async_hooks.js:127:43)
    at Promise.then (<anonymous>)
    at next (/data/node_modules/co/index.js:100:51)
    at onFulfilled (/data/node_modules/co/index.js:69:7)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

客户node版本:12.16.2

排查

根据报错信息看到探针代码:

79697-bkw4hlb974i.png

一开始以为变量 self.stack 是个 undefined ,但它其实在初始化的时候已经是 map 对象了。

后来发现是 map 体积太大引起的。

https://github.com/nodejs/node/issues/37320

本地复现map体积过大:

23627-ysxtqtx6j2p.png

node 限制了 map 体积不能超过 2^24

分析

node探针代码通过 async_hooks 模块实现了对异步函数的 hook,其 api 为:异步钩子 | Node.js v12.22.12 文档 — Async hooks | Node.js v12.22.12 Documentation

93637-o9fhl4scvve.png

探针代码有在异步实例销毁时,把元素从 map 中移除。但不知客户应用里的异步实例一直没销毁,我们探针就没收到 destroy 事件。

后来发现,是 node 版本的问题,客户是 12.16.2 会有这个问题,在 12.22.12 则修复了这个问题。

github issue: Memory leak on Promise.all with async/await · Issue #34328 · nodejs/node

测试脚本:

const { createHook } = require("async_hooks");

const m = new Map();
createHook({
    init(id) {
        m.set(id, "val");
    },
    destroy(id) {
        m.delete(id);
    }
}).enable();

(async function () {
    const a = new Array(10).fill(0)
    for (let i = 0; i < 1e8; i++) {
        await Promise.all(a.map(() => Promise.resolve()))
        if (i % 1e5 === 0) {
            console.log(Math.round(process.memoryUsage().heapUsed / 1024 / 1024), 'MB')
        }
    }
})()

运行结果

44542-bxfyvnwu1ic.png

https://segmentfault.com/a/1190000047758089

未经允许不得转载:IT极限技术分享汇 » 记录客户 nodejs 探针异常问题

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址