Node.js中await调用导致procsseddocs出现重复订单ID问题排查
问题:MongoDB异步处理导致procsseddocs数组出现重复订单ID
在这段Node.js代码中,main函数从MongoDB的order集合获取数据,当businessSubType为FlightBooking时调用flightBookingTransform处理。initialDocs2中存入的是唯一的订单ID,但procsseddocs数组却出现了重复值,请问原因是什么?
const { MongoClient } = require('mongodb'); initialDocs2 = [] procsseddocs = [] txnarr = [] async function main() { const url = "<connectionstring>" const client = new MongoClient(url); let target_db = "<db>" totalDocCount = 0; cabCount = 0; dutyFreeCount = 0; flightBookingCount = 0; pranaamCount = 0; try { await client.connect(); target_db = client.db(target_db); initialdocs = [] await target_db.collection("order").find().forEach(async function (doc) { totalDocCount++; if (doc._id) { if (doc.businessSubType == "FlightBooking") { flightBookingCount++; try { initialdocs.push(String(doc._id)) flightBookingTransform(target_db, doc); } catch (e) { console.log("Errored in flight booking free transform:", doc._id, " error:", e) } } } } ) } catch (e) { console.error(e); } finally { await client.close(); } } async function flightBookingTransform(target_db, docOrder) { try { orderIdString = String(docOrder._id) initialDocs2.push(orderIdString) docTransactionarr = await target_db.collection("transaction").find({ "orderId.oid": orderIdString }).sort({ "_id": -1 }).limit(1).toArray() if (docTransactionarr && docTransactionarr.length > 0) docTransaction = docTransactionarr[0] else return txnarr.push(String(docTransaction._id)) procsseddocs.push(orderIdString) } catch (e) { console.log("Something went wrong", e) } }; main()
问题原因
1. 核心原因:全局变量被并发异步操作覆盖
flightBookingTransform函数中的orderIdString、docTransactionarr、docTransaction均未用let/const声明,默认成为全局变量。当多个flightBookingTransform因为forEach的异步回调并发执行时,这些全局变量会被后续调用覆盖:
- 第一个调用进入
await查询transaction的等待阶段时,第二个调用会修改orderIdString为新的订单ID - 第一个调用的
await完成后,使用的已经是被覆盖后的orderIdString,导致错误的订单ID被重复推入procsseddocs
2. 次要原因:forEach无法等待异步回调
MongoDB的find().forEach()不支持等待异步回调完成,await target_db.collection("order").find().forEach(...)并不会等待所有异步回调执行完毕。这会导致大量flightBookingTransform并发执行,既加剧了全局变量被覆盖的概率,还可能在所有处理完成前就执行client.close(),导致部分数据库查询失败。
修复方案
1. 使用块级作用域变量
给flightBookingTransform中的所有变量添加let/const声明,避免全局变量污染:
async function flightBookingTransform(target_db, docOrder) { try { const orderIdString = String(docOrder._id); initialDocs2.push(orderIdString); const docTransactionarr = await target_db.collection("transaction").find({ "orderId.oid": orderIdString }).sort({ "_id": -1 }).limit(1).toArray(); if (docTransactionarr && docTransactionarr.length > 0) { const docTransaction = docTransactionarr[0]; txnarr.push(String(docTransaction._id)); procsseddocs.push(orderIdString); } else { return; } } catch (e) { console.log("Something went wrong", e); } };
2. 替换forEach为可等待的遍历方式
改用for...of遍历查询结果,确保每个flightBookingTransform执行完成后再处理下一个文档:
// 替换main函数中的forEach代码块 const cursor = target_db.collection("order").find(); for await (const doc of cursor) { totalDocCount++; if (doc._id) { if (doc.businessSubType == "FlightBooking") { flightBookingCount++; try { initialdocs.push(String(doc._id)); await flightBookingTransform(target_db, doc); } catch (e) { console.log("Errored in flight booking free transform:", doc._id, " error:", e); } } } }
内容的提问来源于stack exchange,提问作者Shekhar
相关产品推荐
相关产品推荐

