使用Mongoose和NodeJs,Express的Mongo DB 4.0交易

Gau*_*ar 11 transactions acid mongoose mongodb node.js

我正在开发一个应用程序,我在应用程序层使用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); 
            }
        });
    }                            
});
Run Code Online (Sandbox Code Playgroud)

但是这个解决方案并不atomic总是有可能用金额更新用户钱包,但是在交易收集中没有创建相关交易导致经济损失.

我听说最近MongoDB已经添加Transactions了对它的支持4.0 version,我已经阅读了MongoDB文档但是无法用Node.js中的mongoose成功实现它,有人能告诉我如何使用TransactionsMongoDB 的最新功能重新实现上面的代码哪些有这些功能

Session.startTransaction()
Session.abortTransaction()
Session.commitTransaction()
Run Code Online (Sandbox Code Playgroud)

MongoDB文档:点击这里

Wan*_*iar 16

在Node.js中使用mongoose,任何人都可以告诉我如何使用最新的事务功能重新实现上面的代码

要在mongoose中使用MongoDB多文档事务支持,您需要大于v5.2的版本.例如:

npm install mongoose@5.2
Run Code Online (Sandbox Code Playgroud)

Mongoose事务方法返回一个promise而不是一个需要使用的session 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; 
  }
}
Run Code Online (Sandbox Code Playgroud)

不是原子的,总是有可能用金额更新用户钱包,但是在交易集合中没有创建相关交易导致经济损失

您还应该考虑更改MongoDB 数据模型.特别是如果这两个系列是自然联系的.有关更多信息,另请参阅原子操作的模型数据.

您可以尝试的示例模型是Event Sourcing模型.首先创建一个事务条目作为事件,然后使用聚合重新计算用户的钱包余额.

例如:

{tranId: 1001, fromUser:800, toUser:99, amount:300, time: Date(..)}
{tranId: 1002, fromUser:77, toUser:99, amount:100, time: Date(..)}
Run Code Online (Sandbox Code Playgroud)

然后根据需求(即每6小时)引入一个过程来计算每个用户每个用户的数量作为缓存.您可以通过添加以下内容来显示当前用户的钱包余额:

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

  • 这是一个示例,您必须根据自己的用例进行调整。1) 确保您创建了一个如上所示的异步函数。2) 您可以尝试在事务中执行 [bulkWrite()](http://mongoosejs.com/docs/api.html#bulkwrite_bulkWrite)。 (2认同)