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(...);
}
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.
A flat or nested transaction that accesses objects handled by different servers is referred to as a distributed transaction.
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.
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));
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(...));
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With