MCPcopy
hub / github.com/sequelize/sequelize / verifyDeadlock

Function verifyDeadlock

test/integration/transaction.test.js:527–639  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

525
526 it('should release the connection for a deadlocked transaction (2/2)', async function() {
527 const verifyDeadlock = async () => {
528 const User = this.sequelize.define('user', {
529 username: DataTypes.STRING,
530 awesome: DataTypes.BOOLEAN
531 }, { timestamps: false });
532
533 await this.sequelize.sync({ force: true });
534 const { id } = await User.create({ username: 'jan' });
535
536 // First, we start a transaction T1 and perform a SELECT with it using the `LOCK.SHARE` mode (setting a shared mode lock on the row).
537 // This will cause other sessions to be able to read the row but not modify it.
538 // So, if another transaction tries to update those same rows, it will wait until T1 commits (or rolls back).
539 // https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
540 const t1 = await this.sequelize.transaction();
541 const t1Jan = await User.findByPk(id, { lock: t1.LOCK.SHARE, transaction: t1 });
542
543 // Then we start another transaction T2 and see that it can indeed read the same row.
544 const t2 = await this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED });
545 const t2Jan = await User.findByPk(id, { transaction: t2 });
546
547 // Then, we want to see that an attempt to update that row from T2 will be queued until T1 commits.
548 // However, before commiting T1 we will also perform an update via T1 on the same rows.
549 // This should cause T2 to notice that it can't function anymore, so it detects a deadlock and automatically rolls itself back (and throws an error).
550 // Meanwhile, T1 should still be ok.
551 const executionOrder = [];
552 const [t2AttemptData, t1AttemptData] = await pSettle([
553 (async () => {
554 try {
555 executionOrder.push('Begin attempt to update via T2');
556 await t2Jan.update({ awesome: false }, { transaction: t2 });
557 executionOrder.push('Done updating via T2'); // Shouldn't happen
558 } catch (error) {
559 executionOrder.push('Failed to update via T2');
560 throw error;
561 }
562
563 await delay(30);
564
565 try {
566 // We shouldn't reach this point, but if we do, let's at least commit the transaction
567 // to avoid forever occupying one connection of the pool with a pending transaction.
568 executionOrder.push('Attempting to commit T2');
569 await t2.commit();
570 executionOrder.push('Done committing T2');
571 } catch {
572 executionOrder.push('Failed to commit T2');
573 }
574 })(),
575 (async () => {
576 await delay(100);
577
578 try {
579 executionOrder.push('Begin attempt to update via T1');
580 await t1Jan.update({ awesome: true }, { transaction: t1 });
581 executionOrder.push('Done updating via T1');
582 } catch (error) {
583 executionOrder.push('Failed to update via T1'); // Shouldn't happen
584 throw error;

Callers 1

Calls 7

defineMethod · 0.80
transactionMethod · 0.80
findByPkMethod · 0.80
commitMethod · 0.80
syncMethod · 0.45
createMethod · 0.45
updateMethod · 0.45

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…