Bookshelf.js locked transactions

Is it possible to create atomic database transactions with bookshelf? I have a problem with duplicates in the database. The problematic code looks like this:

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

      

I am calling the method containing this code almost synchronously and asynchronously 20 times. Of these 20, there are 5 duplicate datasets. This results in about 2-3 duplicates in the database. The current workaround is to wrap the whole thing in a setTimeout with a random interval between 0 and 10 seconds, which almost never gives me duplicates. But this is clearly not a production-ready solution.

+3


source to share


2 answers


OK, so in the end I decided to go with the async.js library and queue it up. The queue ensures that a maximum of n asynchronous tasks are running concurrently. In this case 1. I created a module that exports the queue instance. This way I can use it for multiple modules. He's just waiting for the promise to fulfill.

var async = require('async');

module.exports = async.queue(function (task, callback) {
    task().then(function () {
        callback();
    });
},1);

      

Then in the module where I need an atomic transaction, I have the following code:



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

      

It's important to move the transaction into a function so that it doesn't get executed right away.

0


source


Since Bookshelf transactions are promises, you don't need to explicitly call commit()

or rollback()

. Just let a fulfilled promise make a commitment, or you can force a rollback by throwing an exception.

There was apparently a small bug in your code that could cause problems: the argument is missing in fetch()

then()

- this argument is the result of a call fetch()

, an instance, if the object was found or null

if not.



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

      

I can't test it now, but hope it helps.

+1


source







All Articles