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

Function verifyDeadlock

test/integration/transaction.test.js:527–639  ·  test/integration/transaction.test.js::verifyDeadlock
()

Source from the content-addressed store, hash-verified

525
526 it(class="st">'should release the connection for a deadlocked transaction (2/2)', async function() {
527 const verifyDeadlock = async () => {
528 const User = this.sequelize.define(class="st">'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: class="st">'jan' });
535
536 class="cm">// 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 class="cm">// This will cause other sessions to be able to read the row but not modify it.
538 class="cm">// So, if another transaction tries to update those same rows, it will wait until T1 commits (or rolls back).
539 class="cm">// 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 class="cm">// 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 class="cm">// Then, we want to see that an attempt to update that row from T2 will be queued until T1 commits.
548 class="cm">// However, before commiting T1 we will also perform an update via T1 on the same rows.
549 class="cm">// 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 class="cm">// Meanwhile, T1 should still be ok.
551 const executionOrder = [];
552 const [t2AttemptData, t1AttemptData] = await pSettle([
553 (async () => {
554 try {
555 executionOrder.push(class="st">'Begin attempt to update via T2');
556 await t2Jan.update({ awesome: false }, { transaction: t2 });
557 executionOrder.push(class="st">'Done updating via T2'); class="cm">// Shouldn't happen
558 } catch (error) {
559 executionOrder.push(class="st">'Failed to update via T2');
560 throw error;
561 }
562
563 await delay(30);
564
565 try {
566 class="cm">// We shouldnclass="st">'t reach this point, but if we do, let's at least commit the transaction
567 class="cm">// to avoid forever occupying one connection of the pool with a pending transaction.
568 executionOrder.push(class="st">'Attempting to commit T2');
569 await t2.commit();
570 executionOrder.push(class="st">'Done committing T2');
571 } catch {
572 executionOrder.push(class="st">'Failed to commit T2');
573 }
574 })(),
575 (async () => {
576 await delay(100);
577
578 try {
579 executionOrder.push(class="st">'Begin attempt to update via T1');
580 await t1Jan.update({ awesome: true }, { transaction: t1 });
581 executionOrder.push(class="st">'Done updating via T1');
582 } catch (error) {
583 executionOrder.push(class="st">'Failed to update via T1'); class="cm">// 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