Sequelize 并发事务在 PostgreSQL 的事务中变得空闲
Sequelize concurrent transactions become idle in transaction in PostgreSQL
当我使用 sequelize.transaction()
创建足够多的并发事务时,sequelize 不会进行。
例如,如果我用 pool.max = 5 开始 5 个事务,sequelize 在 postgres 中执行 START TRANSACTION;
并且不执行任何进一步的操作。
测试用例是:
const { Sequelize } = require('sequelize');
function parseArgs(argv = process.argv) {
return argv.slice(argv.findIndex((x) => x === __filename) + 1);
}
const args = parseArgs();
const count = parseInt(args[0]) || 5;
const sequelize = new Sequelize('postgres://postgres@localhost:5432/postgres', {
seederStorage: 'sequelize',
dialect: 'postgres',
define: {
timestamps: false,
},
// logging: false,
pool: {
max: count, // keeping max pool size to equal to concurrent transactions
},
});
async function query() {
await sequelize.transaction(async (transaction) => {
// Just doing any query inside transaction. Using model yields same result.
// If no query is run inside transaction, sequelize progresses fine.
const one = await sequelize.query('SELECT 1');
console.log('Queried one', one);
});
}
async function concurrentQuery({ count = 1 }) {
const promises = [];
for (let i = 0; i < count; i++) {
promises.push(query());
}
console.log('Started promises', { count: promises.length });
await Promise.all(promises);
}
concurrentQuery({ count })
.then(() => {
console.log('result', 'pool config', sequelize.config.pool);
})
.catch((err) => {
console.error(err);
})
.finally(() => {
sequelize.close()
.then(() => {
console.log('Closed sequelize');
})
.catch((err) => {
console.error(err);
});
});
重现:
$ node sequelizeTransactionPoolSize.js
并查询postgres:
postgres@localhost:postgres> SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state FROM pg_stat_activity WHERE (now() - pg_stat_activity.query_start) >
interval '2 seconds';
+-------+----------------+--------------------+---------------------+
| pid | duration | query | state |
|-------+----------------+--------------------+---------------------|
| 1042 | 0:00:22.191851 | START TRANSACTION; | idle in transaction |
| 1043 | 0:00:22.187831 | START TRANSACTION; | idle in transaction |
| 1044 | 0:00:22.188163 | START TRANSACTION; | idle in transaction |
| 1045 | 0:00:22.188316 | START TRANSACTION; | idle in transaction |
| 1046 | 0:00:22.180694 | START TRANSACTION; | idle in transaction |
+-------+----------------+--------------------+---------------------+
SELECT 5
Time: 0.019s
使用 Sequelize v5(节点 12)和 v6(节点 14)测试。
这是一个错误吗?或者,我是否应该提供足够的 pool.max 来应对流量高峰?如果预期并发事务为 200 个峰值,请设置 pool.max = 201,可能会使 postgres 过载?
数据库事务和池中的可用连接数与您的问题所指出的方式无关。无论您使用事务进行查询,连接池都会工作 - 它会根据需要将它们移交。当您启动一个事务时,它确保您在事务中所做的更改是原子的,并且不会与事务外部的更新发生冲突。由于您没有将创建的事务传递到后续查询中,它将挂起等待您提交或回滚事务中的更改。如果您使用 async/await
.
这会更容易阅读
async function query() {
// get a transaction
const transaction = await sequelize.transaction();
// pass the transaction to the query
await one = await sequelize.query('SELECT 1', { transaction });
// await transaction.commit();
console.log('Queried one', one);
}
当我使用 sequelize.transaction()
创建足够多的并发事务时,sequelize 不会进行。
例如,如果我用 pool.max = 5 开始 5 个事务,sequelize 在 postgres 中执行 START TRANSACTION;
并且不执行任何进一步的操作。
测试用例是:
const { Sequelize } = require('sequelize');
function parseArgs(argv = process.argv) {
return argv.slice(argv.findIndex((x) => x === __filename) + 1);
}
const args = parseArgs();
const count = parseInt(args[0]) || 5;
const sequelize = new Sequelize('postgres://postgres@localhost:5432/postgres', {
seederStorage: 'sequelize',
dialect: 'postgres',
define: {
timestamps: false,
},
// logging: false,
pool: {
max: count, // keeping max pool size to equal to concurrent transactions
},
});
async function query() {
await sequelize.transaction(async (transaction) => {
// Just doing any query inside transaction. Using model yields same result.
// If no query is run inside transaction, sequelize progresses fine.
const one = await sequelize.query('SELECT 1');
console.log('Queried one', one);
});
}
async function concurrentQuery({ count = 1 }) {
const promises = [];
for (let i = 0; i < count; i++) {
promises.push(query());
}
console.log('Started promises', { count: promises.length });
await Promise.all(promises);
}
concurrentQuery({ count })
.then(() => {
console.log('result', 'pool config', sequelize.config.pool);
})
.catch((err) => {
console.error(err);
})
.finally(() => {
sequelize.close()
.then(() => {
console.log('Closed sequelize');
})
.catch((err) => {
console.error(err);
});
});
重现:
$ node sequelizeTransactionPoolSize.js
并查询postgres:
postgres@localhost:postgres> SELECT pid, now() - pg_stat_activity.query_start AS duration, query, state FROM pg_stat_activity WHERE (now() - pg_stat_activity.query_start) >
interval '2 seconds';
+-------+----------------+--------------------+---------------------+
| pid | duration | query | state |
|-------+----------------+--------------------+---------------------|
| 1042 | 0:00:22.191851 | START TRANSACTION; | idle in transaction |
| 1043 | 0:00:22.187831 | START TRANSACTION; | idle in transaction |
| 1044 | 0:00:22.188163 | START TRANSACTION; | idle in transaction |
| 1045 | 0:00:22.188316 | START TRANSACTION; | idle in transaction |
| 1046 | 0:00:22.180694 | START TRANSACTION; | idle in transaction |
+-------+----------------+--------------------+---------------------+
SELECT 5
Time: 0.019s
使用 Sequelize v5(节点 12)和 v6(节点 14)测试。
这是一个错误吗?或者,我是否应该提供足够的 pool.max 来应对流量高峰?如果预期并发事务为 200 个峰值,请设置 pool.max = 201,可能会使 postgres 过载?
数据库事务和池中的可用连接数与您的问题所指出的方式无关。无论您使用事务进行查询,连接池都会工作 - 它会根据需要将它们移交。当您启动一个事务时,它确保您在事务中所做的更改是原子的,并且不会与事务外部的更新发生冲突。由于您没有将创建的事务传递到后续查询中,它将挂起等待您提交或回滚事务中的更改。如果您使用 async/await
.
async function query() {
// get a transaction
const transaction = await sequelize.transaction();
// pass the transaction to the query
await one = await sequelize.query('SELECT 1', { transaction });
// await transaction.commit();
console.log('Queried one', one);
}