不要怂,就是干,撸起袖子干!

Commit c130d070 by Simon Schick Committed by GitHub

test: debug transaction leak (#12153)

1 parent 2e42d66e
...@@ -33,6 +33,7 @@ const hookTypes = { ...@@ -33,6 +33,7 @@ const hookTypes = {
beforeFindAfterOptions: { params: 1 }, beforeFindAfterOptions: { params: 1 },
afterFind: { params: 2 }, afterFind: { params: 2 },
beforeCount: { params: 1 }, beforeCount: { params: 1 },
transactionCreated: { params: 1, sync: true, noModel: true },
beforeDefine: { params: 2, sync: true, noModel: true }, beforeDefine: { params: 2, sync: true, noModel: true },
afterDefine: { params: 1, sync: true, noModel: true }, afterDefine: { params: 1, sync: true, noModel: true },
beforeInit: { params: 2, sync: true, noModel: true }, beforeInit: { params: 2, sync: true, noModel: true },
......
...@@ -644,11 +644,11 @@ class Sequelize { ...@@ -644,11 +644,11 @@ class Sequelize {
const query = new this.dialect.Query(connection, this, options); const query = new this.dialect.Query(connection, this, options);
try { try {
await this.runHooks('beforeQuery', options, query); await this.runHooks('beforeQuery', options, query, sql);
await checkTransaction(); checkTransaction();
return await query.run(sql, bindParameters); return await query.run(sql, bindParameters);
} finally { } finally {
await this.runHooks('afterQuery', options, query); await this.runHooks('afterQuery', options, query, sql);
if (!options.transaction) { if (!options.transaction) {
await this.connectionManager.releaseConnection(connection); await this.connectionManager.releaseConnection(connection);
} }
...@@ -1100,6 +1100,7 @@ class Sequelize { ...@@ -1100,6 +1100,7 @@ class Sequelize {
} }
const transaction = new Transaction(this, options); const transaction = new Transaction(this, options);
this.runHooks('transactionCreated', transaction);
if (!autoCallback) { if (!autoCallback) {
await transaction.prepareEnvironment(false); await transaction.prepareEnvironment(false);
......
...@@ -2,14 +2,29 @@ ...@@ -2,14 +2,29 @@
const Support = require('../support'); const Support = require('../support');
const runningQueries = new Set(); const runningQueries = new Map();
const runningTransactions = new Map(); // map transaction option to queries.
before(function() { before(function() {
this.sequelize.addHook('beforeQuery', (options, query) => { this.sequelize.addHook('transactionCreated', t => { // tracking race condition, remove me if no longer present.
runningQueries.add(query); t.trace = new Error().stack;
}); });
this.sequelize.addHook('afterQuery', (options, query) => { this.sequelize.addHook('beforeQuery', (options, query, sql) => {
runningQueries.set(query, options);
if (options.transaction) {
const queryList = runningTransactions.get(options.transaction.id);
if (queryList) {
queryList.push(sql);
} else {
runningTransactions.set(options.transaction.id, [sql]);
}
}
});
this.sequelize.addHook('afterQuery', (options, query, sql) => {
runningQueries.delete(query); runningQueries.delete(query);
if (options.transaction && ['COMMIT', 'ROLLBACK'].includes(sql)) {
runningTransactions.delete(options.transaction.id);
}
}); });
}); });
...@@ -22,7 +37,18 @@ afterEach(function() { ...@@ -22,7 +37,18 @@ afterEach(function() {
return; return;
} }
let msg = `Expected 0 running queries. ${runningQueries.size} queries still running in ${this.currentTest.fullTitle()}\n`; let msg = `Expected 0 running queries. ${runningQueries.size} queries still running in ${this.currentTest.fullTitle()}\n`;
msg += `Queries:\n${[...runningQueries].map(query => `${query.uuid}: ${query.sql}`).join('\n')}`; msg += 'Queries:\n\n';
for (const [query, options] of runningQueries) {
msg += `${query.uuid}: ${query.sql}\n`;
if (options.transaction) {
const relatedTransaction = runningTransactions.get(options.transaction.id);
if (relatedTransaction) {
msg += options.transaction.trace;
msg += 'In transaction:\n\n';
msg += relatedTransaction.join('\n');
}
}
}
throw new Error(msg); throw new Error(msg);
}); });
......
...@@ -549,7 +549,7 @@ if (current.dialect.supports.transactions) { ...@@ -549,7 +549,7 @@ if (current.dialect.supports.transactions) {
return User.findAll({ transaction }) return User.findAll({ transaction })
.then(users => expect( users ).to.have.lengthOf(0)) .then(users => expect( users ).to.have.lengthOf(0))
.then(() => User.create({ username: 'jan' })) // Create a User outside of the transaction .then(() => User.create({ username: 'jan' })) // Create a User outside of the transaction
.then(() => User.findAll({ transaction })) .then(() => User.findAll({ transaction }))
.then(users => expect( users ).to.have.lengthOf(0)); // We SHOULD NOT see the created user inside the transaction .then(users => expect( users ).to.have.lengthOf(0)); // We SHOULD NOT see the created user inside the transaction
}); });
}) })
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!