是否有可能与bookshelf创建原子数据库事务?我在数据库中有一个重复的问题。有问题的代码如下:
bookshelf.transaction(function (t) {
var modelLocation = new Models.Location({'name':event.venue});
modelLocation.fetch({transacting:t})
.then(function (fetchedLocation) {
if (!fetchedLocation) {
modelLocation.save(null,{transacting:t}).then(function (savedModel) {
t.commit(savedModel)
}).catch(function (err) {
t.rollback(err)
});
}
else{
t.commit(fetchedLocation)
}
})
})
我几乎同时异步地调用包含此代码的方法20次。从这20个数据集中,有5个重复的数据集。这导致数据库中大约有2-3个副本。当前的解决方案是将整个事情包装在setTimeout中,并在0到10秒之间随机超时,这几乎不会给我重复。但这显然不是一个生产就绪的解决方案。
好的,所以最后,我决定使用async.js库和它的队列。队列保证最多同时执行n个异步任务。在这种情况下是1。我做了一个导出队列实例的模块。这样我就可以跨多个模块使用它。它只是等待诺言的实现。
var async = require('async');
module.exports = async.queue(function (task, callback) {
task().then(function () {
callback();
});
},1);
然后在模块中,我需要一个"原子"事务,我有以下代码:
var queue = require('./transactionQueue');
...
...
queue.push(function(){
return bookshelf.transaction(function (t) {
var modelLocation = new Models.Location({'name':event.venue});
return modelLocation
.fetch({transacting:t})
.then(function (fetchedLocation) {
if (!fetchedLocation) {
return modelLocation
.save(null,{transacting:t});
}
});
});
});
将事务包装到函数中是很重要的,这样它就不会立即执行。
由于Bookshelf事务是承诺,您不需要显式调用commit()
或rollback()
。只要让完成的承诺自己提交,或者你可以通过抛出异常来强制回滚。
在你的代码中,显然有一个可能引起麻烦的小错误:fetch()
的then()
缺少一个参数——这个参数是fetch()
调用的结果,如果找到了对象则是一个实例,如果没有找到则是null
。
bookshelf.transaction(function (t) {
var modelLocation = new Models.Location({'name':event.venue});
return modelLocation
.fetch()
.then(function (fetchedLocation) {
if (!fetchedLocation) {
modelLocation
.save(null,{transacting:t});
}
})l
});
我现在无法测试,但我希望它能帮助。