sequelize: Deadlock on multiple transactions
What are you doing?
const Sequelize = require('sequelize');
const sequelize = new Sequelize('mysql://user:password@dburl:3306/test')
class TestUser extends Sequelize.Model {}
TestUser.init({
username: Sequelize.STRING,
birthday: Sequelize.DATE
}, { sequelize, modelName: 'testuser' })
for (var i = 0; i < 5; i++) {
sequelize.transaction(t => {
return TestUser.findOne({
where: { username: 'janedoe' }
}, { transaction: t })
.then(user => {
return TestUser.update(
{ birthday: new Date(1980, 1, 8) },
{
where: { username: 'janedoe' }
},
{ transaction: t }
)
})
})
.then(() => console.log('done'))
.catch(err => console.log(err))
}
The loop is to mimic consecutive requests to a server. Adjusting the loop count below the connection pool max (default is 5) stops the problem.
To Reproduce Steps to reproduce the behavior:
- Run the quick example in the documentation (to create an entry)
- Run the code above
- See all of updates above fail
What do you expect to happen?
Update the entry successfully.
What is actually happening?
Executing (acafd742-e1e0-4181-be35-ded526d3d4d6): START TRANSACTION;
Executing (358028a0-3206-4262-b96a-f06f9359ecb8): START TRANSACTION;
Executing (eb448c6b-2098-499c-8d60-b6b4c83b2c23): START TRANSACTION;
Executing (cd0d76fc-78a0-4799-bcce-82451fcc38fd): START TRANSACTION;
Executing (23e9b61e-91ab-4764-85a3-98893e0d0f5a): START TRANSACTION;
Executing (acafd742-e1e0-4181-be35-ded526d3d4d6): ROLLBACK;
Executing (358028a0-3206-4262-b96a-f06f9359ecb8): ROLLBACK;
Executing (eb448c6b-2098-499c-8d60-b6b4c83b2c23): ROLLBACK;
Executing (cd0d76fc-78a0-4799-bcce-82451fcc38fd): ROLLBACK;
Executing (23e9b61e-91ab-4764-85a3-98893e0d0f5a): ROLLBACK;
{ SequelizeConnectionAcquireTimeoutError: Operation timeout
...
Environment
Dialect:
- mysql
- postgres
- sqlite
- mssql
- any
Dialect library version: 1.6.5 Database version: 5.7.24 Sequelize version: 5.8.7 Node Version: 8.12.0 OS: Linux 4.19.23-gentoo (x86_64) If TypeScript related: TypeScript version: N/A
Tested with latest release:
- No
- Yes, specify that version: 5.8.7
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 6
- Comments: 17 (7 by maintainers)
I believe I have a workaround using p-queue.
Maybe this could be worked into
Sequelize.transaction()
so that it works out of the box.Thanks for your reply, @papb. I’m glad to hear that you consider this what should be handled by Sequelize and looking forward to the day I can send a bunch of requests without worry about queueing.
Hi all, i’ve been having the same trouble myself but found my answer here:
https://javascript.plainenglish.io/how-to-handle-database-deadlock-in-sequelize-6bee46e9ed99
Tek Loon has amended his connection to include a retry if there’s a deadlock, which sorted out my issue, here’s the snippet:
The above uses mysql but also worked for my mssql DB. Hope this helps!
Leaving this comment in case anyone has a similar problem. Until now I was using the following code template:
but I have found that only pool.max transactions can run simultaneously, and the (pool.max + 1) transaction will cause a deadlock which completely blocks new transactions.
The way I have solved this problem is using this template instead (instead of async-await you can you Promises):
Can you clarify this? Did you expect Sequelize to automatically detect that the number of transactions reached the limit and wait before starting the transaction?
@mycoboco The following code does not deadlock.
And simplified minimal case solutions:
Will deadlock
will not
Please verify on your end as well. I recommend using CLS to make transactions sticky. Cheers.
So to speak, yes, because Sequelize has enough information and is the right place to do that, I think. I don’t think Sequelize should require every server implementation using it to have a global queue for handling requests working with transactions.
If I’m the first or the only to expect this, which would make me surprised and perplexed, you can consider this a feature request. What I’d like to know is whether I should handle such requests by myself or wait for Sequelize to do for me.