async函数循环处理区块交易未等条件完成就执行save触发MongoDB重复键错误
跨区块交易事件处理重复写入问题解决
问题根因
你当前的代码存在两个核心逻辑错误导致跨区块事件处理异常:
- Web3.js的
getPastEvents方法混用了回调和async/await语法:当你给getPastEvents传入第三个回调参数时,该方法不会返回Promise,你写的await contract.getPastEvents(...)不会等待回调内的异步校验、写入逻辑执行完成,就会直接往下执行ticker.save()和currentBlock++进入下一个区块的处理。 - 数据库写入没有做幂等处理,同一个交易哈希在不同区块的事件处理流程中都尝试插入,触发MongoDB的唯一索引重复报错。
修复方案
1. 统一使用Promise写法处理事件查询
去掉getPastEvents的回调参数,直接用await获取事件结果后同步处理,确保当前区块的所有事件全部处理完成再进入后续流程:
// 原回调写法替换为以下代码 const events = await contract.getPastEvents( 'allEvents', { fromBlock: currentBlock, toBlock: currentBlock } ); if (events && events.length > 0) { function Raw(address, data, eventSignature, topics) { this.address = address; this.data = data; this.eventSignature = eventSignature; this.topics = topics; } // 其余你原来写在回调里的事件处理、交易匹配、写入逻辑全部放到这里 }
2. 新增数据库写入幂等逻辑
交易写入时优先用upsert操作,避免重复键报错:
// 替换原来的insert逻辑 await TransactionModel.updateOne( { transactionHash: targetHash }, // 唯一键筛选条件 { $set: { // 你要写入的交易字段 }, // 可以用$addToSet新增跨区块的事件列表,避免覆盖已有事件 $addToSet: { events: newEvent } }, { upsert: true } // 不存在则插入,存在则更新 );
3. 调整区块流转逻辑
确保所有合约的当前区块事件全部处理完成后,再更新lastFetchedBlock和保存ticker,再进入下一个区块循环。
内容的提问来源于stack exchange,提问作者committer
相关产品推荐
相关产品推荐

