Mongoose和NodeJs,Express的Mongo DB 4.0事务

时间:2018-07-08 00:57:33

标签: node.js mongodb mongoose transactions acid

我正在开发一个应用程序,其中我将MongoDB用作应用程序层中具有Nodejs + Express的数据库,我有两个集合,即

  1. 用户
  2. 交易

在这里,我必须以一定数量更新成千上万个用户的钱包,如果成功,则为每个交易创建一个具有相关信息的新文档,这是我的代码:

 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 中,谁能告诉我如何使用具有这些功能

的MongoDB最新Transactions功能来重新实现上述代码
Session.startTransaction()
Session.abortTransaction()
Session.commitTransaction()

MongoDB文档:Click Here

1 个答案:

答案 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小时),计算每个时段内每个用户的数量作为缓存。您可以通过添加以下内容来显示当前用户的钱包余额:

  • 用户的最后缓存量
  • 自上次缓存的金额以来,该用户的任何交易都发生。即0-6小时前。