bookshelf.js锁定事务

4
有没有可能使用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次。其中5个数据集是重复的。这将在数据库中产生大约2-3个重复项。目前的解决方法是使用setTimeout将整个过程包装起来,并设置一个随机的超时时间,在0到10秒之间,这几乎不会给我带来重复项。但这显然不是一个可以用于生产的解决方案。

2个回答

2

好的,最终我决定使用async.js库和它的队列。队列保证最多n个异步任务同时执行,这里是1。 我创建了一个模块,导出了一个队列实例。这样我可以在多个模块中使用它。它只是等待Promise被兑现。

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

将事务包装到函数中很重要,这样它就不会立即执行。


你的问题不能通过唯一键来解决吗?你想要实现的是某种“事务缺席”,这个概念对我来说是全新的。据我所知,没有任何数据库实现这样的机制。你能用其他技术使它工作吗? - flaviodesousa
我最初想用唯一键来解决它,但是那样会抛出错误,你必须捕获它们。所以我更喜欢手动调度查询。我需要的是 https://en.wikipedia.org/wiki/Two-phase_locking。这是基本的数据库功能。 - user1862511
是的,knex支持通过forUpdate()和forShare()实现两阶段锁定。但在您的情况下,没有要锁定的对象,或者像我说的,“事务缺失”,我认为两阶段锁定无法解决这个问题。 - flaviodesousa
如果您在多个节点/服务器上部署代码,则此方法可能无法正常工作。 - Abhishek

1
由于Bookshelf事务(transactions)是Promises,您无需显式调用commit()rollback()。只需让已完成的Promise自行提交,或者您可以通过抛出异常来强制回滚。
在您的代码中,显然有一个小Bug可能会导致问题:在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
});

我现在无法测试,但希望它能有所帮助。


1
抱歉,由于公司内部事务的重命名一些变量,导致缺少参数。我已经三次检查过了,现在是相同的。但问题并没有消失。我现在正在研究使用async.js队列的解决方案。 - user1862511

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接