下面我们来详细讲解“mongoDB 4.0事务回滚的辛酸历程探究”的完整攻略。
概述
在mongoDB 4.0版本中,引入了对事务的支持。这个功能对于一些复杂的应用场景来说非常重要。但是,在使用事务的过程中,用户可能会遇到一些意想不到的问题,比如事务回滚失败等。本文将详细介绍使用mongoDB 4.0事务时的注意事项和陷阱,并结合两个实例来说明。
示例一
首先,我们来看一个简单的例子:
async function runTxn() {
let session = client.startSession({defaultTransactionOptions: {readConcern: {level: 'local'}}});
session.startTransaction();
try {
const opts = { session };
const result1 = await db.collection('collection1').insertOne({ a: 1 }, opts);
const result2 = await db.collection('collection2').updateOne({ a: 1 }, { $set: { b: 2 } }, opts);
throw new Error('Exception');
await session.commitTransaction();
} catch (err) {
await session.abortTransaction();
console.log('Aborted');
} finally {
session.endSession();
}
}
这个例子创建了两个集合collection1
和collection2
,然后使用事务在它们中插入和更新一些数据再抛出一个异常。然后,该事务应该回滚并输出“Aborted”字符串。
但是,在mongoDB 4.0正式发布后,有一些用户遇到了这样的问题,即“Aborted”字符串没有被输出,而是得到了一个事务已经提交的消息。
这个问题的原因是用户在抛出异常后没有立即终止事务,导致事务虽然抛出了异常但仍被提交。
在这个例子中,我们可以通过在事务代码块的最后添加throw err;
的语句,来修复这个问题。
async function runTxn() {
let session = client.startSession({defaultTransactionOptions: {readConcern: {level: 'local'}}});
session.startTransaction();
try {
const opts = { session };
const result1 = await db.collection('collection1').insertOne({ a: 1 }, opts);
const result2 = await db.collection('collection2').updateOne({ a: 1 }, { $set: { b: 2 } }, opts);
throw new Error('Exception');
await session.commitTransaction();
} catch (err) {
await session.abortTransaction();
console.log('Aborted');
throw err;
} finally {
session.endSession();
}
}
示例二
接下来,让我们看看另一个例子:
async function runTxn() {
let session = client.startSession({defaultTransactionOptions: {readConcern: {level: 'local'}}});
session.startTransaction();
try {
const opts = { session };
const result1 = await db.collection('collection1').insertOne({ a: 1 }, opts);
const result2 = await db.collection('collection2').updateOne({ a: 1 }, { $set: { b: 2 } }, opts);
const result3 = await db.collection('collection3').insertOne({ b: 3 }, opts);
await session.commitTransaction();
} catch (err) {
await session.abortTransaction();
console.log('Aborted');
} finally {
session.endSession();
}
}
这个例子与前一个例子类似,不同之处在于我们向另一个集合collection3
插入了一些数据。
如果在这个例子中发生了一个故障,比如是机器宕机,我们再次启动mongoDB时,会收到“Prepare transaction error”的错误消息。
这是因为我们没有清理事务状态。在我们的代码中,我们没有在catch
块中设置或删除Mongo.Collection
状态。如果我们在catch
块中添加以下行:
MongoInternals.NpmModules.mongodb_client_session.modernized((clientSession) => {
clientSession && clientSession.abortTransaction();
});
可以解决这个问题并正确地回滚事务。
总结
在使用mongoDB 4.0事务时,我们需要注意一些细节和问题。在示例一中,我们需要正确处理异常以确保事务正确回滚。在示例二中,我们需要正确处理Mongo.Collection状态以确保在发生意外故障时事务正确回滚。