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);
}