【问题标题】:Mongodb Two Phase Commits And Async OperationMongodb 两阶段提交和异步操作
【发布时间】:2015-12-02 23:02:53
【问题描述】:

看完这篇文章我有一些问题:Perform Two Phase Commits on Mongodb

问题1:在帖子中,它旨在提供一个反向交易的示例。我的问题是实际代码是什么样的?它只是像这样将所有功能组合在一起吗?如果没有,那我就整理一下帖子里的所有代码

var t = db.transactions.findOne( { state: "initial" } ) 
db.transactions.update(
   { _id: t._id, state: "initial" },
   {
      $set: { state: "pending" },
      $currentDate: { lastModified: true }
   }
)
db.accounts.update(
   { _id: t.source, pendingTransactions: { $ne: t._id } },
   { $inc: { balance: -t.value }, $push: { pendingTransactions: t._id } }
)
db.accounts.update(
   { _id: t.destination, pendingTransactions: { $ne: t._id } },
   { $inc: { balance: t.value }, $push: { pendingTransactions: t._id } }
)
db.transactions.update(
   { _id: t._id, state: "pending" },
   {
     $set: { state: "applied" },
     $currentDate: { lastModified: true }
   }
)
db.accounts.update(
   { _id: t.source, pendingTransactions: t._id },
   { $pull: { pendingTransactions: t._id } }
)
db.accounts.update(
   { _id: t.destination, pendingTransactions: t._id },
   { $pull: { pendingTransactions: t._id } }
)
db.transactions.update(
   { _id: t._id, state: "applied" },
   {
     $set: { state: "done" },
     $currentDate: { lastModified: true }
   }
)

问题2:既然mongodb保证在处理单个文档时它的操作是原子的,那么我是否可以将上述所有块包装成promise并将它们链接在一起,因为每个块只修改一个文档。

【问题讨论】:

    标签: mongodb asynchronous promise atomic


    【解决方案1】:

    对于第二个问题,你不必包装任何东西,update() 已经返回了一个承诺。你可以像这样异步运行它们

    var t = db.transactions.findOne({ state: "initial" });
    
    var query1 = db.transactions.update(
        { _id: t._id, state: "initial" },
        {
            $set: { state: "pending" },
            $currentDate: { lastModified: true }
        }
    );
    
    var query2 = db.accounts.update(
        { _id: t.source, pendingTransactions: { $ne: t._id } },
        { $inc: { balance: -t.value }, $push: { pendingTransactions: t._id } }
    );
    
    Promise.all([query1, query2]).then(function done(params) {
        console.log('all updates done');
    }).catch(function catch(err) {
        console.log(err);
    });
    

    编辑

    我快速阅读了 mongodb 文档的链接,据我了解,两部分提交是尝试重新创建 sql 的事务行为。它是这样的:

    • 创建一个文档(事务)以记录对现有文档的未来更新
      1. 根据需要更新现有文件(示例中为银行账户)
      2. 当两个账户都正确更新时更新交易

    通过阅读交易状态,您可以知道两个(或更多)帐户是否已正确更新并处于干净状态。

    代码/伪代码应该像这样使用承诺:

    // create the transaction
    db.transactions.insert(
        { _id: 1, source: "A", destination: "B", value: 100, state: "initial", lastModified: new Date() }
    ).then(function (err, transaction) {
        // now you can start updating the accounts (first phase)
        var query1 = db.accounts.update(
            { _id: t.source, pendingTransactions: { $ne: t._id } },
            { $inc: { balance: -t.value }, $push: { pendingTransactions: t._id } }
        );
        var query2 = db.accounts.update(
            { _id: t.destination, pendingTransactions: { $ne: t._id } },
            { $inc: { balance: t.value }, $push: { pendingTransactions: t._id } }
        );
        // the two accounts must be updated together
        return Promise.all([query1, query2]);
    }).then(function (err, result) {
        // accounts are update correctly, now update the transaction (second phase)
        // the transaction id must be stored somewhere in the code before
        return db.transactions.update(
            { _id: t._id, state: "pending" },
            {
                $set: { state: "applied" },
                $currentDate: { lastModified: true }
            }
        );
    }).then(function (err, result) {
        // another updates on accounts if needed
        // ...
    }).catch(function (err) {
        // deal with all errors here
    };
    

    官方文档使用 initialpendingapplieddone 状态,但您可以使用尽可能多的状态你想要,最少是两个。因此,如果在两个帐户的更新过程中发生了一些事情,在您的应用程序的另一部分中,您查询以获取所有待处理的交易并在相关帐户上运行更新。

    【讨论】:

    • 那么哪一部分让“两句提交”特别?
    • 我没有触及“两阶段提交”部分,我正在更新我的答案。
    猜你喜欢
    • 1970-01-01
    • 2011-11-15
    • 2011-11-24
    • 2013-05-21
    • 2019-04-13
    • 1970-01-01
    • 2012-07-20
    • 2012-11-06
    相关资源
    最近更新 更多