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

Function verifyDeadlock

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

Source from the content-addressed store, hash-verified

538 }
539
540 const verifyDeadlock = async () => {
541 const User = this.sequelize.define('user', {
542 username: DataTypes.STRING,
543 awesome: DataTypes.BOOLEAN
544 }, { timestamps: false });
545
546 await this.sequelize.sync({ force: true });
547 const { id } = await User.create({ username: 'jan' });
548
549 // 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).
550 // This will cause other sessions to be able to read the row but not modify it.
551 // So, if another transaction tries to update those same rows, it will wait until T1 commits (or rolls back).
552 // https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html
553 const t1 = await this.sequelize.transaction();
554 const t1Jan = await User.findByPk(id, { lock: t1.LOCK.SHARE, transaction: t1 });
555
556 // Then we start another transaction T2 and see that it can indeed read the same row.
557 const t2 = await this.sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED });
558 const t2Jan = await User.findByPk(id, { transaction: t2 });
559
560 // Then, we want to see that an attempt to update that row from T2 will be queued until T1 commits.
561 // However, before commiting T1 we will also perform an update via T1 on the same rows.
562 // 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).
563 // Meanwhile, T1 should still be ok.
564 const executionOrder = [];
565 const [t2AttemptData, t1AttemptData] = await pSettle([
566 (async () => {
567 try {
568 executionOrder.push('Begin attempt to update via T2');
569 await t2Jan.update({ awesome: false }, { transaction: t2 });
570 executionOrder.push('Done updating via T2'); // Shouldn't happen
571 } catch (error) {
572 executionOrder.push('Failed to update via T2');
573 throw error;
574 }
575
576 await delay(30);
577
578 try {
579 // We shouldn't reach this point, but if we do, let's at least commit the transaction
580 // to avoid forever occupying one connection of the pool with a pending transaction.
581 executionOrder.push('Attempting to commit T2');
582 await t2.commit();
583 executionOrder.push('Done committing T2');
584 } catch {
585 executionOrder.push('Failed to commit T2');
586 }
587 })(),
588 (async () => {
589 await delay(100);
590
591 try {
592 executionOrder.push('Begin attempt to update via T1');
593 await t1Jan.update({ awesome: true }, { transaction: t1 });
594 executionOrder.push('Done updating via T1');
595 } catch (error) {
596 executionOrder.push('Failed to update via T1'); // Shouldn't happen
597 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