web-dev-qa-db-ja.com

MongooseおよびNode Js、Expressを使用したMongodb 4.0トランザクション

アプリケーションレイヤーでNodejs + Expressを使用してデータベースとしてMongoDBを使用しているアプリケーションを開発しています。つまり、2つのコレクションがあります。

  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ではありません。ユーザーウォレットが金額で更新される可能性がありますが、関連するトランザクションはトランザクションコレクションで作成されず、財務上の損失につながります。

最近、MongoDBTransactionsサポートを4.0 version、MongoDBのドキュメントを読みましたが、Node.jsでmongooseを使用して正常に実装することができませんでした。上記のコードは、これらの機能を持つMongoDBの最新のTransactions機能を使用して再実装されます。

Session.startTransaction()
Session.abortTransaction()
Session.commitTransaction()

MongoDB Docs: ここをクリック

11
Gaurav Kumar

node.jsのmongooseで、最新のトランザクション機能を使用して上記のコードを再実装する方法を教えてください

MongoDBマルチドキュメントトランザクション を使用するには mongoose でのサポートが必要です。v5.2以降のバージョンが必要です。例えば:

npm install [email protected]

Mongooseのトランザクションメソッドは、awaitを使用する必要があるセッションではなく、promiseを返します。見る:

たとえば、上記のリソースのサンプルとサンプルを変更して、次のことを試すことができます。

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 データモデル の変更も検討する必要があります。特に、2つのコレクションが自然にリンクされている場合。詳細については、 アトミック操作のモデルデータ も参照してください。

試すことができるモデルの例は、 Event Sourcing modelです。最初にトランザクションエントリをイベントとして作成し、次に aggregation を使用してユーザーのウォレットの残高を再計算します。

例えば:

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

次に、要件に応じて(つまり6時間ごとに)キャッシュとして期間ごとに各ユーザーの量を計算するプロセスを導入します。以下を追加することにより、現在のユーザーのウォレットの残高を表示できます。

  • ユーザーの最後にキャッシュされた量
  • ユーザーのトランザクションは、最後にキャッシュされた量以降に発生します。つまり、0〜6時間前です。
15
Wan Bachtiar