阅读本帖后我有一些问题: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中并将它们链接在一起,因为每个块只修改一个文档。
答案 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的事务行为。它是这样的:
通过阅读交易的状态,您可以知道这两个(或更多)帐户是否已正确更新并处于干净状态。
代码/伪代码应该使用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
};
官方文档使用初始,待定,已应用,已完成状态,但您可以使用多个州你想要的,最小的是两个。因此,如果在更新两个帐户期间发生了某些事情,则在应用程序的另一部分中,您将查询以获取所有待处理的事务并在相关帐户上运行更新。