Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easy way to handle nested transactions

Suppose there is an "addUser" function, inside we need to insert a record to "Account" table and "User" table, so the two steps have to be within a transaction too, so we will write the following code:

function addUser (userName, password) {
    sequelize.transaction(function () {
        return AccountModel.create(...)
        .then(UserModel.create(...))    
    })
}

However, in another "addTeam" function, inside we need to insert a record to "Team" table and create a admin user using the above function. The function also need to be wrapped inside a transaction.

So the problem comes, the "addUser" function sometimes need to begin a new transaction, and sometimes need to use the transaction passed in. The most obvious ways is below:

function addUser (userName, password, transaction) {
       let func = function (t) {
           return  AccountModel.create(..., t)
           .then(()=>UserModel.create(..., t)));
       if (transaction) func(t);
       else sequelize.transaction(x=>func(t));
}

function addTeam() {
     sequelize.transaction(x=> {
         TeamModel.create(..., x)
         .then(y=>addUser(x));
     });
}

Obviously, it is awful. How to deal with it easily, which let transaction totally transparent to the caller like below:

@Transaction
async function addUser(userName, password) {
    await AccountModel.create(...);
    await UserModel.create(...);
}

@Transaction
async function addTeam(...) {
    await TeamModel.create(...);
    await addUser(...);
} 
like image 485
Ron Avatar asked Jun 28 '16 07:06

Ron


People also ask

What are the advantages of nested transactions?

Advantages of Nested Transactions 1. Nested transactions allow for a simple composition of subtransactions improving modularity of the overall structure. 2. The concurrent execution of subtransactions that follow the prescribed rules allows for enhanced concurrency while preserving consistency.

Can nested transaction be distributed?

A flat or nested transaction that accesses objects handled by different servers is referred to as a distributed transaction.

Does hibernate support nested transactions?

While Hibernate does not explicitly support nested transactions, using a JDBC 3.0 driver that is able to create savepoints can achieve this. Create a Connection at the start of the program when the SessionFactory is created.


2 Answers

sequelize.transaction accepts an options object - If options.transaction is set, this will create a savepoint in the transaction (provided that the SQL dialects supports it), otherwise it will create a new transaction

http://docs.sequelizejs.com/en/latest/api/sequelize/#transactionoptions-promise

So you should be able to do simply

sequelize.transaction({ transaction }, x=>func(t));
like image 63
Jan Aagaard Meier Avatar answered Oct 04 '22 02:10

Jan Aagaard Meier


If you use CLS, a very simple helper function will do the work.

const cls = require('continuation-local-storage');
const Sequelize = require('sequelize');
const NAMESPACE = 'your-namespace';

// Use CLS for Sequelize
Sequelize.cls = cls.createNamespace(NAMESPACE);
const sequelize = new Sequelize(...);

/* * * * * * * * * * * * * * * * * * * * *
 * THE MAGIC: Create a transaction wrapper
 * * * * * * * * * * * * * * * * * * * * */

function transaction(task) {
  return cls.getNamespace(NAMESPACE).get('transaction') ? task() : sequelize.transaction(task);
};

/* * * * * * * * * * * * * * * * * * * * *
 * Your code below
 * * * * * * * * * * * * * * * * * * * * */

function addUser(userName, password) {
  return transaction(function() {
    return AccountModel
      .create(...)
      .then(() => UserModel.create(...));
  });
}

function addTeam() {
  return transaction(function() {
    return TeamModel
      .create(...)
      .then(() => addUser(...));
  });
}
like image 26
Tim Wong Avatar answered Oct 04 '22 02:10

Tim Wong