AWS Lambda中JSON.stringify循环引用错误排查与修复
问题:AWS Lambda中JSON.stringify循环结构错误排查与修复
在AWS Lambda函数运行代码时,出现TypeError: Converting circular structure to JSON错误,错误指向MessageBody: JSON.stringify(messageBody)行,需排查循环结构位置并修改SQS消息结构解决问题。
运行代码
const addRanksToDB = async (tableName, stats) => { return Promise.all(Object.keys(stats).map(async (slug) => { const projectSlug = slug; const rankData = stats[slug]; const messageBody = { rowId: projectSlug, table: tableName, identifier: 'project_slug', action: 'insert', payload: { project_slug: projectSlug, ...rankData, last_updated: knexClient.fn.now(), }, }; return sqs.sendMessage({ MessageBody: JSON.stringify(messageBody), QueueUrl: 'https://sqs.eu-central-1.amazonaws.com/599632852002/handleDataInDBQueue', }).promise(); })); };
rankData结构
{ one_day_volume: 191, seven_day_volume: 162, thirty_day_volume: 179, total_volume: 156, num_owners: 4, average_price: 129, market_cap: 66, floor_price: 146 }
错误信息
{ "errorType": "TypeError", "errorMessage": "Converting circular structure to JSON\n --> starting at object with constructor 'Timeout'\n | property '_idlePrev' -> object with constructor 'TimersList'\n --- property '_idleNext' closes the circle", "trace": [ "TypeError: Converting circular structure to JSON", " --> starting at object with constructor 'Timeout'", " | property '_idlePrev' -> object with constructor 'TimersList'", " --- property '_idleNext' closes the circle", " at JSON.stringify (<anonymous>)", " at /var/task/src/handlers/stats/webpack:/handlers/stats/rankProjectsByStatsCronjob.js:84:27", " at Array.map (<anonymous>)", " at v (/var/task/src/handlers/stats/webpack:/handlers/stats/rankProjectsByStatsCronjob.js:67:43)", " at Runtime.R [as handler] (/var/task/src/handlers/stats/webpack:/handlers/stats/rankProjectsByStatsCronjob.js:188:23)", " at processTicksAndRejections (internal/process/task_queues.js:95:5)" ] }
问题根源与修复方案
问题根源
错误提示中的循环引用来自knexClient.fn.now()——这个方法返回的不是原始日期字符串或时间戳,而是Knex内部的查询构建对象,包含Timeout、TimersList这类带有循环引用的属性,无法被JSON.stringify序列化。
修复方案
有两种可行的修改方式:
方式1:传递客户端侧的当前时间
将knexClient.fn.now()替换为可序列化的日期值,比如ISO格式字符串:
const messageBody = { rowId: projectSlug, table: tableName, identifier: 'project_slug', action: 'insert', payload: { project_slug: projectSlug, ...rankData, last_updated: new Date().toISOString(), // 替换为客户端当前时间 }, };
方式2:在消费端生成数据库时间
如果需要使用数据库服务器的当前时间,不要在消息体中传递Knex函数对象,而是传递一个标记,在SQS消息的消费处理函数中再替换为knexClient.fn.now():
- 发送消息时修改payload:
payload: { project_slug: projectSlug, ...rankData, last_updated: 'USE_DB_NOW', // 用标记代替函数对象 },
- 在消费SQS消息的数据库处理代码中:
// 假设消费端处理逻辑 const handleDBInsert = async (payload) => { const insertData = { ...payload, last_updated: payload.last_updated === 'USE_DB_NOW' ? knexClient.fn.now() : payload.last_updated, }; // 执行数据库插入操作 };
内容的提问来源于stack exchange,提问作者Ic3m4n
相关产品推荐
相关产品推荐

