Mongodb两阶段提交和异步操作

时间:2015-12-02 06:50:55

标签: mongodb asynchronous promise atomic

阅读本帖后我有一些问题:Perform Two Phase Commits on Mongodb

问题1:在帖子中,它的目的是回溯交易的一个例子。我的问题是实际的代码是什么样的?它只是将所有这些功能组合在一起吗?如果没有,那么hwo to wen组织该帖子中的所有代码

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在处理单个文档时保证其操作是原子的,那么我可以将所有上述块包装到promises中并将它们链接在一起,因为每个块只修改一个文档。

1 个答案:

答案 0 :(得分: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 doc的链接,根据我的理解,这两部分提交是尝试重新创建sql的事务行为。它是这样的:

  • 创建一个文档(事务)以记录对现有文档的未来更新
    1. 根据需要更新现有文档(示例中的银行帐户)<<第一阶段
    2. 在正确更新两个帐户时更新交易<<第二阶段

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

代码/伪代码应该使用promises:

// 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
};

官方文档使用初始待定已应用已完成状态,但您可以使用多个州你想要的,最小的是两个。因此,如果在更新两个帐户期间发生了某些事情,则在应用程序的另一部分中,您将查询以获取所有待处理的事务并在相关帐户上运行更新。