我正在开发一个应用程序,其中我将MongoDB用作应用程序层中具有Nodejs + Express的数据库,我有两个集合,即
在这里,我必须以一定数量更新成千上万个用户的钱包,如果成功,则为每个交易创建一个具有相关信息的新文档,这是我的代码:
userModel.update({_id : ObjectId(userId)}, {$inc : {wallet : 500}}, function (err, creditInfo) {
if(err){
console.log(err);
}
if(creditInfo.nModified > 0) {
newTransModel = new transModel({
usersId: ObjectId(userId),
amount: winAmt,
type: 'credit',
});
newTransModel.save(function (err, doc) {
if(err){
Cb(err);
}
});
}
});
但此解决方案不是atomic
,总有可能用金额更新用户钱包,但未在交易收集中创建相关交易,从而导致财务损失。
我听说最近MongoDB
在其Transactions
中增加了4.0 version
支持,我已经阅读了MongoDB文档,但无法通过Mongoose成功实现在Node.js 中,谁能告诉我如何使用具有这些功能
Transactions
功能来重新实现上述代码
Session.startTransaction()
Session.abortTransaction()
Session.commitTransaction()
MongoDB文档:Click Here
答案 0 :(得分:10)
使用Node.js中的猫鼬,谁能告诉我如何使用最新的交易功能重新实现以上代码
要在MongoDB multi-documents transactions中使用mongoose支持,您需要版本高于v5.2。例如:
npm install mongoose@5.2
猫鼬交易方法返回一个Promise,而不是需要使用await
的会话。参见:
例如,更改上面资源和您的示例中的示例,您可以尝试:
const User = mongoose.model('Users', new mongoose.Schema({
userId: String, wallet: Number
}));
const Transaction = mongoose.model('Transactions', new mongoose.Schema({
userId: ObjectId, amount: Number, type: String
}));
await updateWallet(userId, 500);
async function updateWallet(userId, amount) {
const session = await User.startSession();
session.startTransaction();
try {
const opts = { session };
const A = await User.findOneAndUpdate(
{ _id: userId }, { $inc: { wallet: amount } }, opts);
const B = await Transaction(
{ usersId: userId, amount: amount, type: "credit" })
.save(opts);
await session.commitTransaction();
session.endSession();
return true;
} catch (error) {
// If an error occurred, abort the whole transaction and
// undo any changes that might have happened
await session.abortTransaction();
session.endSession();
throw error;
}
}
不是原子的,总是有可能用金额更新用户钱包,但是未在交易收集中创建相关交易,导致财务损失
您还应该考虑更改MongoDB data models。特别是如果两个集合是自然相连的。另请参阅Model data for Atomic Operations,以了解更多信息。
您可以尝试的示例模型是Event Sourcing模型。首先创建一个交易条目作为事件,然后使用aggregation重新计算用户的钱包余额。
例如:
{tranId: 1001, fromUser:800, toUser:99, amount:300, time: Date(..)}
{tranId: 1002, fromUser:77, toUser:99, amount:100, time: Date(..)}
然后引入一个过程,根据需求(即每6小时),计算每个时段内每个用户的数量作为缓存。您可以通过添加以下内容来显示当前用户的钱包余额: